#include "precompiled.h" #include #include "MathUtil.h" #include "lib/ogl.h" #include "lib/res/graphics/ogl_tex.h" #include "lib/res/graphics/ogl_shader.h" #include "Renderer.h" #include "TransparencyRenderer.h" #include "PlayerRenderer.h" #include "ModelRData.h" #include "Model.h" #include "ModelDef.h" #include "MaterialManager.h" #include "Profile.h" #include "renderer/ModelDefRData.h" #include "renderer/RenderPathVertexShader.h" /////////////////////////////////////////////////////////////////// // CModelRData constructor CModelRData::CModelRData(CModel* model) : m_Model(model), m_TempNormals(0), m_DynamicArray(true), m_Indices(0), m_Flags(0) { debug_assert(model); // build all data now Build(); } /////////////////////////////////////////////////////////////////// // CModelRData destructor CModelRData::~CModelRData() { // clean up system copies of data delete[] m_Indices; delete[] m_TempNormals; } void CModelRData::Build() { CModelDefPtr mdef = m_Model->GetModelDef(); if (!mdef->GetRenderData()) { mdef->SetRenderData(new CModelDefRData(&*mdef)); } m_Position.type = GL_FLOAT; m_Position.elems = 3; m_DynamicArray.AddAttribute(&m_Position); if (g_Renderer.GetRenderPath() == CRenderer::RP_VERTEXSHADER) { m_UV.type = GL_FLOAT; m_UV.elems = 2; m_DynamicArray.AddAttribute(&m_UV); m_Normal.type = GL_FLOAT; m_Normal.elems = 3; m_DynamicArray.AddAttribute(&m_Normal); } else { m_Color.type = GL_UNSIGNED_BYTE; m_Color.elems = 3; m_DynamicArray.AddAttribute(&m_Color); } m_DynamicArray.SetNumVertices(mdef->GetNumVertices()); m_DynamicArray.Layout(); // build data BuildStaticVertices(); BuildVertices(); BuildIndices(); // force a texture load on model's texture g_Renderer.LoadTexture(m_Model->GetTexture(),GL_CLAMP_TO_EDGE); // setup model render flags /*if (g_Renderer.IsTextureTransparent(m_Model->GetTexture())) { m_Flags|=MODELRDATA_FLAG_TRANSPARENT; }*/ if(m_Model->GetMaterial().IsPlayer()) { m_Flags |= MODELRDATA_FLAG_PLAYERCOLOR; } else if(m_Model->GetMaterial().UsesAlpha()) { m_Flags |= MODELRDATA_FLAG_TRANSPARENT; } } void CModelRData::BuildIndices() { CModelDefPtr mdef=m_Model->GetModelDef(); debug_assert(mdef); // allocate indices if we haven't got any already if (!m_Indices) { m_Indices=new u16[mdef->GetNumFaces()*3]; } // build indices u32 indices=0; SModelFace* faces=mdef->GetFaces(); for (size_t j=0; jGetNumFaces(); j++) { SModelFace& face=faces[j]; m_Indices[indices++]=face.m_Verts[0]; m_Indices[indices++]=face.m_Verts[1]; m_Indices[indices++]=face.m_Verts[2]; } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // SkinPoint: skin the vertex position using it's blend data and given bone matrices static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVector3D& result) { CVector3D tmp; const SVertexBlend& blend=vertex.m_Blend; // must have at least one valid bone if we're using SkinPoint debug_assert(blend.m_Bone[0]!=0xff); const CMatrix3D& m=matrices[blend.m_Bone[0]]; m.Transform(vertex.m_Coords,result); result*=blend.m_Weight[0]; for (u32 i=1; iGetModelDef(); size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices = mdef->GetVertices(); VertexArrayIterator UVit = m_UV.GetIterator(); for (uint j=0; j < numVertices; ++j, ++UVit) { (*UVit)[0] = vertices[j].m_U; (*UVit)[1] = 1.0-vertices[j].m_V; } } } void CModelRData::BuildVertices() { CModelDefPtr mdef=m_Model->GetModelDef(); size_t numVertices=mdef->GetNumVertices(); SModelVertex* vertices=mdef->GetVertices(); // build vertices VertexArrayIterator Position = m_Position.GetIterator(); VertexArrayIterator Normal; if (m_Normal.type) { Normal = m_Normal.GetIterator(); } else { if (!m_TempNormals) m_TempNormals = new CVector3D[numVertices]; Normal = VertexArrayIterator((char*)m_TempNormals, sizeof(CVector3D)); } const CMatrix3D* bonematrices=m_Model->GetBoneMatrices(); if (bonematrices) { // boned model - calculate skinned vertex positions/normals PROFILE( "skinning bones" ); const CMatrix3D* invbonematrices=m_Model->GetInvBoneMatrices(); for (size_t j=0; jGetTransform(); const CMatrix3D& invtransform=m_Model->GetInvTransform(); for (uint j=0; j Color = m_Color.GetIterator(); CSHCoeffs& shcoeffs = g_Renderer.m_SHCoeffsUnits; CColor sc = m_Model->GetShadingColor(); RGBColor shadingcolor(sc.r, sc.g, sc.b); RGBColor tempcolor; for (uint j=0; jm_ModelLight); idx = g_Renderer.m_VertexShader->m_ModelLight_SHCoefficients; glUniform3fvARB(idx, 9, (float*)coeffs); glEnableClientState(GL_NORMAL_ARRAY); } else { glEnableClientState(GL_COLOR_ARRAY); } } } // reset state prepared by SetupRender void CModelRData::FinishRender(u32 streamflags) { glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (streamflags & STREAM_COLOR) { if (g_Renderer.GetRenderPath() == CRenderer::RP_VERTEXSHADER) { glUseProgramObjectARB(0); glDisableClientState(GL_NORMAL_ARRAY); } else { glDisableClientState(GL_COLOR_ARRAY); } } } // Render one indiviual model. // Try to use RenderModels instead wherever possible. // Must be bracketed by calls to CModelRData::SetupRender/FinishRender void CModelRData::RenderStreams(u32 streamflags, int tmus) { CModelDefPtr mdldef=m_Model->GetModelDef(); if (streamflags & STREAM_UV0) { if (tmus > 1) { for(int i = 1; i < tmus; ++i) g_Renderer.SetTexture(i, m_Model->GetTexture()); } g_Renderer.SetTexture(0, m_Model->GetTexture()); } u8* base = m_DynamicArray.Bind(); GLsizei stride = (GLsizei)m_DynamicArray.GetStride(); glVertexPointer(3, GL_FLOAT, stride, base + m_Position.offset); if (streamflags & STREAM_COLOR) { if (m_Normal.type) { CColor sc = m_Model->GetShadingColor(); glColor3f(sc.r, sc.g, sc.b); glNormalPointer(GL_FLOAT, stride, base + m_Normal.offset); } else glColorPointer(3, m_Color.type, stride, base + m_Color.offset); } if (streamflags & STREAM_UV0) { if (m_UV.type) glTexCoordPointer(2, GL_FLOAT, stride, base + m_UV.offset); else ((CModelDefRData*)mdldef->GetRenderData())->PrepareStream(streamflags); } // render the lot size_t numFaces=mdldef->GetNumFaces(); glDrawRangeElementsEXT(GL_TRIANGLES,0,mdldef->GetNumVertices(),numFaces*3,GL_UNSIGNED_SHORT,m_Indices); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris+=numFaces; } void CModelRData::Update() { if (m_UpdateFlags!=0) { // renderdata changed : rebuild necessary portions if (m_UpdateFlags & RENDERDATA_UPDATE_VERTICES) { BuildVertices(); } if (m_UpdateFlags & RENDERDATA_UPDATE_INDICES) { BuildIndices(); } m_UpdateFlags=0; } } typedef std::pair IntFloatPair; static std::vector IndexSorter; struct SortFacesByDist { bool operator()(const IntFloatPair& lhs,const IntFloatPair& rhs) { return lhs.second>rhs.second ? true : false; } }; float CModelRData::BackToFrontIndexSort(CMatrix3D& objToCam) { float mindist=1.0e30f; CVector3D osvtx,csvtx; CModelDefPtr mdldef=m_Model->GetModelDef(); SModelVertex* vtxs=mdldef->GetVertices(); size_t numFaces=mdldef->GetNumFaces(); SModelFace* faces=mdldef->GetFaces(); IndexSorter.reserve(numFaces); SModelFace* facePtr=faces; u32 i; for (i=0;im_Verts[0]].m_Coords; osvtx+=vtxs[facePtr->m_Verts[1]].m_Coords; osvtx+=vtxs[facePtr->m_Verts[2]].m_Coords; osvtx*=1.0f/3.0f; csvtx=objToCam.Transform(osvtx); float distsqrd=SQR(csvtx.X)+SQR(csvtx.Y)+SQR(csvtx.Z); if (distsqrdm_SubmissionNext) { mdefdata->PrepareStream(streamflags); for(uint idx = 0; idx < mdefdata->m_SubmissionSlots; ++idx) { CModelRData* modeldata = mdefdata->m_SubmissionModels[idx]; if (streamflags & STREAM_UV0) g_Renderer.SetTexture(0, modeldata->GetModel()->GetTexture()); for(; modeldata; modeldata = modeldata->m_SubmissionNext) { if (flags && !(modeldata->GetModel()->GetFlags()&flags)) continue; CModelDefPtr mdldef = modeldata->GetModel()->GetModelDef(); u8* base = modeldata->m_DynamicArray.Bind(); GLsizei stride = (GLsizei)modeldata->m_DynamicArray.GetStride(); glVertexPointer(3, GL_FLOAT, stride, base + modeldata->m_Position.offset); if (streamflags & STREAM_COLOR) { if (modeldata->m_Normal.type) { CColor sc = modeldata->GetModel()->GetShadingColor(); glColor3f(sc.r, sc.g, sc.b); glNormalPointer(GL_FLOAT, stride, base + modeldata->m_Normal.offset); } else { glColorPointer(3, modeldata->m_Color.type, stride, base + modeldata->m_Color.offset); } } if (streamflags & STREAM_UV0 && modeldata->m_UV.type) { glTexCoordPointer(2, GL_FLOAT, stride, base + modeldata->m_UV.offset); } // render the lot size_t numFaces=mdldef->GetNumFaces(); glDrawRangeElementsEXT(GL_TRIANGLES, 0, mdldef->GetNumVertices(), numFaces*3, GL_UNSIGNED_SHORT, modeldata->m_Indices); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris+=numFaces; } } } } ///////////////////////////////////////////////////////////////////////////////////////////// // Submit: submit a model to render this frame void CModelRData::Submit(CModel* model) { CModelRData* data=(CModelRData*) model->GetRenderData(); if (data==0) { // no renderdata for model, create it now PROFILE( "create render data" ); data=new CModelRData(model); model->SetRenderData(data); } else { PROFILE( "update render data" ); data->Update(); } if (data->GetFlags() & MODELRDATA_FLAG_TRANSPARENT) { // add this mode to the transparency renderer for later processing - calculate // transform matrix g_TransparencyRenderer.Add(model); } else if (data->GetFlags() & MODELRDATA_FLAG_PLAYERCOLOR) { // add this model to the player renderer g_PlayerRenderer.Add(model); } else { CModelDefPtr mdldef = model->GetModelDef(); CModelDefRData* mdefdata = (CModelDefRData*)mdldef->GetRenderData(); debug_assert(mdefdata != 0); mdefdata->Submit(data); } } ///////////////////////////////////////////////////////////////////////////////////////////// // ClearSubmissions: Clear the submissions list // TODO: This is asymmetrical: It only clears CModelRData lists, but no player/transparency renderer lists void CModelRData::ClearSubmissions() { for(CModelDefRData* mdefdata = CModelDefRData::m_Submissions; mdefdata; mdefdata = mdefdata->m_SubmissionNext) { mdefdata->ClearSubmissions(); } CModelDefRData::m_Submissions = 0; }