示例#1
0
//! Because of metadata overrides we don't know the true hierarchy until all pieces have been loaded
void CAssParser::BuildPieceHierarchy(S3DModel* model)
{
	//! Loop through all pieces and create missing hierarchy info
	for (ModelPieceMap::const_iterator it = model->pieces.begin(); it != model->pieces.end(); ++it) {
		S3DModelPiece* piece = it->second;

		if (piece->name == "root") {
			piece->parent = NULL;
			assert(model->GetRootPiece() == NULL);
			model->SetRootPiece(piece); // FIXME what if called multiple times?
			continue;
		}

		if (piece->parentName != "") {
			if ((piece->parent = model->FindPiece(piece->parentName)) == NULL) {
				LOG_SL(LOG_SECTION_PIECE, L_ERROR,
						"Missing piece '%s' declared as parent of '%s'.",
						piece->parentName.c_str(), piece->name.c_str());
			} else {
				piece->parent->children.push_back(piece);
			}

			continue;
		}

		//! A piece with no parent that isn't the root (orphan)
		if ((piece->parent = model->FindPiece("root")) == NULL) {
			LOG_SL(LOG_SECTION_PIECE, L_ERROR, "Missing root piece");
		} else {
			piece->parent->children.push_back(piece);
		}
	}
}
示例#2
0
// Because of metadata overrides we don't know the true hierarchy until all pieces have been loaded
void CAssParser::BuildPieceHierarchy(S3DModel* model)
{
	// Loop through all pieces and create missing hierarchy info
	for (ModelPieceMap::const_iterator it = model->pieceMap.begin(); it != model->pieceMap.end(); ++it) {
		SAssPiece* piece = static_cast<SAssPiece*>(it->second);

		if (piece == model->GetRootPiece()) {
			assert(piece->parent == NULL);
			assert(model->GetRootPiece() == piece);
			continue;
		}

		if (!piece->parentName.empty()) {
			if ((piece->parent = model->FindPiece(piece->parentName)) == NULL) {
				LOG_SL(LOG_SECTION_PIECE, L_ERROR,
					"Missing piece '%s' declared as parent of '%s'.",
					piece->parentName.c_str(), piece->name.c_str());
			} else {
				piece->parent->children.push_back(piece);
			}

			continue;
		}

		// a piece with no named parent that isn't the root (orphan)
		// link these to the root piece if it exists (which it should)
		if ((piece->parent = model->GetRootPiece()) == NULL) {
			LOG_SL(LOG_SECTION_PIECE, L_ERROR, "Missing root piece");
		} else {
			piece->parent->children.push_back(piece);
		}
	}
}
示例#3
0
SAssPiece* CAssParser::LoadPiece(
	S3DModel* model,
	const aiNode* pieceNode,
	const aiScene* scene,
	const LuaTable& modelTable,
	ModelPieceMap& pieceMap,
	ParentNameMap& parentMap
) {
	++model->numPieces;

	SAssPiece* piece = new SAssPiece();

	if (pieceNode->mParent == nullptr) {
		// set the model's root piece ASAP, needed later
		assert(pieceNode == scene->mRootNode);
		assert(model->GetRootPiece() == nullptr);
		model->SetRootPiece(piece);
	}

	SetPieceName(piece, model, pieceNode, pieceMap);

	LOG_SL(LOG_SECTION_PIECE, L_INFO, "Converting node '%s' to piece '%s' (%d meshes).", pieceNode->mName.data, piece->name.c_str(), pieceNode->mNumMeshes);

	// Load additional piece properties from metadata
	const LuaTable& pieceTable = modelTable.SubTable("pieces").SubTable(piece->name);

	if (pieceTable.IsValid()) {
		LOG_SL(LOG_SECTION_PIECE, L_INFO, "Found metadata for piece '%s'", piece->name.c_str());
	}


	LoadPieceTransformations(piece, model, pieceNode, pieceTable);
	LoadPieceGeometry(piece, pieceNode, scene);
	SetPieceParentName(piece, model, pieceNode, pieceTable, parentMap);

	{
		// operator[] creates an empty string if piece is not in map
		const auto parentNameIt = parentMap.find(piece);
		const std::string& parentName = (parentNameIt != parentMap.end())? (parentNameIt->second).c_str(): "[null]";

		// Verbose logging of piece properties
		LOG_SL(LOG_SECTION_PIECE, L_INFO, "Loaded model piece: %s with %d meshes", piece->name.c_str(), pieceNode->mNumMeshes);
		LOG_SL(LOG_SECTION_PIECE, L_INFO, "piece->name: %s", piece->name.c_str());
		LOG_SL(LOG_SECTION_PIECE, L_INFO, "piece->parent: %s", parentName.c_str());
	}

	// Recursively process all child pieces
	for (unsigned int i = 0; i < pieceNode->mNumChildren; ++i) {
		LoadPiece(model, pieceNode->mChildren[i], scene, modelTable, pieceMap, parentMap);
	}

	pieceMap[piece->name] = piece;
	return piece;
}
示例#4
0
SAssPiece* CAssParser::LoadPiece(
	S3DModel* model,
	const aiNode* pieceNode,
	const aiScene* scene,
	const LuaTable& modelTable
) {
	++model->numPieces;

	SAssPiece* piece = new SAssPiece();

	if (pieceNode->mParent == NULL) {
		// set the model's root piece ASAP, needed later
		assert(pieceNode == scene->mRootNode);
		assert(model->GetRootPiece() == NULL);
		model->SetRootPiece(piece);
	}

	SetPieceName(piece, model, pieceNode);

	LOG_SL(LOG_SECTION_PIECE, L_INFO, "Converting node '%s' to piece '%s' (%d meshes).", pieceNode->mName.data, piece->name.c_str(), pieceNode->mNumMeshes);

	// Load additional piece properties from metadata
	const LuaTable& pieceTable = modelTable.SubTable("pieces").SubTable(piece->name);

	if (pieceTable.IsValid()) {
		LOG_SL(LOG_SECTION_PIECE, L_INFO, "Found metadata for piece '%s'", piece->name.c_str());
	}

	// Load transforms
	LoadPieceTransformations(piece, model, pieceNode, pieceTable);

	if (SetModelRadiusAndHeight(model, piece, pieceNode, pieceTable))
		return NULL;

	LoadPieceGeometry(piece, pieceNode, scene);
	SetPieceParentName(piece, model, pieceNode, pieceTable);

	// Verbose logging of piece properties
	LOG_SL(LOG_SECTION_PIECE, L_INFO, "Loaded model piece: %s with %d meshes", piece->name.c_str(), pieceNode->mNumMeshes);
	LOG_SL(LOG_SECTION_PIECE, L_INFO, "piece->name: %s", piece->name.c_str());
	LOG_SL(LOG_SECTION_PIECE, L_INFO, "piece->parent: %s", piece->parentName.c_str());

	// Recursively process all child pieces
	for (unsigned int i = 0; i < pieceNode->mNumChildren; ++i) {
		LoadPiece(model, pieceNode->mChildren[i], scene, modelTable);
	}

	model->pieceMap[piece->name] = piece;
	return piece;
}
bool CArchiveScanner::CheckCompression(const IArchive* ar,const std::string& fullName, std::string& error)
{
	if (!ar->CheckForSolid())
		return true;
	for (unsigned fid = 0; fid != ar->NumFiles(); ++fid) {
		std::string name;
		int size;
		ar->FileInfo(fid, name, size);
		const std::string lowerName = StringToLower(name);
		const auto metaFileClass = CArchiveScanner::GetMetaFileClass(lowerName);
		if ((metaFileClass == 0) || ar->HasLowReadingCost(fid))
			continue;

		// is a meta-file and not cheap to read
		if (metaFileClass == 1) {
			// 1st class
			error = "Unpacking/reading cost for meta file " + name
					+ " is too high, please repack the archive (make sure to use a non-solid algorithm, if applicable)";
			return false;
		} else if (metaFileClass == 2) {
			// 2nd class
			LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING,
					"Archive %s: The cost for reading a 2nd-class meta-file is too high: %s",
					fullName.c_str(), name.c_str());
		}

	}
	return true;
}
示例#6
0
bool CArchiveScanner::CheckCompression(const IArchive* ar, const std::string& fullName, std::string& error)
{
	if (!ar->CheckForSolid())
		return true;

	for (unsigned fid = 0; fid != ar->NumFiles(); ++fid) {
		if (ar->HasLowReadingCost(fid))
			continue;

		const std::pair<std::string, int>& info = ar->FileInfo(fid);

		switch (GetMetaFileClass(StringToLower(info.first))) {
			case 1: {
				error += "reading primary meta-file " + info.first + " too expensive; ";
				error += "please repack this archive with non-solid compression";
				return false;
			} break;
			case 2: {
				LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING, "Archive %s: reading secondary meta-file %s too expensive", fullName.c_str(), info.first.c_str());
			} break;
			case 0:
			default: {
				continue;
			} break;
		}
	}

	return true;
}
std::string CArchiveScanner::MapNameToMapFile(const std::string& s) const
{
	// Convert map name to map archive
	for (std::map<std::string, ArchiveInfo>::const_iterator aii = archiveInfos.begin(); aii != archiveInfos.end(); ++aii) {
		if (s == aii->second.archiveData.GetNameVersioned()) {
			return aii->second.archiveData.GetMapFile();
		}
	}
	LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING, "map file of %s not found", s.c_str());
	return s;
}
示例#8
0
void CWordCompletion::AddWord(const std::string& word, bool startOfLine,
		bool unitName, bool miniMap)
{
	if (!word.empty()) {
		if (words.find(word) != words.end()) {
			LOG_SL("WordCompletion", L_DEBUG,
					"Tried to add already present word: %s", word.c_str());
			return;
		}
		words[word] = WordProperties(startOfLine, unitName, miniMap);
	}
}
示例#9
0
std::string CArchiveScanner::MapNameToMapFile(const std::string& s) const
{
	// Convert map name to map archive
	const auto pred = [&s](const decltype(archiveInfos)::value_type& p) { return ((p.second).archiveData.GetNameVersioned() == s); };
	const auto iter = std::find_if(archiveInfos.cbegin(), archiveInfos.cend(), pred);

	if (iter != archiveInfos.cend())
		return ((iter->second).archiveData.GetMapFile());

	LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING, "map file of %s not found", s.c_str());
	return s;
}
示例#10
0
// Because of metadata overrides we don't know the true hierarchy until all pieces have been loaded
void CAssParser::BuildPieceHierarchy(S3DModel* model, ModelPieceMap& pieceMap, const ParentNameMap& parentMap)
{
	const char* fmt1 = "Missing piece '%s' declared as parent of '%s'.";
	const char* fmt2 = "Missing root piece (parent of orphan '%s')";

	// Loop through all pieces and create missing hierarchy info
	for (auto it = pieceMap.cbegin(); it != pieceMap.cend(); ++it) {
		SAssPiece* piece = static_cast<SAssPiece*>(it->second);

		if (piece == model->GetRootPiece()) {
			assert(piece->parent == nullptr);
			assert(model->GetRootPiece() == piece);
			continue;
		}

		const auto parentNameIt = parentMap.find(piece);

		if (parentNameIt != parentMap.end()) {
			const std::string& parentName = parentNameIt->second;
			const auto pieceIt = pieceMap.find(parentName);

			if (pieceIt != pieceMap.end()) {
				piece->parent = pieceIt->second;
				piece->parent->children.push_back(piece);
			} else {
				LOG_SL(LOG_SECTION_PIECE, L_ERROR, fmt1, parentName.c_str(), piece->name.c_str());
			}

			continue;
		}

		// a piece with no named parent that isn't the root (orphan)
		// link these to the root piece if it exists (which it should)
		if ((piece->parent = model->GetRootPiece()) == nullptr) {
			LOG_SL(LOG_SECTION_PIECE, L_ERROR, fmt2, piece->name.c_str());
		} else {
			piece->parent->children.push_back(piece);
		}
	}
}
示例#11
0
unsigned int CArchiveScanner::GetSingleArchiveChecksum(const std::string& name) const
{
	std::string lcname = FileSystem::GetFilename(name);
	StringToLowerInPlace(lcname);

	std::map<std::string, ArchiveInfo>::const_iterator aii = archiveInfos.find(lcname);
	if (aii == archiveInfos.end()) {
		LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING, "%s checksum: not found (0)", name.c_str());
		return 0;
	}

	LOG_S(LOG_SECTION_ARCHIVESCANNER,"%s checksum: %d/%u", name.c_str(), aii->second.checksum, aii->second.checksum);
	return aii->second.checksum;
}
示例#12
0
unsigned int CArchiveScanner::GetSingleArchiveChecksum(const std::string& filePath)
{
	ComputeChecksumForArchive(filePath);
	const std::string lcname = std::move(StringToLower(FileSystem::GetFilename(filePath)));

	const auto aii = archiveInfos.find(lcname);
	if (aii == archiveInfos.end()) {
		LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING, "%s checksum: not found (0)", filePath.c_str());
		return 0;
	}

	LOG_S(LOG_SECTION_ARCHIVESCANNER,"%s checksum: %d/%u", filePath.c_str(), aii->second.checksum, aii->second.checksum);
	return aii->second.checksum;
}
示例#13
0
	void write(const char* message) {
		LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "Assimp: %s", message);
	}
