void Renderer::Cull( XNA::Frustum* frustum, Transform* pTransform ) { if (m_Mesh) { XNA::OrientedBox objectBox; for( int i=0; i<m_Mesh->GetNumberOfSubmeshes(); i++ ) { Submesh* submesh = m_Mesh->GetSubmesh( i ); XMVECTOR extents = XMLoadFloat3( &submesh->GetGeometryChunk()->GetAABB()->Extents ); XMVECTOR scale = XMLoadFloat3( &pTransform->GetScale().intoXMFLOAT3() ); XMVECTOR offset = XMLoadFloat3( &submesh->GetGeometryChunk()->GetAABB()->Center )*scale; XMVECTOR position = XMVector3Rotate( offset, XMLoadFloat4(&pTransform->GetOrientation().intoXMFLOAT4()) ); position += XMLoadFloat3( &pTransform->GetPosition().intoXMFLOAT3() ); XMStoreFloat3( &objectBox.Center, position ); XMStoreFloat3( &objectBox.Extents, extents*scale ); objectBox.Orientation = pTransform->GetOrientation().intoXMFLOAT4(); if( XNA::IntersectOrientedBoxFrustum( &objectBox, frustum ) > 0 ) m_SubmeshRenderData[i].bVisible = true; else m_SubmeshRenderData[i].bVisible = false; } } }
void Renderer::CullLight( SpotLight* light, Transform* pTransform ) { if (m_Mesh) { XNA::OrientedBox objectBox; XNA::Frustum lightFrustum = light->GetFrustum(); for( int i=0; i<m_Mesh->GetNumberOfSubmeshes(); i++ ) { if (m_SubmeshRenderData[i].bVisible) { Submesh* submesh = m_Mesh->GetSubmesh( i ); XMVECTOR extents = XMLoadFloat3( &submesh->GetGeometryChunk()->GetAABB()->Extents ); XMVECTOR scale = XMLoadFloat3( &pTransform->GetScale().intoXMFLOAT3() ); XMVECTOR offset = XMLoadFloat3( &submesh->GetGeometryChunk()->GetAABB()->Center )*scale; XMVECTOR position = XMVector3Rotate( offset, XMLoadFloat4(&pTransform->GetOrientation().intoXMFLOAT4()) ); position += XMLoadFloat3( &pTransform->GetPosition().intoXMFLOAT3() ); XMStoreFloat3( &objectBox.Center, position ); XMStoreFloat3( &objectBox.Extents, extents*scale ); objectBox.Orientation = pTransform->GetOrientation().intoXMFLOAT4(); if( XNA::IntersectOrientedBoxFrustum( &objectBox, &lightFrustum ) > 0 ) m_SubmeshRenderData[i].AffectingSpotLights.push_back( light ); } } } }
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; }
bool BinaryWriter< Submesh >::doWrite( Submesh const & obj ) { bool result = true; if ( result ) { VertexBuffer const & buffer = obj.getVertexBuffer(); size_t size = buffer.getSize(); uint32_t stride = buffer.getDeclaration().stride(); auto count = uint32_t( size / stride ); result = doWriteChunk( count, ChunkType::eSubmeshVertexCount, m_chunk ); if ( result ) { auto const * srcbuf = reinterpret_cast< InterleavedVertex const * >( buffer.getData() ); std::vector< InterleavedVertexT< double > > dstbuf( count ); doCopyVertices( count, srcbuf, dstbuf.data() ); result = doWriteChunk( dstbuf, ChunkType::eSubmeshVertex, m_chunk ); } } if ( result ) { IndexBuffer const & buffer = obj.getIndexBuffer(); uint32_t count = buffer.getSize() / 3; result = doWriteChunk( count, ChunkType::eSubmeshFaceCount, m_chunk ); if ( result ) { auto const * srcbuf = reinterpret_cast< FaceIndices const * >( buffer.getData() ); result = doWriteChunk( srcbuf, buffer.getSize() / 3, ChunkType::eSubmeshFaces, m_chunk ); } } if ( result ) { auto it = obj.m_components.find( BonesComponent::Name ); if ( it != obj.m_components.end() ) { BinaryWriter< BonesComponent >{}.write( *std::static_pointer_cast< BonesComponent >( it->second ), m_chunk ); } } return result; }
// Create buffers required for drawing the mesh on hardware. This must be called // before rendering whenever the mesh has changed. // // return true if all hardware buffers could be created. bool MeshGeometry::realize() const { // Mark hardware buffers as up-to-date. Even if vertex buffer allocation fails, // we don't want to keep retrying every time a frame is rendered. m_hwBuffersCurrent = true; // Only create vertex buffers for submeshes for which the size of the vertex // data exceeds the limit below. const unsigned int VertexBufferSizeThreshold = 4096; // Don't create anything if hardware/driver doesn't support vertex buffer objects, // but report success anyhow. if (!GLVertexBuffer::supported()) { return true; } bool ok = true; freeSubmeshBuffers(); for (vector< counted_ptr<Submesh> >::const_iterator iter = m_submeshes.begin(); iter != m_submeshes.end(); ++iter) { Submesh* submesh = iter->ptr(); GLVertexBuffer* vertexBuffer = NULL; unsigned int size = submesh->vertices()->stride() * submesh->vertices()->count(); if (size > VertexBufferSizeThreshold) { vertexBuffer = new GLVertexBuffer(size, GL_STATIC_DRAW_ARB, submesh->vertices()->data()); if (!vertexBuffer->isValid()) { delete vertexBuffer; ok = false; } } // A null vertex pointer is legal and indicates that the vertex data is // stored in system memory instead of graphics memory. m_submeshBuffers.push_back(vertexBuffer); } return ok; }
void Geometry::setMaterial( Submesh & submesh , MaterialSPtr material , bool updateSubmesh ) { MeshSPtr mesh = getMesh(); if ( mesh ) { auto it = std::find_if( mesh->begin() , mesh->end() , [&submesh]( SubmeshSPtr lookup ) { return lookup.get() == &submesh; } ); REQUIRE( it != mesh->end() ); bool changed = false; MaterialSPtr oldMaterial; auto itSubMat = m_submeshesMaterials.find( &submesh ); if ( itSubMat != m_submeshesMaterials.end() ) { MaterialSPtr oldMaterial = itSubMat->second.lock(); if ( oldMaterial != material ) { itSubMat->second = material; changed = true; } } else if ( material ) { m_submeshesMaterials.emplace( &submesh, material ); changed = true; } if ( changed ) { submesh.setMaterial( oldMaterial, material, updateSubmesh ); if ( material->hasEnvironmentMapping() ) { getScene()->createEnvironmentMap( *getParent() ); } } } else { CASTOR_EXCEPTION( "No mesh" ); } }
RenderingMesh* COLRenderWidget::createRenderingMesh(Mesh* mesh) { Matrix4 fModelMat = Matrix4::Identity; MeshFrame* frame = mesh->getFrame(); if (frame) { fModelMat = fModelMat * frame->getAbsoluteModelMatrix(); } RenderingPrimitiveFormat rpf; switch (mesh->getVertexFormat()) { case VertexFormatPoints: rpf = RenderingPrimitivePoints; break; case VertexFormatLines: rpf = RenderingPrimitiveLines; break; case VertexFormatTriangles: rpf = RenderingPrimitiveTriangles; break; case VertexFormatTriangleStrips: rpf = RenderingPrimitiveTriangleStrip; break; } //uint32_t flags = RenderingMesh::EnableShaderPluginUniformBuffers | RenderingMesh::HasTransparency; uint32_t flags = RenderingMesh::EnableShaderPluginUniformBuffers; DefaultRenderingMesh* rm = new DefaultRenderingMesh ( rpf, flags, mesh->getVertexCount(), 0, NULL, 0, mesh->getDataBuffer(), mesh->getIndexBuffer(), mesh->getVertexOffset(), mesh->getVertexStride(), mesh->getSubmeshIDOffset(), mesh->getSubmeshIDStride(), mesh->getNormalOffset(), mesh->getNormalStride(), mesh->getTexCoordOffset(), mesh->getTexCoordStride(), mesh->getVertexColorOffset(), mesh->getVertexColorStride(), -1, 0, -1, 0 ); rm->setModelMatrix(fModelMat); for (Mesh::SubmeshIterator sit = mesh->getSubmeshBegin() ; sit != mesh->getSubmeshEnd() ; sit++) { Submesh* submesh = *sit; Material* mat = submesh->getMaterial(); GLuint oglTex = 0; uint8_t r = 255; uint8_t g = 255; uint8_t b = 255; uint8_t a = 255; if (mat) { mat->getColor(r, g, b, a); } RenderingSubmesh* sm = new RenderingSubmesh(rm, submesh->getIndexCount(), submesh->getIndexOffset(), oglTex); sm->setMaterialColor(r, g, b, a); } return rm; }
/** 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; }
void Mesh::link() { if (isLinked) return; // Build the data buffer size_t numSubmeshes = submeshes.size(); glBindBuffer(GL_ARRAY_BUFFER, dataBuffer); char* bufData = (char*) glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); char* newData = new char[dataBufferSingleSize * numSubmeshes]; for (uint8_t i = 0 ; i < numSubmeshes ; i++) { memcpy(newData + i*dataBufferSingleSize, bufData, dataBufferSingleSize); if (submeshIDOffs != -1) { for (size_t j = 0 ; j < vertexCount ; j++) { newData[i*dataBufferSingleSize + submeshIDOffs + j*submeshIDStride] = i; } } } glUnmapBuffer(GL_ARRAY_BUFFER); glBufferData(GL_ARRAY_BUFFER, dataBufferSingleSize * numSubmeshes, newData, GL_STATIC_DRAW); delete[] newData; // Build the index buffer if (indexBuffer == 0) glGenBuffers(1, &indexBuffer); bool hasCompiledSubmeshes = false; //GLuint bufSize = 0; size_t numIndices = 0; for (SubmeshIterator it = submeshes.begin() ; it != submeshes.end() ; it++) { Submesh* submesh = *it; if (submesh->isLinked()) hasCompiledSubmeshes = true; numIndices += submesh->getIndexCount(); } uint32_t* newIndices = new uint32_t[numIndices]; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); uint32_t* oldIndices; if (hasCompiledSubmeshes) oldIndices = (uint32_t*) glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); size_t offset = 0; for (SubmeshIterator it = submeshes.begin() ; it != submeshes.end() ; it++) { Submesh* submesh = *it; int ic = submesh->getIndexCount(); if (submesh->isLinked()) { memcpy(newIndices + offset, oldIndices + submesh->getIndexOffset(), ic*4); } else { memcpy(newIndices + offset, submesh->indices, ic*4); } submesh->setLinked(offset); offset += ic; } if (hasCompiledSubmeshes) glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices*4, newIndices, GL_STATIC_DRAW); delete[] newIndices; isLinked = true; }
/** *\~english *\return The submesh buffer matching given submesh. *\~french *\return Le tampon de sous-maillage correspondant au sous-maillage donné. */ inline SubmeshAnimationBufferMap::const_iterator find( Submesh const & submesh )const { return m_submeshesBuffers.find( submesh.getId() ); }
bool BinaryParser< Submesh >::doParse( Submesh & obj ) { bool result = true; String name; std::vector< FaceIndices > faces; std::vector< VertexBoneData > bones; std::vector< InterleavedVertexT< double > > srcbuf; uint32_t count{ 0u }; uint32_t faceCount{ 0u }; uint32_t boneCount{ 0u }; BinaryChunk chunk; std::shared_ptr< BonesComponent > bonesComponent; while ( result && doGetSubChunk( chunk ) ) { switch ( chunk.getChunkType() ) { case ChunkType::eSubmeshVertexCount: result = doParseChunk( count, chunk ); if ( result ) { srcbuf.resize( count ); } break; case ChunkType::eSubmeshVertex: result = doParseChunk( srcbuf, chunk ); if ( result && !srcbuf.empty() ) { std::vector< InterleavedVertex > dstbuf( srcbuf.size() ); doCopyVertices( uint32_t( srcbuf.size() ), srcbuf.data(), dstbuf.data() ); obj.addPoints( dstbuf ); } break; case ChunkType::eSubmeshBoneCount: if ( !bonesComponent ) { bonesComponent = std::make_shared< BonesComponent >( obj ); obj.addComponent( bonesComponent ); } result = doParseChunk( count, chunk ); if ( result ) { boneCount = count; bones.resize( count ); } break; case ChunkType::eSubmeshBones: result = doParseChunk( bones, chunk ); if ( result && boneCount > 0 ) { bonesComponent->addBoneDatas( bones ); } boneCount = 0u; break; case ChunkType::eBonesComponent: bonesComponent = std::make_shared< BonesComponent >( obj ); result = BinaryParser< BonesComponent >{}.parse( *bonesComponent, chunk ); if ( result ) { obj.addComponent( bonesComponent ); } break; case ChunkType::eSubmeshFaceCount: result = doParseChunk( count, chunk ); if ( result ) { faceCount = count; faces.resize( count ); } break; case ChunkType::eSubmeshFaces: result = doParseChunk( faces, chunk ); if ( result && faceCount > 0 ) { auto indexMapping = std::make_shared< TriFaceMapping >( obj ); indexMapping->addFaceGroup( faces ); obj.setIndexMapping( indexMapping ); } faceCount = 0u; break; default: result = false; break; } } return result; }
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; }
/** Optimize the mesh by merging submeshes that share the same vertex spec. * This reduces the number of separate vertex buffers required. */ bool MeshGeometry::mergeSubmeshes() { if (m_submeshes.size() <= 1) { return true; } // At the beginning, all submeshes are unmerged, none are merged vector<counted_ptr<Submesh> > merged; vector<Submesh*> unmerged; for (vector<counted_ptr<Submesh> >::const_iterator iter = m_submeshes.begin(); iter != m_submeshes.end(); ++iter) { unmerged.push_back(iter->ptr()); } // Iterate over submeshes repeatedly, removing groups that are merged. while (!unmerged.empty()) { const VertexArray* vertexArray = unmerged.front()->vertices(); vector<Submesh*> matches; vector<Submesh*> nonmatches; for (vector<Submesh*>::const_iterator iter = unmerged.begin(); iter != unmerged.end(); ++iter) { Submesh* s = *iter; if (vertexArray->stride() == s->vertices()->stride() && vertexArray->vertexSpec() == s->vertices()->vertexSpec()) { matches.push_back(s); } else { nonmatches.push_back(s); } } if (matches.size() == 1) { // Avoid the expense of merging when there's just a single // mesh. merged.push_back(counted_ptr<Submesh>(matches.front())); } else { Submesh* mergedSubmesh = Submesh::mergeSubmeshes(matches); if (!mergedSubmesh) { return false; } merged.push_back(counted_ptr<Submesh>(mergedSubmesh)); } // Set submeshes to the list of unmerged submeshes unmerged = nonmatches; } VESTA_LOG("Merged %d submeshes into %d", m_submeshes.size(), merged.size()); m_submeshes = merged; setMeshChanged(); return true; }