// Render one model void PolygonSortModelRenderer::RenderModel(int streamflags, CModel* model, void* data) { CModelDefPtr mdef = model->GetModelDef(); PSModel* psmdl = (PSModel*)data; // Setup per-CModel arrays u8* base = psmdl->m_Array.Bind(); GLsizei stride = (GLsizei)psmdl->m_Array.GetStride(); glVertexPointer(3, GL_FLOAT, stride, base + psmdl->m_Position.offset); if (streamflags & STREAM_COLOR) glColorPointer(3, psmdl->m_Color.type, stride, base + psmdl->m_Color.offset); // render the lot size_t numFaces = mdef->GetNumFaces(); if (!g_Renderer.m_SkipSubmit) { pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdef->GetNumVertices()-1, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, psmdl->m_Indices); } // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; }
// Render one model void ShaderModelRenderer::RenderModel(int streamflags, CModel* model, void* data) { CModelDefPtr mdldef = model->GetModelDef(); ShaderModel* shadermodel = (ShaderModel*)data; u8* base = shadermodel->m_Array.Bind(); GLsizei stride = (GLsizei)shadermodel->m_Array.GetStride(); u8* indexBase = m->shadermodeldef->m_IndexArray.Bind(); if (streamflags & STREAM_POS) glVertexPointer(3, GL_FLOAT, stride, base + shadermodel->m_Position.offset); if (streamflags & STREAM_NORMAL) glNormalPointer(GL_FLOAT, stride, base + shadermodel->m_Normal.offset); if (streamflags & STREAM_UV0) glTexCoordPointer(2, GL_FLOAT, stride, base + shadermodel->m_UV.offset); // render the lot size_t numFaces = mdldef->GetNumFaces(); if (!g_Renderer.m_SkipSubmit) { pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase); } // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; }
// Render one model void InstancingModelRenderer::RenderModel(const CShaderProgramPtr& shader, int UNUSED(streamflags), CModel* model, CModelRData* UNUSED(data)) { CModelDefPtr mdldef = model->GetModelDef(); if (m->gpuSkinning) { // Bind matrices for current animation state. // Add 1 to NumBones because of the special 'root' bone. // HACK: NVIDIA drivers return uniform name with "[0]", Intel Windows drivers without; // try uploading both names since one of them should work, and this is easier than // canonicalising the uniform names in CShaderProgramGLSL shader->Uniform("skinBlendMatrices[0]", mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices()); shader->Uniform("skinBlendMatrices", mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices()); } // render the lot size_t numFaces = mdldef->GetNumFaces(); if (!g_Renderer.m_SkipSubmit) { // Draw with DrawRangeElements where available, since it might be more efficient #if CONFIG2_GLES glDrawElements(GL_TRIANGLES, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase); #else pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase); #endif } // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; }
PSModel::PSModel(CModel* model) : m_Model(model), m_Array(GL_DYNAMIC_DRAW) { CModelDefPtr mdef = m_Model->GetModelDef(); // Positions and normals must be 16-byte aligned for SSE writes. // We can pack the color after the position; it will be corrupted by // BuildPositionAndNormals, but that's okay since we'll recompute the // colors afterwards. m_Color.type = GL_UNSIGNED_BYTE; m_Color.elems = 4; m_Array.AddAttribute(&m_Color); m_Position.type = GL_FLOAT; m_Position.elems = 3; m_Array.AddAttribute(&m_Position); m_Array.SetNumVertices(mdef->GetNumVertices()); m_Array.Layout(); // Verify alignment ENSURE(m_Position.offset % 16 == 0); ENSURE(m_Array.GetStride() % 16 == 0); m_Indices = new u16[mdef->GetNumFaces()*3]; }
// Helper function to copy object-space position and normal vectors into arrays. void ModelRenderer::CopyPositionAndNormals( const CModelDefPtr& mdef, const VertexArrayIterator<CVector3D>& Position, const VertexArrayIterator<CVector3D>& Normal) { size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices = mdef->GetVertices(); for(size_t j = 0; j < numVertices; ++j) { Position[j] = vertices[j].m_Coords; Normal[j] = vertices[j].m_Norm; } }
// Copy UV coordinates void ModelRenderer::BuildUV( const CModelDefPtr& mdef, const VertexArrayIterator<float[2]>& UV, int UVset) { size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices = mdef->GetVertices(); for (size_t j=0; j < numVertices; ++j) { UV[j][0] = vertices[j].m_UVs[UVset * 2]; UV[j][1] = 1.0-vertices[j].m_UVs[UVset * 2 + 1]; } }
// Updated transforms void PolygonSortModelRenderer::UpdateModelData(CModel* model, void* data, int updateflags) { PSModel* psmdl = (PSModel*)data; if (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR)) { CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); // build vertices // allocate working space for computing normals if (numVertices > m->normalsNumVertices) { rtl_FreeAligned(m->normals); size_t newSize = round_up_to_pow2(numVertices); m->normals = (char*)rtl_AllocateAligned(newSize*16, 16); m->normalsNumVertices = newSize; } VertexArrayIterator<CVector3D> Position = psmdl->m_Position.GetIterator<CVector3D>(); VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>(m->normals, 16); ModelRenderer::BuildPositionAndNormals(model, Position, Normal); VertexArrayIterator<SColor4ub> Color = psmdl->m_Color.GetIterator<SColor4ub>(); ModelRenderer::BuildColor4ub(model, Normal, Color); // upload everything to vertex buffer psmdl->m_Array.Upload(); } // resort model indices from back to front, according to the view camera position - and store // the returned sqrd distance to the centre of the nearest triangle // Use the view camera instead of the cull camera because: // a) polygon sorting implicitly uses the view camera (and changing that would be costly) // b) using the cull camera is likely not interesting from a debugging POV PROFILE_START( "sorting transparent" ); CMatrix3D worldToCam; g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam); psmdl->BackToFrontIndexSort(worldToCam); PROFILE_END( "sorting transparent" ); }
PSModelDef::PSModelDef(const CModelDefPtr& mdef) : m_Array(GL_STATIC_DRAW) { m_UV.type = GL_FLOAT; m_UV.elems = 2; m_Array.AddAttribute(&m_UV); m_Array.SetNumVertices(mdef->GetNumVertices()); m_Array.Layout(); VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>(); ModelRenderer::BuildUV(mdef, UVit); m_Array.Upload(); m_Array.FreeBackingStore(); }
// Helper function to transform position and normal vectors into world-space. void ModelRenderer::BuildPositionAndNormals( CModel* model, const VertexArrayIterator<CVector3D>& Position, const VertexArrayIterator<CVector3D>& Normal) { CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices=mdef->GetVertices(); if (model->IsSkinned()) { // boned model - calculate skinned vertex positions/normals // Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in // some broken situations if (numVertices && vertices[0].m_Blend.m_Bone[0] == 0xff) { LOGERROR("Model %s is boned with unboned animation", mdef->GetName().string8()); return; } #if HAVE_SSE if (g_EnableSSE) { CModelDef::SkinPointsAndNormals_SSE(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices()); } else #endif { CModelDef::SkinPointsAndNormals(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices()); } } else { PROFILE( "software transform" ); // just copy regular positions, transform normals to world space const CMatrix3D& transform = model->GetTransform(); const CMatrix3D& invtransform = model->GetInvTransform(); for (size_t j=0; j<numVertices; j++) { transform.Transform(vertices[j].m_Coords,Position[j]); invtransform.RotateTransposed(vertices[j].m_Norm,Normal[j]); } } }
// Render one model void InstancingModelRenderer::RenderModel(CShaderProgramPtr& UNUSED(shader), int UNUSED(streamflags), CModel* model, void* UNUSED(data)) { CModelDefPtr mdldef = model->GetModelDef(); // render the lot size_t numFaces = mdldef->GetNumFaces(); if (!g_Renderer.m_SkipSubmit) { pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase); } // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; }
// Helper function for lighting void ModelRenderer::BuildColor4ub( CModel* model, const VertexArrayIterator<CVector3D>& Normal, const VertexArrayIterator<SColor4ub>& Color) { PROFILE( "lighting vertices" ); CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); CColor shadingColor = model->GetShadingColor(); for (size_t j=0; j<numVertices; j++) { RGBColor tempcolor = lightEnv.EvaluateUnitScaled(Normal[j]); tempcolor.X *= shadingColor.r; tempcolor.Y *= shadingColor.g; tempcolor.Z *= shadingColor.b; Color[j] = ConvertRGBColorTo4ub(tempcolor); } }
// Build model data (and modeldef data if necessary) void* ShaderModelRenderer::CreateModelData(CModel* model) { CModelDefPtr mdef = model->GetModelDef(); ShaderModelDef* shadermodeldef = (ShaderModelDef*)mdef->GetRenderData(m); if (!shadermodeldef) { shadermodeldef = new ShaderModelDef(mdef); mdef->SetRenderData(m, shadermodeldef); } // Build the per-model data ShaderModel* shadermodel = new ShaderModel; shadermodel->m_Position.type = GL_FLOAT; shadermodel->m_Position.elems = 3; shadermodel->m_Array.AddAttribute(&shadermodel->m_Position); shadermodel->m_UV.type = GL_FLOAT; shadermodel->m_UV.elems = 2; shadermodel->m_Array.AddAttribute(&shadermodel->m_UV); shadermodel->m_Normal.type = GL_FLOAT; shadermodel->m_Normal.elems = 3; shadermodel->m_Array.AddAttribute(&shadermodel->m_Normal); shadermodel->m_Array.SetNumVertices(mdef->GetNumVertices()); shadermodel->m_Array.Layout(); // Fill in static UV coordinates VertexArrayIterator<float[2]> UVit = shadermodel->m_UV.GetIterator<float[2]>(); ModelRenderer::BuildUV(mdef, UVit); return shadermodel; }
IModelDef::IModelDef(const CModelDefPtr& mdef) : m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW) { size_t numVertices = mdef->GetNumVertices(); m_Position.type = GL_FLOAT; m_Position.elems = 3; m_Array.AddAttribute(&m_Position); m_Normal.type = GL_FLOAT; m_Normal.elems = 3; m_Array.AddAttribute(&m_Normal); m_UV.type = GL_FLOAT; m_UV.elems = 2; m_Array.AddAttribute(&m_UV); m_Array.SetNumVertices(numVertices); m_Array.Layout(); VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>(); VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>(); VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>(); ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal); ModelRenderer::BuildUV(mdef, UVit); m_Array.Upload(); m_Array.FreeBackingStore(); m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3); m_IndexArray.Layout(); ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator()); m_IndexArray.Upload(); m_IndexArray.FreeBackingStore(); }
IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents) : m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW) { size_t numVertices = mdef->GetNumVertices(); m_Position.type = GL_FLOAT; m_Position.elems = 3; m_Array.AddAttribute(&m_Position); m_Normal.type = GL_FLOAT; m_Normal.elems = 3; m_Array.AddAttribute(&m_Normal); m_UVs.resize(mdef->GetNumUVsPerVertex()); for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++) { m_UVs[i].type = GL_FLOAT; m_UVs[i].elems = 2; m_Array.AddAttribute(&m_UVs[i]); } if (gpuSkinning) { m_BlendJoints.type = GL_UNSIGNED_BYTE; m_BlendJoints.elems = 4; m_Array.AddAttribute(&m_BlendJoints); m_BlendWeights.type = GL_UNSIGNED_BYTE; m_BlendWeights.elems = 4; m_Array.AddAttribute(&m_BlendWeights); } if (calculateTangents) { // Generate tangents for the geometry:- m_Tangent.type = GL_FLOAT; m_Tangent.elems = 4; m_Array.AddAttribute(&m_Tangent); // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning] int numVertexAttrs = 3 + 3 + 4 + 2 * mdef->GetNumUVsPerVertex(); if (gpuSkinning) { numVertexAttrs += 8; } // the tangent generation can increase the number of vertices temporarily // so reserve a bit more memory to avoid reallocations in GenTangents (in most cases) std::vector<float> newVertices; newVertices.reserve(numVertexAttrs * numVertices * 2); // Generate the tangents ModelRenderer::GenTangents(mdef, newVertices, gpuSkinning); // how many vertices do we have after generating tangents? int newNumVert = newVertices.size() / numVertexAttrs; std::vector<int> remapTable(newNumVert); std::vector<float> vertexDataOut(newNumVert * numVertexAttrs); // re-weld the mesh to remove duplicated vertices int numVertices2 = WeldMesh(&remapTable[0], &vertexDataOut[0], &newVertices[0], newNumVert, numVertexAttrs); // Copy the model data to graphics memory:- m_Array.SetNumVertices(numVertices2); m_Array.Layout(); VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>(); VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>(); VertexArrayIterator<CVector4D> Tangent = m_Tangent.GetIterator<CVector4D>(); VertexArrayIterator<u8[4]> BlendJoints; VertexArrayIterator<u8[4]> BlendWeights; if (gpuSkinning) { BlendJoints = m_BlendJoints.GetIterator<u8[4]>(); BlendWeights = m_BlendWeights.GetIterator<u8[4]>(); } // copy everything into the vertex array for (int i = 0; i < numVertices2; i++) { int q = numVertexAttrs * i; Position[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]); q += 3; Normal[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]); q += 3; Tangent[i] = CVector4D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2], vertexDataOut[q + 3]); q += 4; if (gpuSkinning) { for (size_t j = 0; j < 4; ++j) { BlendJoints[i][j] = (u8)vertexDataOut[q + 0 + 2 * j]; BlendWeights[i][j] = (u8)vertexDataOut[q + 1 + 2 * j]; } q += 8; } for (size_t j = 0; j < mdef->GetNumUVsPerVertex(); j++) { VertexArrayIterator<float[2]> UVit = m_UVs[j].GetIterator<float[2]>(); UVit[i][0] = vertexDataOut[q + 0 + 2 * j]; UVit[i][1] = vertexDataOut[q + 1 + 2 * j]; } } // upload vertex data m_Array.Upload(); m_Array.FreeBackingStore(); m_IndexArray.SetNumVertices(mdef->GetNumFaces() * 3); m_IndexArray.Layout(); VertexArrayIterator<u16> Indices = m_IndexArray.GetIterator(); size_t idxidx = 0; // reindex geometry and upload index for (size_t j = 0; j < mdef->GetNumFaces(); ++j) { Indices[idxidx++] = remapTable[j * 3 + 0]; Indices[idxidx++] = remapTable[j * 3 + 1]; Indices[idxidx++] = remapTable[j * 3 + 2]; } m_IndexArray.Upload(); m_IndexArray.FreeBackingStore(); } else { // Upload model without calculating tangents:- m_Array.SetNumVertices(numVertices); m_Array.Layout(); VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>(); VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>(); ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal); for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++) { VertexArrayIterator<float[2]> UVit = m_UVs[i].GetIterator<float[2]>(); ModelRenderer::BuildUV(mdef, UVit, i); } if (gpuSkinning) { VertexArrayIterator<u8[4]> BlendJoints = m_BlendJoints.GetIterator<u8[4]>(); VertexArrayIterator<u8[4]> BlendWeights = m_BlendWeights.GetIterator<u8[4]>(); for (size_t i = 0; i < numVertices; ++i) { const SModelVertex& vtx = mdef->GetVertices()[i]; for (size_t j = 0; j < 4; ++j) { BlendJoints[i][j] = vtx.m_Blend.m_Bone[j]; BlendWeights[i][j] = (u8)(255.f * vtx.m_Blend.m_Weight[j]); } } } m_Array.Upload(); m_Array.FreeBackingStore(); m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3); m_IndexArray.Layout(); ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator()); m_IndexArray.Upload(); m_IndexArray.FreeBackingStore(); } }