示例#14
0
文件: gml.cpp 项目: AMDmi3/spring
void gmlPrintCallChainWarning(const char *func) {
	LOG_SL("Threading", L_ERROR, "Invalid attempt (%d/%d) to invoke LuaUI (%s) from another Lua environment, "
			"certain widgets require LuaThreadingModel > %d to work properly with multithreading", gmlCallChainWarning, GML_MAX_CALL_CHAIN_WARNINGS, func, (int)MT_LUA_SINGLE_BATCH);
}
示例#15
0
void SAssPiece::DrawForList() const
{
	if (isEmpty)
		return;

	LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Compiling piece %s", name.c_str());

	vboAttributes.Bind(GL_ARRAY_BUFFER);
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(3, GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, pos)));

		glEnableClientState(GL_NORMAL_ARRAY);
		glNormalPointer(GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, normal)));

		glClientActiveTexture(GL_TEXTURE0);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, texCoord)));

		glClientActiveTexture(GL_TEXTURE1);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, texCoord)));

		if (hasTexCoord2) {
			glClientActiveTexture(GL_TEXTURE2);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glTexCoordPointer(2, GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, texCoord2)));
		}

		glClientActiveTexture(GL_TEXTURE5);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(3, GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, sTangent)));

		glClientActiveTexture(GL_TEXTURE6);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(3, GL_FLOAT, sizeof(SAssVertex), vboAttributes.GetPtr(offsetof(SAssVertex, tTangent)));
	vboAttributes.Unbind();

	vboIndices.Bind(GL_ELEMENT_ARRAY_BUFFER);
		/*
		* since aiProcess_SortByPType is being used,
		* we're sure we'll get only 1 type here,
		* so combination check isn't needed, also
		* anything more complex than triangles is
		* being split thanks to aiProcess_Triangulate
		*/
		glDrawRangeElements(GL_TRIANGLES, 0, vertices.size() - 1, vertexDrawIndices.size(), GL_UNSIGNED_INT, vboIndices.GetPtr());
	vboIndices.Unbind();

	glClientActiveTexture(GL_TEXTURE6);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glClientActiveTexture(GL_TEXTURE5);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glClientActiveTexture(GL_TEXTURE2);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glClientActiveTexture(GL_TEXTURE1);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glClientActiveTexture(GL_TEXTURE0);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);

	LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Completed compiling piece %s",
			name.c_str());
}
示例#16
0
SAssPiece* CAssParser::LoadPiece(SAssModel* model, aiNode* node, const LuaTable& metaTable)
{
	// Create new piece
	++model->numPieces;
	SAssPiece* piece = new SAssPiece;
	piece->type = MODELTYPE_OTHER;
	piece->node = node;
	piece->isEmpty = (node->mNumMeshes == 0);

	if (node->mParent) {
		piece->name = std::string(node->mName.data);
	} else {
		//FIXME is this really smart?
		piece->name = "root"; //! The real model root
	}

	// find a new name if none given or if a piece with the same name already exists
	if (piece->name.empty()) {
		piece->name = "piece";
	}

	ModelPieceMap::const_iterator it = model->pieces.find(piece->name);
	if (it != model->pieces.end()) {
		char buf[64];
		int i = 0;
		while (it != model->pieces.end()) {
			SNPRINTF(buf, 64, "%s%02i", piece->name.c_str(), i++);
			it = model->pieces.find(buf);
		}
		piece->name = buf;
	}
	LOG_S(LOG_SECTION_PIECE, "Converting node '%s' to piece '%s' (%d meshes).",
			node->mName.data, piece->name.c_str(), node->mNumMeshes);

	// Load additional piece properties from metadata
	const LuaTable& pieceTable = metaTable.SubTable("pieces").SubTable(piece->name);
	if (pieceTable.IsValid()) {
		LOG_S(LOG_SECTION_PIECE, "Found metadata for piece '%s'",
				piece->name.c_str());
	}

	// Load transforms
	LoadPieceTransformations(model, piece, pieceTable);

	// Update piece min/max extents
	for (unsigned meshListIndex = 0; meshListIndex < node->mNumMeshes; meshListIndex++) {
		const unsigned int meshIndex = node->mMeshes[meshListIndex];
		const SAssModel::MinMax& minmax = model->mesh_minmax[meshIndex];

		piece->mins = std::min(piece->mins, minmax.mins);
		piece->maxs = std::max(piece->maxs, minmax.maxs);
	}


	// Check if piece is special (ie, used to set Spring model properties)
	if (strcmp(node->mName.data, "SpringHeight") == 0) {
		// Set the model height to this nodes Z value
		if (!metaTable.KeyExists("height")) {
			model->height = piece->offset.z;
			LOG_S(LOG_SECTION_MODEL,
					"Model height of %f set by special node 'SpringHeight'",
					model->height);
		}
		--model->numPieces;
		delete piece;
		return NULL;
	}
	if (strcmp(node->mName.data, "SpringRadius") == 0) {
		if (!metaTable.KeyExists("midpos")) {
			model->relMidPos = piece->scaleRotMatrix.Mul(piece->offset);
			LOG_S(LOG_SECTION_MODEL,
					"Model midpos of (%f,%f,%f) set by special node 'SpringRadius'",
					model->relMidPos.x, model->relMidPos.y, model->relMidPos.z);
		}
		if (!metaTable.KeyExists("radius")) {
			if (piece->maxs.x <= 0.00001f) {
				aiVector3D _scale, _offset;
				aiQuaternion _rotate;
				piece->node->mTransformation.Decompose(_scale,_rotate,_offset);
				model->radius = aiVectorToFloat3(_scale).x; // the blender import script only sets the scale property
			} else {
				model->radius = piece->maxs.x; // use the transformed mesh extents
			}
			LOG_S(LOG_SECTION_MODEL,
					"Model radius of %f set by special node 'SpringRadius'",
					model->radius);
		}
		--model->numPieces;
		delete piece;
		return NULL;
	}



	//! Get vertex data from node meshes
	for (unsigned meshListIndex = 0; meshListIndex < node->mNumMeshes; ++meshListIndex) {
		unsigned int meshIndex = node->mMeshes[meshListIndex];
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching mesh %d from scene",
				meshIndex);
		const aiMesh* mesh = model->scene->mMeshes[meshIndex];
		std::vector<unsigned> mesh_vertex_mapping;
		// extract vertex data
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
				"Processing vertices for mesh %d (%d vertices)",
				meshIndex, mesh->mNumVertices);
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
				"Normals: %s Tangents/Bitangents: %s TexCoords: %s",
				(mesh->HasNormals() ? "Y" : "N"),
				(mesh->HasTangentsAndBitangents() ? "Y" : "N"),
				(mesh->HasTextureCoords(0) ? "Y" : "N"));

		piece->vertices.reserve(piece->vertices.size() + mesh->mNumVertices);
		for (unsigned vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) {
			SAssVertex vertex;

			// vertex coordinates
			const aiVector3D& aiVertex = mesh->mVertices[vertexIndex];
			vertex.pos = aiVectorToFloat3(aiVertex);

			// vertex normal
			LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching normal for vertex %d",
					vertexIndex);
			const aiVector3D& aiNormal = mesh->mNormals[vertexIndex];
			if (!IS_QNAN(aiNormal)) {
				vertex.normal = aiVectorToFloat3(aiNormal);
			}

			// vertex tangent, x is positive in texture axis
			if (mesh->HasTangentsAndBitangents()) {
				LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
						"Fetching tangent for vertex %d", vertexIndex );
				const aiVector3D& aiTangent = mesh->mTangents[vertexIndex];
				const aiVector3D& aiBitangent = mesh->mBitangents[vertexIndex];

				vertex.sTangent = aiVectorToFloat3(aiTangent);
				vertex.tTangent = aiVectorToFloat3(aiBitangent);
			}

			// vertex texcoords
			if (mesh->HasTextureCoords(0)) {
				vertex.texCoord.x = mesh->mTextureCoords[0][vertexIndex].x;
				vertex.texCoord.y = mesh->mTextureCoords[0][vertexIndex].y;
			}

			if (mesh->HasTextureCoords(1)) {
				piece->hasTexCoord2 = true,
				vertex.texCoord2.x = mesh->mTextureCoords[1][vertexIndex].x;
				vertex.texCoord2.y = mesh->mTextureCoords[1][vertexIndex].y;
			}

			mesh_vertex_mapping.push_back(piece->vertices.size());
			piece->vertices.push_back(vertex);
		}

		// extract face data
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
				"Processing faces for mesh %d (%d faces)",
				meshIndex, mesh->mNumFaces);
		/*
		 * since aiProcess_SortByPType is being used,
		 * we're sure we'll get only 1 type here,
		 * so combination check isn't needed, also
		 * anything more complex than triangles is
		 * being split thanks to aiProcess_Triangulate
		 */
		piece->vertexDrawIndices.reserve(piece->vertexDrawIndices.size() + mesh->mNumFaces * 3);
		for (unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
			const aiFace& face = mesh->mFaces[faceIndex];

			// some models contain lines (mNumIndices == 2)
			// we cannot render those (esp. they would need to be called in a 2nd drawcall)
			if (face.mNumIndices != 3)
				continue;

			for (unsigned vertexListID = 0; vertexListID < face.mNumIndices; ++vertexListID) {
				const unsigned int vertexFaceIdx = face.mIndices[vertexListID];
				const unsigned int vertexDrawIdx = mesh_vertex_mapping[vertexFaceIdx];
				piece->vertexDrawIndices.push_back(vertexDrawIdx);
			}
		}
	}

	piece->isEmpty = piece->vertices.empty();

	//! Get parent name from metadata or model
	if (pieceTable.KeyExists("parent")) {
		piece->parentName = pieceTable.GetString("parent", "");
	} else if (node->mParent) {
		if (node->mParent->mParent) {
			piece->parentName = std::string(node->mParent->mName.data);
		} else { // my parent is the root, which gets renamed
			piece->parentName = "root";
		}
	} else {
		piece->parentName = "";
	}

	LOG_S(LOG_SECTION_PIECE, "Loaded model piece: %s with %d meshes",
			piece->name.c_str(), node->mNumMeshes);

	// Verbose logging of piece properties
	LOG_S(LOG_SECTION_PIECE, "piece->name: %s", piece->name.c_str());
	LOG_S(LOG_SECTION_PIECE, "piece->parent: %s", piece->parentName.c_str());

	// Recursively process all child pieces
	for (unsigned int i = 0; i < node->mNumChildren; ++i) {
		LoadPiece(model, node->mChildren[i], metaTable);
	}

	model->pieces[piece->name] = piece;
	return piece;
}
示例#17
0
S3DModel* CAssParser::Load(const std::string& modelFilePath)
{
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading model: %s", modelFilePath.c_str());

	const std::string& modelPath = FileSystem::GetDirectory(modelFilePath);
	const std::string& modelName = FileSystem::GetBasename(modelFilePath);

	// Load the lua metafile. This contains properties unique to Spring models and must return a table
	std::string metaFileName = modelFilePath + ".lua";

	if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) {
		// Try again without the model file extension
		metaFileName = modelPath + '/' + modelName + ".lua";
	}
	if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO, "No meta-file '%s'. Using defaults.", metaFileName.c_str());
	}

	LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);

	if (!metaFileParser.Execute()) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str());
	}

	// Get the (root-level) model table
	const LuaTable& modelTable = metaFileParser.GetRoot();

	if (!modelTable.IsValid()) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO, "No valid model metadata in '%s' or no meta-file", metaFileName.c_str());
	}


	// Create a model importer instance
	Assimp::Importer importer;


	// Give the importer an IO class that handles Spring's VFS
	importer.SetIOHandler(new AssVFSSystem());
	// Speed-up processing by skipping things we don't need
	importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, ASS_IMPORTER_OPTIONS);

