Submesh* CmodLoader::loadSubmesh() { VertexSpec* vertexSpec = loadVertexSpec(); if (!vertexSpec) { return NULL; } VertexArray* vertexArray = loadVertexArray(*vertexSpec); if (!vertexArray) { delete vertexSpec; return NULL; } Submesh* submesh = new Submesh(vertexArray); bool done = false; while (!done && !error()) { quint16 token = 0; *m_inputStream >> token; if (token == CmodEndMesh) { done = true; } else { unsigned int materialIndex = 0; PrimitiveBatch* primitives = loadPrimitiveBatch(token, vertexArray->count(), &materialIndex); if (primitives) { submesh->addPrimitiveBatch(primitives, materialIndex); } } } if (error()) { delete submesh; submesh = NULL; } return submesh; }
/** Merge a list of submeshes to create a single submesh. All submeshes must share * the same vertex spec. There must be at least one submesh in the list to merge. * * \return the new submesh, or null if there was an error creating the submesh. */ Submesh* Submesh::mergeSubmeshes(const std::vector<Submesh*>& submeshes) { if (submeshes.empty()) { return NULL; } const VertexSpec& vertexSpec = submeshes.front()->vertices()->vertexSpec(); const unsigned int vertexStride = submeshes.front()->vertices()->stride(); // Verify that the strides and vertex specs of all submeshes match unsigned int vertexCount = 0; for (vector<Submesh*>::const_iterator iter = submeshes.begin(); iter != submeshes.end(); ++iter) { Submesh* s = *iter; if (s->vertices()->vertexSpec() != vertexSpec || s->vertices()->stride() != vertexStride) { VESTA_WARNING("MergeSubmeshes attempted on incompatible submeshes."); return false; } vertexCount += s->vertices()->count(); } // Create a new vertex array large enough to contain all of the submeshes unsigned int vertexDataSize = vertexCount * vertexStride; Submesh* submesh = NULL; char* vertexData = NULL; VertexArray* vertexArray = NULL; try { vertexData = new char[vertexDataSize]; vertexArray = new VertexArray(vertexData, vertexCount, vertexSpec, vertexStride); submesh = new Submesh(vertexArray); } catch (bad_alloc&) { VESTA_WARNING("Out of memory during submesh merge."); if (vertexArray) { // Deleting the vertex array takes care of the vertexData too delete vertexArray; } else if (vertexData) { delete[] vertexData; } return NULL; } unsigned int vertexDataOffset = 0; // Copy vertices from the submeshes in the merge list to the new vertex array for (vector<Submesh*>::const_iterator iter = submeshes.begin(); iter != submeshes.end(); ++iter) { Submesh* s = *iter; unsigned int submeshVertexDataSize = vertexStride * s->vertices()->count(); assert(vertexDataOffset + submeshVertexDataSize <= vertexDataSize); copy(reinterpret_cast<const char*>(s->vertices()->data()), reinterpret_cast<const char*>(s->vertices()->data()) + submeshVertexDataSize, vertexData + vertexDataOffset); vertexDataOffset += submeshVertexDataSize; } // Copy materials and primitive batches from submeshes in the merge list try { unsigned int vertexOffset = 0; for (vector<Submesh*>::const_iterator iter = submeshes.begin(); iter != submeshes.end() && submesh != NULL; ++iter) { Submesh* s = *iter; assert(s->materials().size() == s->primitiveBatches().size()); for (unsigned int i = 0; i < s->primitiveBatchCount(); ++i) { const PrimitiveBatch* prims = s->primitiveBatches().at(i); PrimitiveBatch* newPrims = new PrimitiveBatch(*prims); if (vertexOffset != 0) { if (!newPrims->offsetIndices(vertexOffset)) { delete submesh; submesh = NULL; break; } } submesh->addPrimitiveBatch(newPrims, s->materials().at(i)); } vertexOffset += s->vertices()->count(); } } catch (bad_alloc&) { VESTA_WARNING("Out of memory during submesh merge."); delete submesh; submesh = NULL; } return submesh; }
static MeshGeometry* Convert3DSMesh(Lib3dsFile* meshfile, TextureMapLoader* textureLoader) { MeshGeometry* meshGeometry = new MeshGeometry(); for (int materialIndex = 0; materialIndex < meshfile->nmaterials; ++materialIndex) { Lib3dsMaterial* material = meshfile->materials[materialIndex]; Material* vmaterial = new Material(); // Convert a 3ds material to VESTA material vmaterial->setOpacity(1.0f - material->transparency); vmaterial->setDiffuse(Spectrum(material->diffuse)); if (material->shininess != 0.0f) { vmaterial->setSpecular(Spectrum(material->specular)); vmaterial->setPhongExponent(std::pow(2.0f, 1.0f + 10.0f * material->shininess)); } if (material->self_illum_flag) { vmaterial->setEmission(vmaterial->diffuse() * material->self_illum); } const string baseTextureName(material->texture1_map.name); if (!baseTextureName.empty()) { TextureProperties texProperties; if ((material->texture1_map.flags & LIB3DS_TEXTURE_NO_TILE) != 0) { texProperties.addressS = TextureProperties::Clamp; texProperties.addressT = TextureProperties::Clamp; } if (textureLoader) { TextureMap* baseTexture = textureLoader->loadTexture(baseTextureName, texProperties); vmaterial->setBaseTexture(baseTexture); } } meshGeometry->addMaterial(vmaterial); } for (int meshIndex = 0; meshIndex < meshfile->nmeshes; ++meshIndex) { Lib3dsMesh* mesh = meshfile->meshes[meshIndex]; if (mesh->nfaces > 0) { bool hasTextureCoords = mesh->texcos != 0; // Generate normals for the mesh float* normals = new float[mesh->nfaces * 9]; lib3ds_mesh_calculate_vertex_normals(mesh, (float(*)[3]) normals); VertexPool vertexPool; for (int faceIndex = 0; faceIndex < mesh->nfaces; ++faceIndex) { for (int i = 0; i < 3; i++) { int vertexIndex = mesh->faces[faceIndex].index[i]; vertexPool.addVec3(mesh->vertices[vertexIndex]); vertexPool.addVec3(&normals[(faceIndex * 3 + i) * 3]); if (hasTextureCoords) { // Invert the v texture coordinate, since 3ds uses a texture // coordinate system that is flipped with respect to OpenGL's vertexPool.addVec2(mesh->texcos[vertexIndex][0], 1.0f - mesh->texcos[vertexIndex][1]); } } } delete[] normals; const VertexSpec* vertexSpec = hasTextureCoords ? &VertexSpec::PositionNormalTex : &VertexSpec::PositionNormal; VertexArray* vertexArray = vertexPool.createVertexArray(mesh->nfaces * 3, *vertexSpec); PrimitiveBatch* batch = new PrimitiveBatch(PrimitiveBatch::Triangles, mesh->nfaces); // Get the material for the primitive batch // TODO: This assumes that a single material is applied to the whole mesh; however, // materials can be assigned per-face (but rarely are in most 3ds files.) unsigned int materialIndex = Submesh::DefaultMaterialIndex; if (mesh->nfaces > 0) { // Use the material of the first face materialIndex = mesh->faces[0].material; } Submesh* submesh = new Submesh(vertexArray); submesh->addPrimitiveBatch(batch, materialIndex); meshGeometry->addSubmesh(submesh); } } return meshGeometry; }