pawsFrameDrawable::pawsFrameDrawable(csRef<iDocumentNode> node) : scfImplementationType (this) { defaultTransparentColourBlue = -1; defaultTransparentColourGreen = -1; defaultTransparentColourRed = -1; defaultAlphaValue = 0; // Read off the image and file vars imageFileLocation = node->GetAttributeValue("file"); resourceName = node->GetAttributeValue("resource"); csString typeStr(node->GetAttributeValue("type")); type = FDT_FULL; if (typeStr == "horizontal") type = FDT_HORIZONTAL; else if (typeStr == "vertical") type = FDT_VERTICAL; csRef<iDocumentNodeIterator> iter = node->GetNodes(); while ( iter->HasNext() ) { csRef<iDocumentNode> childNode = iter->Next(); // Read the default alpha value. if (strcmp(childNode->GetValue(), "alpha") == 0) defaultAlphaValue = childNode->GetAttributeValueAsInt("level"); // Read the default transparent colour. else if (strcmp(childNode->GetValue(), "trans") == 0) { defaultTransparentColourRed = childNode->GetAttributeValueAsInt("r"); defaultTransparentColourGreen = childNode->GetAttributeValueAsInt("g"); defaultTransparentColourBlue = childNode->GetAttributeValueAsInt("b"); } else if (strcmp(childNode->GetValue(), "top_left") == 0) LoadPiece(childNode, FDP_TOP_LEFT); else if (strcmp(childNode->GetValue(), "top") == 0) LoadPiece(childNode, FDP_TOP); else if (strcmp(childNode->GetValue(), "top_right") == 0) LoadPiece(childNode, FDP_TOP_RIGHT); else if (strcmp(childNode->GetValue(), "left") == 0) LoadPiece(childNode, FDP_LEFT); else if (strcmp(childNode->GetValue(), "middle") == 0) LoadPiece(childNode, FDP_MIDDLE); else if (strcmp(childNode->GetValue(), "right") == 0) LoadPiece(childNode, FDP_RIGHT); else if (strcmp(childNode->GetValue(), "bottom_left") == 0) LoadPiece(childNode, FDP_BOTTOM_LEFT); else if (strcmp(childNode->GetValue(), "bottom") == 0) LoadPiece(childNode, FDP_BOTTOM); else if (strcmp(childNode->GetValue(), "bottom_right") == 0) LoadPiece(childNode, FDP_BOTTOM_RIGHT); } }
S3DOPiece* C3DOParser::LoadPiece(S3DModel* model, int pos, S3DOPiece* parent, int* numobj, const std::vector<unsigned char>& fileBuf) { (*numobj)++; _3DObject me; int curOffset = pos; READ_3DOBJECT(me, fileBuf, curOffset); std::string s = GET_TEXT(me.OffsetToObjectName, fileBuf, curOffset); StringToLowerInPlace(s); S3DOPiece* piece = new S3DOPiece(); piece->name = s; piece->parent = parent; piece->offset.x = me.XFromParent * SCALE_FACTOR_3DO; piece->offset.y = me.YFromParent * SCALE_FACTOR_3DO; piece->offset.z = -me.ZFromParent * SCALE_FACTOR_3DO; piece->goffset = piece->offset + ((parent != NULL)? parent->goffset: ZeroVector); GetVertexes(&me, piece, fileBuf); GetPrimitives(piece, me.OffsetToPrimitiveArray, me.NumberOfPrimitives, ((pos == 0)? me.SelectionPrimitive: -1), fileBuf); piece->CalcNormals(); piece->SetMinMaxExtends(); piece->emitPos = ZeroVector; piece->emitDir = FwdVector; if (piece->vertexPos.size() >= 2) { piece->emitPos = piece->vertexPos[0]; piece->emitDir = piece->vertexPos[1] - piece->vertexPos[0]; } else if (piece->vertexPos.size() == 1) { piece->emitDir = piece->vertexPos[0]; } model->mins = float3::min(piece->goffset + piece->mins, model->mins); model->maxs = float3::max(piece->goffset + piece->maxs, model->maxs); piece->SetCollisionVolume(CollisionVolume("box", piece->maxs - piece->mins, (piece->maxs + piece->mins) * 0.5f)); if (me.OffsetToChildObject > 0) { piece->children.push_back(LoadPiece(model, me.OffsetToChildObject, piece, numobj, fileBuf)); } if (me.OffsetToSiblingObject > 0) { parent->children.push_back(LoadPiece(model, me.OffsetToSiblingObject, parent, numobj, fileBuf)); } return piece; }
S3DModel* C3DOParser::Load(const std::string& name) { CFileHandler file(name); std::vector<unsigned char> fileBuf; if (!file.FileExists()) { throw content_error("[3DOParser] could not find model-file " + name); } fileBuf.resize(file.FileSize(), 0); if (file.Read(&fileBuf[0], file.FileSize()) == 0) { throw content_error("[3DOParser] failed to read model-file " + name); } S3DModel* model = new S3DModel(); model->name = name; model->type = MODELTYPE_3DO; model->textureType = 0; model->numPieces = 0; model->mins = DEF_MIN_SIZE; model->maxs = DEF_MAX_SIZE; S3DOPiece* rootPiece = LoadPiece(model, 0, NULL, &model->numPieces, fileBuf); model->SetRootPiece(rootPiece); // set after the extrema are known model->radius = (model->maxs - model->mins ).Length() * 0.5f; model->height = (model->maxs.y - model->mins.y); model->relMidPos = (model->maxs + model->mins) * 0.5f; return model; }
S3DModel* CS3OParser::Load(const std::string& name) { CFileHandler file(name); if (!file.FileExists()) { throw content_error("[S3OParser] could not find model-file " + name); } std::vector<unsigned char> fileBuf(file.FileSize()); file.Read(&fileBuf[0], file.FileSize()); S3OHeader header; memcpy(&header, &fileBuf[0], sizeof(header)); header.swap(); S3DModel* model = new S3DModel(); model->name = name; model->type = MODELTYPE_S3O; model->numPieces = 0; model->tex1 = (char*) &fileBuf[header.texture1]; model->tex2 = (char*) &fileBuf[header.texture2]; model->mins = DEF_MIN_SIZE; model->maxs = DEF_MAX_SIZE; texturehandlerS3O->PreloadS3OTexture(model); SS3OPiece* rootPiece = LoadPiece(model, NULL, &fileBuf[0], header.rootPiece); model->SetRootPiece(rootPiece); // set after the extrema are known model->radius = (header.radius <= 0.01f)? (model->maxs - model->mins ).Length() * 0.5f: header.radius; model->height = (header.height <= 0.01f)? (model->maxs.y - model->mins.y) : header.height; model->relMidPos = float3(header.midx, header.midy, header.midz); return model; }
S3DModel* CS3OParser::Load(const std::string& name) { CFileHandler file(name); if (!file.FileExists()) { throw content_error("[S3OParser] could not find model-file " + name); } unsigned char* fileBuf = new unsigned char[file.FileSize()]; file.Read(fileBuf, file.FileSize()); S3OHeader header; memcpy(&header, fileBuf, sizeof(header)); header.swap(); S3DModel* model = new S3DModel; model->name = name; model->type = MODELTYPE_S3O; model->numPieces = 0; model->tex1 = (char*) &fileBuf[header.texture1]; model->tex2 = (char*) &fileBuf[header.texture2]; model->mins = DEF_MIN_SIZE; model->maxs = DEF_MAX_SIZE; texturehandlerS3O->LoadS3OTexture(model); SS3OPiece* rootPiece = LoadPiece(model, NULL, fileBuf, header.rootPiece); model->SetRootPiece(rootPiece); model->radius = (header.radius <= 0.01f)? (model->maxs.y - model->mins.y): header.radius; model->height = (header.height <= 0.01f)? (model->radius + model->radius): header.height; model->relMidPos = float3(header.midx, header.midy, header.midz); model->relMidPos.y = std::max(model->relMidPos.y, 1.0f); // ? delete[] fileBuf; return model; }
S3DModel C3DOParser::Load(const std::string& name) { CFileHandler file(name); std::vector<unsigned char> fileBuf; if (!file.FileExists()) throw content_error("[3DOParser] could not find model-file " + name); if (!file.IsBuffered()) { fileBuf.resize(file.FileSize(), 0); if (file.Read(fileBuf.data(), fileBuf.size()) == 0) throw content_error("[3DOParser] failed to read model-file " + name); } else { fileBuf = std::move(file.GetBuffer()); } S3DModel model; model.name = name; model.type = MODELTYPE_3DO; model.textureType = 0; model.numPieces = 0; model.mins = DEF_MIN_SIZE; model.maxs = DEF_MAX_SIZE; model.FlattenPieceTree(LoadPiece(&model, 0, nullptr, &model.numPieces, fileBuf)); // set after the extrema are known model.radius = model.CalcDrawRadius(); model.height = model.CalcDrawHeight(); model.relMidPos = model.CalcDrawMidPos(); return model; }
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; }
SS3O* CS3OParser::LoadPiece(unsigned char* buf, int offset,CModel* model, int parent) { int cur = model->numobjects++; SS3O* piece=new SS3O; Piece* fp=(Piece*)&buf[offset]; piece->offset.x=fp->xoffset; piece->offset.y=fp->yoffset; piece->offset.z=fp->zoffset; piece->primitiveType=fp->primitiveType; piece->name=(char*)&buf[fp->name]; ModelPieceInfo mpi; mpi.name = piece->name; // ModelPieceInfo::offset is in non-TA style coordinates, // so the animation doesn't have to mess around with negating z mpi.offset = float3(piece->offset.x, piece->offset.y, -piece->offset.z); mpi.parent = parent; model->pieceInfo.push_back(mpi); int vertexPointer=fp->vertices; for(int a=0;a<fp->numVertices;++a){ piece->vertices.push_back(*(SS3OVertex*)&buf[vertexPointer]); /* piece->vertices.back().normal.x=piece->vertices.back().pos.x; piece->vertices.back().normal.y=piece->vertices.back().pos.y; piece->vertices.back().normal.z=piece->vertices.back().pos.z; piece->vertices.back().normal.Normalize();*/ vertexPointer+=sizeof(Vertex); } int vertexTablePointer=fp->vertexTable; for(int a=0;a<fp->vertexTableSize;++a){ int num=*(int*)&buf[vertexTablePointer]; piece->vertexDrawOrder.push_back(num); vertexTablePointer+=sizeof(int); if(num==-1 && a!=fp->vertexTableSize-1){ //for triangle strips piece->vertexDrawOrder.push_back(num); num=*(int*)&buf[vertexTablePointer]; piece->vertexDrawOrder.push_back(num); } } int childPointer=fp->childs; for(int a=0;a<fp->numChilds;++a){ piece->childs.push_back(LoadPiece(buf,*(int*)&buf[childPointer],model,cur)); childPointer+=sizeof(int); } return piece; }
S3DModel* CS3OParser::Load(std::string name) { PUSH_CODE_MODE; ENTER_SYNCED; CFileHandler file(name); if(!file.FileExists()){ POP_CODE_MODE; throw content_error("File not found: "+name); } unsigned char* fileBuf=SAFE_NEW unsigned char[file.FileSize()]; file.Read(fileBuf, file.FileSize()); S3OHeader header; memcpy(&header,fileBuf,sizeof(header)); header.swap(); S3DModel *model = SAFE_NEW S3DModel; model->type=MODELTYPE_S3O; model->numobjects=0; model->name=name; model->tex1=(char*)&fileBuf[header.texture1]; model->tex2=(char*)&fileBuf[header.texture2]; texturehandlerS3O->LoadS3OTexture(model); SS3OPiece* object=LoadPiece(fileBuf,header.rootPiece,model); object->type=MODELTYPE_S3O; FindMinMax(object); model->rootobject=object; model->radius = header.radius; model->height = header.height; model->relMidPos.x=header.midx; model->relMidPos.y=header.midy; model->relMidPos.z=header.midz; if(model->relMidPos.y<1) model->relMidPos.y=1; model->maxx=object->maxx; model->maxy=object->maxy; model->maxz=object->maxz; model->minx=object->minx; model->miny=object->miny; model->minz=object->minz; delete[] fileBuf; POP_CODE_MODE; return model; }
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; }
SS3OPiece* CS3OParser::LoadPiece(unsigned char* buf, int offset,S3DModel* model) { model->numobjects++; SS3OPiece* piece = SAFE_NEW SS3OPiece; piece->type = MODELTYPE_S3O; Piece* fp = (Piece*)&buf[offset]; fp->swap(); // Does it matter we mess with the original buffer here? Don't hope so. piece->offset.x = fp->xoffset; piece->offset.y = fp->yoffset; piece->offset.z = fp->zoffset; piece->primitiveType = fp->primitiveType; piece->name = (char*)&buf[fp->name]; int vertexPointer=fp->vertices; for(int a=0;a<fp->numVertices;++a){ ((Vertex*)&buf[vertexPointer])->swap(); // Does it matter we mess with the original buffer here? piece->vertices.push_back(*(SS3OVertex*)&buf[vertexPointer]); /* piece->vertices.back().normal.x=piece->vertices.back().pos.x; piece->vertices.back().normal.y=piece->vertices.back().pos.y; piece->vertices.back().normal.z=piece->vertices.back().pos.z; piece->vertices.back().normal.Normalize();*/ vertexPointer+=sizeof(Vertex); } int vertexTablePointer=fp->vertexTable; for(int a=0;a<fp->vertexTableSize;++a){ int num = swabdword(*(int*)&buf[vertexTablePointer]); piece->vertexDrawOrder.push_back(num); vertexTablePointer += sizeof(int); if(num==-1 && a!=fp->vertexTableSize-1){ //for triangle strips piece->vertexDrawOrder.push_back(num); num = swabdword(*(int*)&buf[vertexTablePointer]); piece->vertexDrawOrder.push_back(num); } } piece->isEmpty = false;//piece->vertexDrawOrder.empty(); piece->vertexCount = piece->vertices.size(); int childPointer = fp->childs; for(int a=0;a<fp->numChilds;++a){ piece->childs.push_back(LoadPiece(buf,swabdword(*(int*)&buf[childPointer]),model)); childPointer += sizeof(int); } return piece; }
SS3O* CS3OParser::LoadPiece(unsigned char* buf, int offset,S3DOModel* model) { model->numobjects++; SS3O* piece=new SS3O; Piece* fp=(Piece*)&buf[offset]; piece->offset.x=fp->xoffset; piece->offset.y=fp->yoffset; piece->offset.z=fp->zoffset; piece->primitiveType=fp->primitiveType; piece->name=(char*)&buf[fp->name]; int vertexPointer=fp->vertices; for(int a=0;a<fp->numVertices;++a){ piece->vertices.push_back(*(SS3OVertex*)&buf[vertexPointer]); /* piece->vertices.back().normal.x=piece->vertices.back().pos.x; piece->vertices.back().normal.y=piece->vertices.back().pos.y; piece->vertices.back().normal.z=piece->vertices.back().pos.z; piece->vertices.back().normal.Normalize();*/ vertexPointer+=sizeof(Vertex); } int vertexTablePointer=fp->vertexTable; for(int a=0;a<fp->vertexTableSize;++a){ int num=*(int*)&buf[vertexTablePointer]; piece->vertexDrawOrder.push_back(num); vertexTablePointer+=sizeof(int); if(num==-1 && a!=fp->vertexTableSize-1){ //for triangle strips piece->vertexDrawOrder.push_back(num); num=*(int*)&buf[vertexTablePointer]; piece->vertexDrawOrder.push_back(num); } } int childPointer=fp->childs; for(int a=0;a<fp->numChilds;++a){ piece->childs.push_back(LoadPiece(buf,*(int*)&buf[childPointer],model)); childPointer+=sizeof(int); } return piece; }
CModel* CS3OParser::Load3DO(string name,float scale,int side) { if(name.find(".")==string::npos) name+=".s3o"; StringToLowerInPlace(name); map<string,CModel*>::iterator ui; if((ui=units.find(name))!=units.end()){ return ui->second; } PUSH_CODE_MODE; ENTER_SYNCED; CFileHandler file(name); if(!file.FileExists()){ POP_CODE_MODE; throw content_error("File not found: "+name); } unsigned char* fileBuf=new unsigned char[file.FileSize()]; file.Read(fileBuf, file.FileSize()); S3OHeader header; memcpy(&header,fileBuf,sizeof(header)); CModel *model = new CModel; model->numobjects=0; SS3O* object=LoadPiece(fileBuf,header.rootPiece,model,-1); model->rootobjects3o=object; model->rootobject3do=0; object->isEmpty=true; model->name=name; model->textureType=texturehandler->LoadS3OTexture((char*)&fileBuf[header.texture1],(char*)&fileBuf[header.texture2]); FindMinMax(object); units[name]=model; CreateLists(object); model->radius = header.radius*scale; //this is a hack to make aircrafts less likely to collide and get hit by nontracking weapons model->height = header.height; model->relMidPos.x=header.midx; model->relMidPos.y=header.midy; model->relMidPos.z=header.midz; if(model->relMidPos.y<1) model->relMidPos.y=1; // info->AddLine("%s has height %f",name,model->height); fartextureHandler->CreateFarTexture(model); model->maxx=model->rootobjects3o->maxx; model->maxy=model->rootobjects3o->maxy; model->maxz=model->rootobjects3o->maxz; model->minx=model->rootobjects3o->minx; model->miny=model->rootobjects3o->miny; model->minz=model->rootobjects3o->minz; delete[] fileBuf; POP_CODE_MODE; return model; }
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; }
S3DOModel* CS3OParser::LoadS3O(std::string name,float scale,int side) { GML_STDMUTEX_LOCK(model); // LoadS3O if(name.find(".")==std::string::npos) name+=".s3o"; StringToLowerInPlace(name); std::map<std::string,S3DOModel*>::iterator ui; if((ui=units.find(name))!=units.end()){ return ui->second; } PUSH_CODE_MODE; ENTER_SYNCED; CFileHandler file(name); if(!file.FileExists()){ POP_CODE_MODE; throw content_error("File not found: "+name); } unsigned char* fileBuf=SAFE_NEW unsigned char[file.FileSize()]; file.Read(fileBuf, file.FileSize()); S3OHeader header; memcpy(&header,fileBuf,sizeof(header)); header.swap(); S3DOModel *model = SAFE_NEW S3DOModel; model->numobjects=0; SS3O* object=LoadPiece(fileBuf,header.rootPiece,model); model->rootobjects3o=object; model->rootobject3do=0; object->isEmpty=true; model->name=name; model->tex1=(char*)&fileBuf[header.texture1]; model->tex2=(char*)&fileBuf[header.texture2]; texturehandler->LoadS3OTexture(model); FindMinMax(object); units[name]=model; CreateLists(object); // this is a hack to make aircrafts less likely to collide and get hit by nontracking weapons // note: does not apply anymore, unit <--> projectile coldet no longer depends on model->radius model->radius = header.radius * scale; model->height = header.height; model->relMidPos.x=header.midx; model->relMidPos.y=header.midy; model->relMidPos.z=header.midz; if(model->relMidPos.y<1) model->relMidPos.y=1; // logOutput.Print("%s has height %f",name,model->height); fartextureHandler->CreateFarTexture(model); model->maxx=model->rootobjects3o->maxx; model->maxy=model->rootobjects3o->maxy; model->maxz=model->rootobjects3o->maxz; model->minx=model->rootobjects3o->minx; model->miny=model->rootobjects3o->miny; model->minz=model->rootobjects3o->minz; delete[] fileBuf; POP_CODE_MODE; return model; }
SS3OPiece* CS3OParser::LoadPiece(S3DModel* model, SS3OPiece* parent, unsigned char* buf, int offset) { model->numPieces++; Piece* fp = (Piece*)&buf[offset]; fp->swap(); // Does it matter we mess with the original buffer here? Don't hope so. SS3OPiece* piece = new SS3OPiece(); piece->type = MODELTYPE_S3O; piece->mins = DEF_MIN_SIZE; piece->maxs = DEF_MAX_SIZE; piece->offset.x = fp->xoffset; piece->offset.y = fp->yoffset; piece->offset.z = fp->zoffset; piece->primitiveType = fp->primitiveType; piece->name = (char*) &buf[fp->name]; piece->parent = parent; piece->vertices.reserve(fp->numVertices); piece->vertexDrawOrder.reserve((size_t)(fp->vertexTableSize * 1.1f)); //1.1f is just a guess (check below) // retrieve each vertex int vertexOffset = fp->vertices; for (int a = 0; a < fp->numVertices; ++a) { Vertex* v = reinterpret_cast<Vertex*>(&buf[vertexOffset]); v->swap(); SS3OVertex* sv = reinterpret_cast<SS3OVertex*>(&buf[vertexOffset]); sv->normal.SafeANormalize(); piece->vertices.push_back(*sv); vertexOffset += sizeof(Vertex); } // retrieve the draw order for the vertices int vertexTableOffset = fp->vertexTable; for (int a = 0; a < fp->vertexTableSize; ++a) { const int vertexDrawIdx = swabDWord(*(int*) &buf[vertexTableOffset]); piece->vertexDrawOrder.push_back(vertexDrawIdx); vertexTableOffset += sizeof(int); } piece->isEmpty = piece->vertexDrawOrder.empty(); piece->goffset = piece->offset + ((parent != NULL)? parent->goffset: ZeroVector); piece->SetVertexTangents(); piece->SetMinMaxExtends(); model->mins.x = std::min(piece->mins.x, model->mins.x); model->mins.y = std::min(piece->mins.y, model->mins.y); model->mins.z = std::min(piece->mins.z, model->mins.z); model->maxs.x = std::max(piece->maxs.x, model->maxs.x); model->maxs.y = std::max(piece->maxs.y, model->maxs.y); model->maxs.z = std::max(piece->maxs.z, model->maxs.z); const float3 cvScales = piece->maxs - piece->mins; const float3 cvOffset = (piece->maxs - piece->goffset) + (piece->mins - piece->goffset); piece->SetCollisionVolume(new CollisionVolume("box", cvScales, cvOffset * 0.5f, CollisionVolume::COLVOL_HITTEST_CONT)); int childTableOffset = fp->childs; for (int a = 0; a < fp->numChilds; ++a) { int childOffset = swabDWord(*(int*) &buf[childTableOffset]); SS3OPiece* childPiece = LoadPiece(model, piece, buf, childOffset); piece->childs.push_back(childPiece); childTableOffset += sizeof(int); } return piece; }
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; }
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; }
SS3OPiece* CS3OParser::LoadPiece(S3DModel* model, SS3OPiece* parent, unsigned char* buf, int offset) { model->numPieces++; Piece* fp = (Piece*)&buf[offset]; fp->swap(); // Does it matter we mess with the original buffer here? Don't hope so. SS3OPiece* piece = new SS3OPiece(); piece->offset.x = fp->xoffset; piece->offset.y = fp->yoffset; piece->offset.z = fp->zoffset; piece->primType = fp->primitiveType; piece->name = (char*) &buf[fp->name]; piece->parent = parent; if (parent != NULL) { piece->parentName = parent->name; } piece->SetVertexCount(fp->numVertices); piece->SetVertexDrawIndexCount(fp->vertexTableSize); // retrieve each vertex int vertexOffset = fp->vertices; for (int a = 0; a < fp->numVertices; ++a) { Vertex* v = reinterpret_cast<Vertex*>(&buf[vertexOffset]); v->swap(); SS3OVertex sv; sv.pos = float3(v->xpos, v->ypos, v->zpos); sv.normal = float3(v->xnormal, v->ynormal, v->znormal).SafeANormalize(); sv.texCoord = float2(v->texu, v->texv); piece->SetVertex(a, sv); vertexOffset += sizeof(Vertex); } // retrieve the draw order for the vertices int vertexTableOffset = fp->vertexTable; for (int a = 0; a < fp->vertexTableSize; ++a) { const int vertexDrawIdx = swabDWord(*(int*) &buf[vertexTableOffset]); piece->SetVertexDrawIndex(a, vertexDrawIdx); vertexTableOffset += sizeof(int); } piece->goffset = piece->offset + ((parent != NULL)? parent->goffset: ZeroVector); piece->SetHasGeometryData(piece->GetVertexDrawIndexCount() != 0); piece->SetVertexTangents(); piece->SetMinMaxExtends(); model->mins = std::min(piece->goffset + piece->mins, model->mins); model->maxs = std::max(piece->goffset + piece->maxs, model->maxs); piece->SetCollisionVolume(new CollisionVolume("box", piece->maxs - piece->mins, (piece->maxs + piece->mins) * 0.5f)); int childTableOffset = fp->children; for (int a = 0; a < fp->numchildren; ++a) { int childOffset = swabDWord(*(int*) &buf[childTableOffset]); SS3OPiece* childPiece = LoadPiece(model, piece, buf, childOffset); piece->children.push_back(childPiece); childTableOffset += sizeof(int); } return piece; }
SS3OPiece* CS3OParser::LoadPiece(S3DModel* model, SS3OPiece* parent, unsigned char* buf, int offset) { model->numPieces++; // retrieve piece data Piece* fp = (Piece*)&buf[offset]; fp->swap(); // Does it matter we mess with the original buffer here? Don't hope so. Vertex* vertexList = reinterpret_cast<Vertex*>(&buf[fp->vertices]); int* indexList = reinterpret_cast<int*>(&buf[fp->vertexTable]); int* childList = reinterpret_cast<int*>(&buf[fp->children]); // create piece SS3OPiece* piece = new SS3OPiece(); piece->offset.x = fp->xoffset; piece->offset.y = fp->yoffset; piece->offset.z = fp->zoffset; piece->primType = fp->primitiveType; piece->name = (char*) &buf[fp->name]; piece->parent = parent; // retrieve vertices piece->SetVertexCount(fp->numVertices); for (int a = 0; a < fp->numVertices; ++a) { Vertex* v = (vertexList++); v->swap(); SS3OVertex sv; sv.pos = float3(v->xpos, v->ypos, v->zpos); sv.normal = float3(v->xnormal, v->ynormal, v->znormal).SafeANormalize(); sv.texCoords[0] = float2(v->texu, v->texv); sv.texCoords[1] = float2(v->texu, v->texv); piece->SetVertex(a, sv); } // retrieve draw indices piece->SetIndexCount(fp->vertexTableSize); for (int a = 0; a < fp->vertexTableSize; ++a) { const int vertexDrawIdx = swabDWord(*(indexList++)); piece->SetIndex(a, vertexDrawIdx); } // post process the piece { piece->goffset = piece->offset + ((parent != NULL)? parent->goffset: ZeroVector); piece->Trianglize(); piece->SetVertexTangents(); piece->SetMinMaxExtends(); model->mins = float3::min(piece->goffset + piece->mins, model->mins); model->maxs = float3::max(piece->goffset + piece->maxs, model->maxs); piece->SetCollisionVolume(CollisionVolume("box", piece->maxs - piece->mins, (piece->maxs + piece->mins) * 0.5f)); } // load children pieces piece->children.reserve(fp->numchildren); for (int a = 0; a < fp->numchildren; ++a) { int childOffset = swabDWord(*(childList++)); SS3OPiece* childPiece = LoadPiece(model, piece, buf, childOffset); piece->children.push_back(childPiece); } return piece; }
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; }
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; }