#ifndef BITMAP_NO_OPENGL
	{
		importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,   maxVertices);
		importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices / 3);
	}
#endif

	// Read the model file to build a scene object
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Importing model file: %s", modelFilePath.c_str());

	const aiScene* scene = nullptr;

	{
		// ASSIMP spams many SIGFPEs atm in normal & tangent generation
		ScopedDisableFpuExceptions fe;
		scene = importer.ReadFile(modelFilePath, ASS_POSTPROCESS_OPTIONS);
	}

	if (scene != nullptr) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO,
			"Processing scene for model: %s (%d meshes / %d materials / %d textures)",
			modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials,
			scene->mNumTextures);
	} else {
		throw content_error("[AssimpParser] Model Import: " + std::string(importer.GetErrorString()));
	}

	ModelPieceMap pieceMap;
	ParentNameMap parentMap;

	S3DModel* model = new S3DModel();
	model->name = modelFilePath;
	model->type = MODELTYPE_ASS;

	// Load textures
	FindTextures(model, scene, modelTable, modelPath, modelName);
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading textures. Tex1: '%s' Tex2: '%s'", model->texs[0].c_str(), model->texs[1].c_str());

	texturehandlerS3O->PreloadTexture(model, modelTable.GetBool("fliptextures", true), modelTable.GetBool("invertteamcolor", true));

	// Load all pieces in the model
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading pieces from root node '%s'", scene->mRootNode->mName.data);
	LoadPiece(model, scene->mRootNode, scene, modelTable, pieceMap, parentMap);

	// Update piece hierarchy based on metadata
	BuildPieceHierarchy(model, pieceMap, parentMap);
	CalculateModelProperties(model, modelTable);

	// Verbose logging of model properties
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str());
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]);
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model %s Imported.", model->name.c_str());
	return model;
}
示例#18
0
bool CAssParser::SetModelRadiusAndHeight(
	S3DModel* model,
	const SAssPiece* piece,
	const aiNode* pieceNode,
	const LuaTable& pieceTable
) {
	// check if this piece is "special" (ie, used to set Spring model properties)
	// if so, extract them and then remove the piece from the hierarchy entirely
	//
	if (piece->name == "SpringHeight") {
		// set the model height to this node's Y-value (FIXME: 'y' is Assimp/Blender-specific)
		if (!pieceTable.KeyExists("height")) {
			model->height = piece->offset.y;

			LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model height of %f set by special node 'SpringHeight'", model->height);
		}

		--model->numPieces;
		delete piece;
		return true;
	}

	if (piece->name == "SpringRadius") {
		if (!pieceTable.KeyExists("midpos")) {
			CMatrix44f scaleRotMat;
			piece->ComposeTransform(scaleRotMat, ZeroVector, ZeroVector, piece->scales);

			// NOTE:
			//   this makes little sense because the "SpringRadius"
			//   piece can be placed anywhere within the hierarchy
			model->relMidPos = scaleRotMat.Mul(piece->offset);

			LOG_SL(LOG_SECTION_MODEL, L_INFO,
				"Model midpos of (%f,%f,%f) set by special node 'SpringRadius'",
				model->relMidPos.x, model->relMidPos.y, model->relMidPos.z);
		}
		if (!pieceTable.KeyExists("radius")) {
			if (true || piece->maxs.x <= 0.00001f) {
				// scales have been set at this point
				// the Blender import script only sets the scale property [?]
				//
				// model->radius = piece->scales.Length();
				model->radius = piece->scales.x;
			} else {
				// FIXME:
				//   geometry bounds are calculated by LoadPieceGeometry
				//   which is called after SetModelRadiusAndHeight -> can
				//   not take this branch yet
				// use the transformed mesh extents (FIXME: the bounds are NOT
				// actually transformed but derived from raw vertex positions!)
				//
				// model->radius = ((piece->maxs - piece->mins) * 0.5f).Length();
				model->radius = piece->maxs.x;
			}

			LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model radius of %f set by special node 'SpringRadius'", model->radius);
		}

		--model->numPieces;
		delete piece;
		return true;
	}

	return false;
}
示例#19
0
S3DModel* CAssParser::Load(const std::string& modelFilePath)
{
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading model: %s", modelFilePath.c_str());

	const std::string& modelPath = FileSystem::GetDirectory(modelFilePath);
	const std::string& modelName = FileSystem::GetBasename(modelFilePath);

	// Load the lua metafile. This contains properties unique to Spring models and must return a table
	std::string metaFileName = modelFilePath + ".lua";

	if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) {
		// Try again without the model file extension
		metaFileName = modelPath + '/' + modelName + ".lua";
	}
	if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO, "No meta-file '%s'. Using defaults.", metaFileName.c_str());
	}

	LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);

	if (!metaFileParser.Execute()) {
		LOG_SL(LOG_SECTION_MODEL, L_ERROR, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str());
	}

	// Get the (root-level) model table
	const LuaTable& modelTable = metaFileParser.GetRoot();

	if (!modelTable.IsValid()) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO, "No valid model metadata in '%s' or no meta-file", metaFileName.c_str());
	}


	// Create a model importer instance
	Assimp::Importer importer;

	// Create a logger for debugging model loading issues
	Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE);
	Assimp::DefaultLogger::get()->attachStream(new AssLogStream(), ASS_LOGGING_OPTIONS);

	// Give the importer an IO class that handles Spring's VFS
	importer.SetIOHandler(new AssVFSSystem());
	// Speed-up processing by skipping things we don't need
	importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, ASS_IMPORTER_OPTIONS);

#ifndef BITMAP_NO_OPENGL
	{
		// Optimize VBO-Mesh sizes/ranges
		GLint maxIndices  = 1024;
		GLint maxVertices = 1024;
		// FIXME returns non-optimal data, at best compute it ourselves (pre-TL cache size!)
		glGetIntegerv(GL_MAX_ELEMENTS_INDICES,  &maxIndices);
		glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices);
		importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,   maxVertices);
		importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices / 3);
	}
#endif

	// Read the model file to build a scene object
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Importing model file: %s", modelFilePath.c_str());

	const aiScene* scene;
	{
		// ASSIMP spams many SIGFPEs atm in normal & tangent generation
		ScopedDisableFpuExceptions fe;
		scene = importer.ReadFile(modelFilePath, ASS_POSTPROCESS_OPTIONS);
	}

	if (scene != NULL) {
		LOG_SL(LOG_SECTION_MODEL, L_INFO,
			"Processing scene for model: %s (%d meshes / %d materials / %d textures)",
			modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials,
			scene->mNumTextures);
	} else {
		throw content_error("[AssimpParser] Model Import: " + std::string(importer.GetErrorString()));
	}

	S3DModel* model = new S3DModel();
	model->name = modelFilePath;
	model->type = MODELTYPE_ASS;

	// Load textures
	FindTextures(model, scene, modelTable, modelPath, modelName);
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading textures. Tex1: '%s' Tex2: '%s'", model->tex1.c_str(), model->tex2.c_str());
	texturehandlerS3O->LoadS3OTexture(model);

	// Load all pieces in the model
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading pieces from root node '%s'", scene->mRootNode->mName.data);
	LoadPiece(model, scene->mRootNode, scene, modelTable);

	// Update piece hierarchy based on metadata
	BuildPieceHierarchy(model);
	CalculateModelProperties(model, modelTable);

	// Verbose logging of model properties
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str());
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->drawRadius: %f", model->drawRadius);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]);
	LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model %s Imported.", model->name.c_str());
	return model;
}
示例#20
0
void CAssParser::LoadPieceTransformations(
	SAssPiece* piece,
	const S3DModel* model,
	const aiNode* pieceNode,
	const LuaTable& pieceTable
) {
	aiVector3D aiScaleVec, aiTransVec;
	aiQuaternion aiRotateQuat;

	// process transforms
	pieceNode->mTransformation.Decompose(aiScaleVec, aiRotateQuat, aiTransVec);

	// metadata-scaling
	piece->scales   = pieceTable.GetFloat3("scale", aiVectorToFloat3(aiScaleVec));
	piece->scales.x = pieceTable.GetFloat("scalex", piece->scales.x);
	piece->scales.y = pieceTable.GetFloat("scaley", piece->scales.y);
	piece->scales.z = pieceTable.GetFloat("scalez", piece->scales.z);

	if (piece->scales.x != piece->scales.y || piece->scales.y != piece->scales.z) {
		// LOG_SL(LOG_SECTION_MODEL, L_WARNING, "Spring doesn't support non-uniform scaling");
		piece->scales.y = piece->scales.x;
		piece->scales.z = piece->scales.x;
	}

	// metadata-translation
	piece->offset   = pieceTable.GetFloat3("offset", aiVectorToFloat3(aiTransVec));
	piece->offset.x = pieceTable.GetFloat("offsetx", piece->offset.x);
	piece->offset.y = pieceTable.GetFloat("offsety", piece->offset.y);
	piece->offset.z = pieceTable.GetFloat("offsetz", piece->offset.z);

	// metadata-rotation
	// NOTE:
	//   these rotations are "pre-scripting" but "post-modelling"
	//   together with the (baked) aiRotateQuad they determine the
	//   model's pose *before* any animations execute
	//
	// float3 rotAngles = pieceTable.GetFloat3("rotate", aiQuaternionToRadianAngles(aiRotateQuat) * RADTODEG);
	float3 pieceRotAngles = pieceTable.GetFloat3("rotate", ZeroVector);

	pieceRotAngles.x = pieceTable.GetFloat("rotatex", pieceRotAngles.x);
	pieceRotAngles.y = pieceTable.GetFloat("rotatey", pieceRotAngles.y);
	pieceRotAngles.z = pieceTable.GetFloat("rotatez", pieceRotAngles.z);
	pieceRotAngles  *= DEGTORAD;

	LOG_SL(LOG_SECTION_PIECE, L_INFO,
		"(%d:%s) Assimp offset (%f,%f,%f), rotate (%f,%f,%f,%f), scale (%f,%f,%f)",
		model->numPieces, piece->name.c_str(),
		aiTransVec.x, aiTransVec.y, aiTransVec.z,
		aiRotateQuat.w, aiRotateQuat.x, aiRotateQuat.y, aiRotateQuat.z,
		aiScaleVec.x, aiScaleVec.y, aiScaleVec.z
	);
	LOG_SL(LOG_SECTION_PIECE, L_INFO,
		"(%d:%s) Relative offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)",
		model->numPieces, piece->name.c_str(),
		piece->offset.x, piece->offset.y, piece->offset.z,
		pieceRotAngles.x, pieceRotAngles.y, pieceRotAngles.z,
		piece->scales.x, piece->scales.y, piece->scales.z
	);

	// NOTE:
	//   at least collada (.dae) files generated by Blender represent
	//   a coordinate-system that differs from the "standard" formats
	//   (3DO, S3O, ...) for which existing tools at least have prior
	//   knowledge of Spring's expectations --> let the user override
	//   the ROOT rotational transform and the rotation-axis mapping
	//   used by animation scripts (but re-modelling/re-exporting is
	//   always preferred!) even though AssImp should convert models
	//   to its own system which matches that of Spring
	//
	//   .dae  : x=Rgt, y=-Fwd, z= Up, as=(-1, -1, 1), am=AXIS_XZY (if Z_UP)
	//   .dae  : x=Rgt, y=-Fwd, z= Up, as=(-1, -1, 1), am=AXIS_XZY (if Y_UP) [!?]
	//   .blend: ????
	piece->bakedRotMatrix = aiMatrixToMatrix(aiMatrix4x4t<float>(aiRotateQuat.GetMatrix()));

	if (piece == model->GetRootPiece()) {
		const float3 xaxis = pieceTable.GetFloat3("xaxis", piece->bakedRotMatrix.GetX());
		const float3 yaxis = pieceTable.GetFloat3("yaxis", piece->bakedRotMatrix.GetY());
		const float3 zaxis = pieceTable.GetFloat3("zaxis", piece->bakedRotMatrix.GetZ());

		if (math::fabs(xaxis.SqLength() - yaxis.SqLength()) < 0.01f && math::fabs(yaxis.SqLength() - zaxis.SqLength()) < 0.01f) {
			piece->bakedRotMatrix = CMatrix44f(ZeroVector, xaxis, yaxis, zaxis);
		}
	}

	piece->rotAxisSigns = pieceTable.GetFloat3("rotAxisSigns", float3(-OnesVector));
	piece->axisMapType = AxisMappingType(pieceTable.GetInt("rotAxisMap", AXIS_MAPPING_XYZ));

	// construct 'baked' part of the piece-space matrix
	// AssImp order is translate * rotate * scale * v;
	// we leave the translation and scale parts out and
	// put those in <offset> and <scales> --> transform
	// is just R instead of T * R * S
	//
	// note: for all non-AssImp models this is identity!
	//
	piece->ComposeRotation(piece->bakedRotMatrix, pieceRotAngles);
	piece->SetHasIdentityRotation(piece->bakedRotMatrix.IsIdentity() == 0);

	assert(piece->bakedRotMatrix.IsOrthoNormal() == 0);
}
示例#21
0
SAssPiece* CAssParser::LoadPiece(SAssModel* model, aiNode* node, const LuaTable& metaTable)
{
	//! Create new piece
	++model->numPieces;
	SAssPiece* piece = new SAssPiece;
	piece->type = MODELTYPE_OTHER;
	piece->node = node;
	piece->model = model;
	piece->isEmpty = (node->mNumMeshes == 0);

	if (node->mParent) {
		piece->name = std::string(node->mName.data);
	} else {
		//FIXME is this really smart?
		piece->name = "root"; //! The real model root
	}

	//! find a new name if none given or if a piece with the same name already exists
	if (piece->name.empty()) {
		piece->name = "piece";
	}
	ModelPieceMap::const_iterator it = model->pieces.find(piece->name);
	if (it != model->pieces.end()) {
		char buf[64];
		int i = 0;
		while (it != model->pieces.end()) {
			SNPRINTF(buf, 64, "%s%02i", piece->name.c_str(), i++);
			it = model->pieces.find(buf);
		}
		piece->name = buf;
	}
	LOG_S(LOG_SECTION_PIECE, "Converting node '%s' to piece '%s' (%d meshes).",
			node->mName.data, piece->name.c_str(), node->mNumMeshes);

	//! Load additional piece properties from metadata
	const LuaTable& pieceTable = metaTable.SubTable("pieces").SubTable(piece->name);
	if (pieceTable.IsValid()) {
		LOG_S(LOG_SECTION_PIECE, "Found metadata for piece '%s'",
				piece->name.c_str());
	}

	//! Load transforms
	LoadPieceTransformations(piece, pieceTable);

	//! Update piece min/max extents
	for (unsigned meshListIndex = 0; meshListIndex < node->mNumMeshes; meshListIndex++) {
		unsigned int meshIndex = node->mMeshes[meshListIndex];
		SAssModel::MinMax& minmax = model->mesh_minmax[meshIndex];
		piece->mins.x = std::min(piece->mins.x, minmax.mins.x);
		piece->mins.y = std::min(piece->mins.y, minmax.mins.y);
		piece->mins.z = std::min(piece->mins.z, minmax.mins.z);
		piece->maxs.x = std::max(piece->maxs.x, minmax.maxs.x);
		piece->maxs.y = std::max(piece->maxs.y, minmax.maxs.y);
		piece->maxs.z = std::max(piece->maxs.z, minmax.maxs.z);
	}


	//! Check if piece is special (ie, used to set Spring model properties)
	if (strcmp(node->mName.data, "SpringHeight") == 0) {
		//! Set the model height to this nodes Z value
		if (!metaTable.KeyExists("height")) {
			model->height = piece->offset.z;
			LOG_S(LOG_SECTION_MODEL,
					"Model height of %f set by special node 'SpringHeight'",
					model->height);
		}
		--model->numPieces;
		delete piece;
		return NULL;
	}
	if (strcmp(node->mName.data, "SpringRadius") == 0) {
		if (!metaTable.KeyExists("midpos")) {
			model->relMidPos = float3(piece->offset.x, piece->offset.z, piece->offset.y); //! Y and Z are swapped because this piece isn't rotated
			LOG_S(LOG_SECTION_MODEL,
					"Model midpos of (%f,%f,%f) set by special node 'SpringRadius'",
					model->relMidPos.x, model->relMidPos.y, model->relMidPos.z);
		}
		if (!metaTable.KeyExists("radius")) {
			if (piece->maxs.x <= 0.00001f) {
				model->radius = piece->scale.x; //! the blender import script only sets the scale property
			} else {
				model->radius = piece->maxs.x; //! use the transformed mesh extents
			}
			LOG_S(LOG_SECTION_MODEL,
					"Model radius of %f set by special node 'SpringRadius'",
					model->radius);
		}
		--model->numPieces;
		delete piece;
		return NULL;
	}


	//! Get vertex data from node meshes
	for (unsigned meshListIndex = 0; meshListIndex < node->mNumMeshes; ++meshListIndex) {
		unsigned int meshIndex = node->mMeshes[meshListIndex];
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching mesh %d from scene",
				meshIndex);
		aiMesh* mesh = model->scene->mMeshes[meshIndex];
		std::vector<unsigned> mesh_vertex_mapping;
		//! extract vertex data
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
				"Processing vertices for mesh %d (%d vertices)",
				meshIndex, mesh->mNumVertices);
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
				"Normals: %s Tangents/Bitangents: %s TexCoords: %s",
				(mesh->HasNormals() ? "Y" : "N"),
				(mesh->HasTangentsAndBitangents() ? "Y" : "N"),
				(mesh->HasTextureCoords(0) ? "Y" : "N"));

		// FIXME add piece->vertices.reserve()
		for (unsigned vertexIndex= 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) {
			SAssVertex vertex;

			//! vertex coordinates
			//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching vertex %d from mesh", vertexIndex);
			const aiVector3D& aiVertex = mesh->mVertices[vertexIndex];
			vertex.pos.x = aiVertex.x;
			vertex.pos.y = aiVertex.y;
			vertex.pos.z = aiVertex.z;

			//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "vertex position %d: %f %f %f", vertexIndex, vertex.pos.x, vertex.pos.y, vertex.pos.z);

			//! vertex normal
			LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching normal for vertex %d",
					vertexIndex);
			const aiVector3D& aiNormal = mesh->mNormals[vertexIndex];
			vertex.hasNormal = !IS_QNAN(aiNormal);
			if (vertex.hasNormal) {
				vertex.normal.x = aiNormal.x;
				vertex.normal.y = aiNormal.y;
				vertex.normal.z = aiNormal.z;
				//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "vertex normal %d: %f %f %f",vertexIndex, vertex.normal.x, vertex.normal.y,vertex.normal.z);
			}

			//! vertex tangent, x is positive in texture axis
			if (mesh->HasTangentsAndBitangents()) {
				LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
						"Fetching tangent for vertex %d", vertexIndex );
				const aiVector3D& aiTangent = mesh->mTangents[vertexIndex];
				const aiVector3D& aiBitangent = mesh->mBitangents[vertexIndex];
				vertex.hasTangent = !IS_QNAN(aiBitangent) && !IS_QNAN(aiTangent);

				const float3 tangent(aiTangent.x, aiTangent.y, aiTangent.z);
				//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "vertex tangent %d: %f %f %f",vertexIndex, tangent.x, tangent.y,tangent.z);
				piece->sTangents.push_back(tangent);

				const float3 bitangent(aiBitangent.x, aiBitangent.y, aiBitangent.z);
				//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "vertex bitangent %d: %f %f %f",vertexIndex, bitangent.x, bitangent.y,bitangent.z);
				piece->tTangents.push_back(bitangent);
			}

			//! vertex texcoords
			if (mesh->HasTextureCoords(0)) {
				vertex.textureX = mesh->mTextureCoords[0][vertexIndex].x;
				vertex.textureY = mesh->mTextureCoords[0][vertexIndex].y;
				//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "vertex texcoords %d: %f %f", vertexIndex, vertex.textureX, vertex.textureY);
			}

			mesh_vertex_mapping.push_back(piece->vertices.size());
			piece->vertices.push_back(vertex);
		}

		//! extract face data
		// FIXME add piece->vertexDrawOrder.reserve()
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
				"Processing faces for mesh %d (%d faces)",
				meshIndex, mesh->mNumFaces);
		for (unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
			const aiFace& face = mesh->mFaces[faceIndex];
			//! get the vertex belonging to the mesh
			for (unsigned vertexListID = 0; vertexListID < face.mNumIndices; ++vertexListID) {
				unsigned int vertexID = mesh_vertex_mapping[face.mIndices[vertexListID]];
				//LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "face %d vertex %d", faceIndex, vertexID);
				piece->vertexDrawOrder.push_back(vertexID);
			}
		}
	}

	//! collision volume for piece (not sure about these coords)
	// FIXME add metatable tags for this!!!!
	const float3 cvScales = piece->maxs - piece->mins;
	const float3 cvOffset = (piece->maxs - piece->offset) + (piece->mins - piece->offset);
	//const float3 cvOffset(piece->offset.x, piece->offset.y, piece->offset.z);
	piece->colvol = new CollisionVolume("box", cvScales, cvOffset, CollisionVolume::COLVOL_HITTEST_CONT);

	//! Get parent name from metadata or model
	if (pieceTable.KeyExists("parent")) {
		piece->parentName = pieceTable.GetString("parent", "");
	} else if (node->mParent) {
		if (node->mParent->mParent) {
			piece->parentName = std::string(node->mParent->mName.data);
		} else { //! my parent is the root, which gets renamed
			piece->parentName = "root";
		}
	} else {
		piece->parentName = "";
	}

	LOG_S(LOG_SECTION_PIECE, "Loaded model piece: %s with %d meshes",
			piece->name.c_str(), node->mNumMeshes);

	//! Verbose logging of piece properties
	LOG_S(LOG_SECTION_PIECE, "piece->name: %s", piece->name.c_str());
	LOG_S(LOG_SECTION_PIECE, "piece->parent: %s", piece->parentName.c_str());

	//! Recursively process all child pieces
	for (unsigned int i = 0; i < node->mNumChildren; ++i) {
		LoadPiece(model, node->mChildren[i], metaTable);
	}

	model->pieces[piece->name] = piece;
	return piece;
}
示例#22
0
void CAssParser::LoadPieceGeometry(SAssPiece* piece, const aiNode* pieceNode, const aiScene* scene)
{
	// Get vertex data from node meshes
	for (unsigned meshListIndex = 0; meshListIndex < pieceNode->mNumMeshes; ++meshListIndex) {
		const unsigned int meshIndex = pieceNode->mMeshes[meshListIndex];
		const aiMesh* mesh = scene->mMeshes[meshIndex];

		LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching mesh %d from scene", meshIndex);
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
			"Processing vertices for mesh %d (%d vertices)",
			meshIndex, mesh->mNumVertices);
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG,
			"Normals: %s Tangents/Bitangents: %s TexCoords: %s",
			(mesh->HasNormals() ? "Y" : "N"),
			(mesh->HasTangentsAndBitangents() ? "Y" : "N"),
			(mesh->HasTextureCoords(0) ? "Y" : "N"));

		piece->vertices.reserve(piece->vertices.size() + mesh->mNumVertices);
		piece->vertexDrawIndices.reserve(piece->vertexDrawIndices.size() + mesh->mNumFaces * 3);

		std::vector<unsigned> mesh_vertex_mapping;

		// extract vertex data per mesh
		for (unsigned vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) {
			const aiVector3D& aiVertex = mesh->mVertices[vertexIndex];

			SAssVertex vertex;

			// vertex coordinates
			vertex.pos = aiVectorToFloat3(aiVertex);

			// update piece min/max extents
			piece->mins = float3::min(piece->mins, vertex.pos);
			piece->maxs = float3::max(piece->maxs, vertex.pos);

			// vertex normal
			LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching normal for vertex %d", vertexIndex);

			const aiVector3D& aiNormal = mesh->mNormals[vertexIndex];

			if (!IS_QNAN(aiNormal)) {
				vertex.normal = aiVectorToFloat3(aiNormal);
			}

			// vertex tangent, x is positive in texture axis
			if (mesh->HasTangentsAndBitangents()) {
				LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Fetching tangent for vertex %d", vertexIndex);

				const aiVector3D& aiTangent = mesh->mTangents[vertexIndex];
				const aiVector3D& aiBitangent = mesh->mBitangents[vertexIndex];

				vertex.sTangent = aiVectorToFloat3(aiTangent);
				vertex.tTangent = aiVectorToFloat3(aiBitangent);
			}

			// vertex tex-coords per channel
			for (unsigned int uvChanIndex = 0; uvChanIndex < NUM_MODEL_UVCHANNS; uvChanIndex++) {
				if (!mesh->HasTextureCoords(uvChanIndex))
					break;

				piece->SetNumTexCoorChannels(uvChanIndex + 1);

				vertex.texCoords[uvChanIndex].x = mesh->mTextureCoords[uvChanIndex][vertexIndex].x;
				vertex.texCoords[uvChanIndex].y = mesh->mTextureCoords[uvChanIndex][vertexIndex].y;
			}

			mesh_vertex_mapping.push_back(piece->vertices.size());
			piece->vertices.push_back(vertex);
		}

		// extract face data
		LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Processing faces for mesh %d (%d faces)", meshIndex, mesh->mNumFaces);

		/*
		 * since aiProcess_SortByPType is being used,
		 * we're sure we'll get only 1 type here,
		 * so combination check isn't needed, also
		 * anything more complex than triangles is
		 * being split thanks to aiProcess_Triangulate
		 */
		for (unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
			const aiFace& face = mesh->mFaces[faceIndex];

			// some models contain lines (mNumIndices == 2) which
			// we cannot render and they would need a 2nd drawcall)
			if (face.mNumIndices != 3)
				continue;

			for (unsigned vertexListID = 0; vertexListID < face.mNumIndices; ++vertexListID) {
				const unsigned int vertexFaceIdx = face.mIndices[vertexListID];
				const unsigned int vertexDrawIdx = mesh_vertex_mapping[vertexFaceIdx];
				piece->vertexDrawIndices.push_back(vertexDrawIdx);
			}
		}
	}

	piece->SetHasGeometryData(!piece->vertices.empty());
}
示例#23
0
void SAssPiece::DrawForList() const
{
	if (isEmpty) {
		return;
	}
	LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Compiling piece %s", name.c_str());
	//! Add GL commands to the pieces displaylist

	const SAssVertex* sAssV = &vertices[0];

	//! pass the tangents as 3D texture coordinates
	//! (array elements are float3's, which are 12
	//! bytes in size and each represent a single
	//! xyz triple)
	//! TODO: test if we have this many texunits
	//! (if not, could only send the s-tangents)?

	if (!sTangents.empty()) {
		glClientActiveTexture(GL_TEXTURE5);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(3, GL_FLOAT, sizeof(float3), &sTangents[0].x);
	}
	if (!tTangents.empty()) {
		glClientActiveTexture(GL_TEXTURE6);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(3, GL_FLOAT, sizeof(float3), &tTangents[0].x);
	}

	glClientActiveTexture(GL_TEXTURE0);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(SAssVertex), &sAssV->textureX);

	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(SAssVertex), &sAssV->pos.x);

	glEnableClientState(GL_NORMAL_ARRAY);
	glNormalPointer(GL_FLOAT, sizeof(SAssVertex), &sAssV->normal.x);

	/*
	 * since aiProcess_SortByPType is being used,
	 * we're sure we'll get only 1 type here,
	 * so combination check isn't needed, also
	 * anything more complex than triangles is
	 * being split thanks to aiProcess_Triangulate
	 */
	glDrawElements(GL_TRIANGLES, vertexDrawOrder.size(), GL_UNSIGNED_INT, &vertexDrawOrder[0]);

	if (!sTangents.empty()) {
		glClientActiveTexture(GL_TEXTURE6);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}
	if (!tTangents.empty()) {
		glClientActiveTexture(GL_TEXTURE5);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	glClientActiveTexture(GL_TEXTURE0);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);

	LOG_SL(LOG_SECTION_PIECE, L_DEBUG, "Completed compiling piece %s",
			name.c_str());
}
示例#24
0
S3DModel* CAssParser::Load(const std::string& modelFilePath)
{
	LOG_S(LOG_SECTION_MODEL, "Loading model: %s", modelFilePath.c_str() );
	const std::string modelPath  = FileSystem::GetDirectory(modelFilePath);
	const std::string modelName  = FileSystem::GetBasename(modelFilePath);

	//! LOAD METADATA
	//! Load the lua metafile. This contains properties unique to Spring models and must return a table
	std::string metaFileName = modelFilePath + ".lua";
	if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) {
		//! Try again without the model file extension
		metaFileName = modelPath + '/' + modelName + ".lua";
	}
	LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
	if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) {
		LOG_S(LOG_SECTION_MODEL, "No meta-file '%s'. Using defaults.", metaFileName.c_str());
	} else if (!metaFileParser.Execute()) {
		LOG_SL(LOG_SECTION_MODEL, L_ERROR, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str());
	}

	//! Get the (root-level) model table
	const LuaTable& metaTable = metaFileParser.GetRoot();
	if (metaTable.IsValid()) {
		LOG_S(LOG_SECTION_MODEL, "Found valid model metadata in '%s'", metaFileName.c_str());
	}


	//! LOAD MODEL DATA
	//! Create a model importer instance
	Assimp::Importer importer;

	//! Create a logger for debugging model loading issues
	Assimp::DefaultLogger::create("",Assimp::Logger::VERBOSE);
	const unsigned int severity = Assimp::Logger::Debugging|Assimp::Logger::Info|Assimp::Logger::Err|Assimp::Logger::Warn;
	Assimp::DefaultLogger::get()->attachStream( new AssLogStream(), severity );

	//! Give the importer an IO class that handles Spring's VFS
	importer.SetIOHandler( new AssVFSSystem() );

	//! Speed-up processing by skipping things we don't need
	importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_CAMERAS|aiComponent_LIGHTS|aiComponent_TEXTURES|aiComponent_ANIMATIONS);

#ifndef BITMAP_NO_OPENGL
	//! Optimize VBO-Mesh sizes/ranges
	GLint maxIndices  = 1024;
	GLint maxVertices = 1024;
	glGetIntegerv(GL_MAX_ELEMENTS_INDICES,  &maxIndices);
	glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices); //FIXME returns not optimal data, at best compute it ourself! (pre-TL cache size!)
	importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,   maxVertices);
	importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices/3);
#endif

	//! Read the model file to build a scene object
	LOG_S(LOG_SECTION_MODEL, "Importing model file: %s", modelFilePath.c_str() );
	const aiScene* scene = importer.ReadFile( modelFilePath, ASS_POSTPROCESS_OPTIONS );
	if (scene != NULL) {
		LOG_S(LOG_SECTION_MODEL,
				"Processing scene for model: %s (%d meshes / %d materials / %d textures)",
				modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials,
				scene->mNumTextures );
	} else {
		LOG_SL(LOG_SECTION_MODEL, L_ERROR, "Model Import: %s",
				importer.GetErrorString());
	}

	SAssModel* model = new SAssModel;
	model->name = modelFilePath;
	model->type = MODELTYPE_ASS;
	model->scene = scene;
	//model->meta = &metaTable;

	//! Gather per mesh info
	CalculatePerMeshMinMax(model);

	//! Assign textures
	//! The S3O texture handler uses two textures.
	//! The first contains diffuse color (RGB) and teamcolor (A)
	//! The second contains glow (R), reflectivity (G) and 1-bit Alpha (A).
	if (metaTable.KeyExists("tex1")) {
		model->tex1 = metaTable.GetString("tex1", "default.png");
	} else {
		//! Search for a texture
		std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + ".*");
		for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
			model->tex1 = FileSystem::GetFilename(*fi);
			break; //! there can be only one!
		}
	}
	if (metaTable.KeyExists("tex2")) {
		model->tex2 = metaTable.GetString("tex2", "");
	} else {
		//! Search for a texture
		std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + "2.*");
		for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
			model->tex2 = FileSystem::GetFilename(*fi);
			break; //! there can be only one!
		}
	}
	model->flipTexY = metaTable.GetBool("fliptextures", true); //! Flip texture upside down
	model->invertTexAlpha = metaTable.GetBool("invertteamcolor", true); //! Reverse teamcolor levels

	//! Load textures
	LOG_S(LOG_SECTION_MODEL, "Loading textures. Tex1: '%s' Tex2: '%s'",
			model->tex1.c_str(), model->tex2.c_str());
	texturehandlerS3O->LoadS3OTexture(model);

	//! Load all pieces in the model
	LOG_S(LOG_SECTION_MODEL, "Loading pieces from root node '%s'",
			scene->mRootNode->mName.data);
	LoadPiece(model, scene->mRootNode, metaTable);

	//! Update piece hierarchy based on metadata
	BuildPieceHierarchy( model );

	//! Simplified dimensions used for rough calculations
	model->radius = metaTable.GetFloat("radius", model->radius);
	model->height = metaTable.GetFloat("height", model->height);
	model->relMidPos = metaTable.GetFloat3("midpos", model->relMidPos);
	model->mins = metaTable.GetFloat3("mins", model->mins);
	model->maxs = metaTable.GetFloat3("maxs", model->maxs);

	//! Calculate model dimensions if not set
	if (!metaTable.KeyExists("mins") || !metaTable.KeyExists("maxs")) CalculateMinMax( model->rootPiece );
	if (model->radius < 0.0001f) CalculateRadius( model );
	if (model->height < 0.0001f) CalculateHeight( model );

	//! Verbose logging of model properties
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str());
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]);
	LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]);

	LOG_S(LOG_SECTION_MODEL, "Model %s Imported.", model->name.c_str());
	return model;
}
示例#25
0
void CArchiveScanner::ScanArchive(const std::string& fullName, bool doChecksum)
{
	const std::string fn    = FileSystem::GetFilename(fullName);
	const std::string fpath = FileSystem::GetDirectory(fullName);
	const std::string lcfn  = StringToLower(fn);

	// Cache variables
	std::map<std::string, ArchiveInfo>::iterator aii;
	bool cached = false;

	// Stat file
	struct stat info = {0};
	int statfailed = stat(fullName.c_str(), &info);

	// If stat fails, assume the archive is not broken nor cached
	if (!statfailed) {
		// Determine whether this archive has earlier be found to be broken
		std::map<std::string, BrokenArchive>::iterator bai = brokenArchives.find(lcfn);
		if (bai != brokenArchives.end()) {
			if ((unsigned)info.st_mtime == bai->second.modified && fpath == bai->second.path) {
				bai->second.updated = true;
				return;
			}
		}


		// Determine whether to rely on the cached info or not
		if ((aii = archiveInfos.find(lcfn)) != archiveInfos.end()) {
			// This archive may have been obsoleted, do not process it if so
			if (aii->second.replaced.length() > 0) {
				return;
			}

			if ((unsigned)info.st_mtime == aii->second.modified && fpath == aii->second.path) {
				cached = true;
				aii->second.updated = true;
			}

			// If we are here, we could have invalid info in the cache
			// Force a reread if it is a directory archive (.sdd), as
			// st_mtime only reflects changes to the directory itself,
			// not the contents.
			if (!cached) {
				archiveInfos.erase(aii);
			}
		}
	}

	// Time to parse the info we are interested in
	if (cached) {
		// If cached is true, aii will point to the archive
		if (doChecksum && (aii->second.checksum == 0))
			aii->second.checksum = GetCRC(fullName);
	} else {
		IArchive* ar = archiveLoader.OpenArchive(fullName);
		if (!ar || !ar->IsOpen()) {
			LOG("Unable to open archive: %s", fullName.c_str());
			return;
		}

		ArchiveInfo ai;

		std::string error;
		std::string mapfile;

		const bool hasModinfo = ar->FileExists("modinfo.lua");
		const bool hasMapinfo = ar->FileExists("mapinfo.lua");

		// check for smf/sm3 and if the uncompression of important files is too costy
		for (unsigned fid = 0; fid != ar->NumFiles(); ++fid) {
			std::string name;
			int size;
			ar->FileInfo(fid, name, size);
			const std::string lowerName = StringToLower(name);
			const std::string ext = FileSystem::GetExtension(lowerName);

			if ((ext == "smf") || (ext == "sm3")) {
				mapfile = name;
			}

			const unsigned char metaFileClass = GetMetaFileClass(lowerName);
			if ((metaFileClass != 0) && !(ar->HasLowReadingCost(fid))) {
				// is a meta-file and not cheap to read
				if (metaFileClass == 1) {
					// 1st class
					error = "Unpacking/reading cost for meta file " + name
							+ " is too high, please repack the archive (make sure to use a non-solid algorithm, if applicable)";
					break;
				} else if (metaFileClass == 2) {
					// 2nd class
					LOG_SL(LOG_SECTION_ARCHIVESCANNER, L_WARNING,
							"Archive %s: The cost for reading a 2nd-class meta-file is too high: %s",
							fullName.c_str(), name.c_str());
				}
			}
		}

		/*
		if (!error.empty()) {
			// we already have an error, no further evaluation required
		}
		*/
		if (hasMapinfo || !mapfile.empty()) {
			// it is a map
			if (hasMapinfo) {
				ScanArchiveLua(ar, "mapinfo.lua", ai, error);
			} else if (hasModinfo) {
				// backwards-compat for modinfo.lua in maps
				ScanArchiveLua(ar, "modinfo.lua", ai, error);
			}

			if (ai.archiveData.GetName().empty()) {
				// FIXME The name will never be empty, if version is set (see HACK in ArchiveData)
				ai.archiveData.SetInfoItemValueString("name_pure", FileSystem::GetBasename(mapfile));
				ai.archiveData.SetInfoItemValueString("name", FileSystem::GetBasename(mapfile));
			}
			if (ai.archiveData.GetMapFile().empty()) {
				ai.archiveData.SetInfoItemValueString("mapfile", mapfile);
			}

			AddDependency(ai.archiveData.GetDependencies(), "Map Helper v1");
			ai.archiveData.SetInfoItemValueInteger("modType", modtype::map);

			LOG_S(LOG_SECTION_ARCHIVESCANNER, "Found new map: %s",
					ai.archiveData.GetNameVersioned().c_str());
		} else if (hasModinfo) {
			// it is a mod
			ScanArchiveLua(ar, "modinfo.lua", ai, error);
			if (ai.archiveData.GetModType() == modtype::primary) {
				AddDependency(ai.archiveData.GetDependencies(), "Spring content v1");
			}

			LOG_S(LOG_SECTION_ARCHIVESCANNER, "Found new game: %s",
					ai.archiveData.GetNameVersioned().c_str());
		} else {
			// neither a map nor a mod: error
			error = "missing modinfo.lua/mapinfo.lua";
		}

		delete ar;

		if (!error.empty()) {
			// for some reason, the archive is marked as broken
			LOG_L(L_WARNING, "Failed to scan %s (%s)",
					fullName.c_str(), error.c_str());

			// record it as broken, so we don't need to look inside everytime
			BrokenArchive ba;
			ba.path = fpath;
			ba.modified = info.st_mtime;
			ba.updated = true;
			ba.problem = error;
			brokenArchives[lcfn] = ba;
			return;
		}

		ai.path = fpath;
		ai.modified = info.st_mtime;
		ai.origName = fn;
		ai.updated = true;

		// Optionally calculate a checksum for the file
		// To prevent reading all files in all directory (.sdd) archives
		// every time this function is called, directory archive checksums
		// are calculated on the fly.
		if (doChecksum) {
			ai.checksum = GetCRC(fullName);
		} else {
			ai.checksum = 0;
		}

		archiveInfos[lcfn] = ai;
	}
}