void CMeshViewer::Draw3D(float TimeDelta) { guard(CMeshViewer::Draw3D); assert(Inst); if (GShowDebugInfo) { // draw axis BindDefaultMaterial(true); glBegin(GL_LINES); for (int i = 0; i < 3; i++) { CVec3 tmp = nullVec3; tmp[i] = 1; glColor3fv(tmp.v); tmp[i] = 70; glVertex3fv(tmp.v); glVertex3fv(nullVec3.v); } glEnd(); glColor3f(1, 1, 1); } // draw mesh glPolygonMode(GL_FRONT_AND_BACK, Wireframe ? GL_LINE : GL_FILL); //?? bWireframe is inside Inst, but used here only ? DrawMesh(Inst); unguard; }
void CMeshViewer::DisplayUV(const CMeshVertex* Verts, int VertexSize, const CBaseMeshLod* Mesh, int UVIndex) { guard(CMeshViewer::DisplayUV); int width, height; Window->GetWindowSize(width, height); int w = min(width, height); // add some border int x0 = width - w; int y0 = 10; w -= 20; glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (int MaterialIndex = 0; MaterialIndex < Mesh->Sections.Num(); MaterialIndex++) { const CMeshSection &Sec = Mesh->Sections[MaterialIndex]; if (!Sec.NumFaces) continue; BindDefaultMaterial(true); glDisable(GL_CULL_FACE); unsigned color = CMeshInstance::GetMaterialDebugColor(MaterialIndex); glColor4ubv((GLubyte*)&color); CIndexBuffer::IndexAccessor_t Index = Mesh->Indices.GetAccessor(); glBegin(GL_TRIANGLES); for (int i = Sec.FirstIndex; i < Sec.FirstIndex + Sec.NumFaces * 3; i++) { int VertIndex = Index(i); const CMeshVertex* V = OffsetPointer(Verts, VertIndex * VertexSize); const CMeshUVFloat& UV = (UVIndex == 0) ? V->UV : Mesh->ExtraUV[UVIndex-1][VertIndex]; glVertex2f(UV.U * w + x0, UV.V * w + y0); } glEnd(); } glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); unguard; }
void CStatMeshInstance::Draw(unsigned flags) { guard(CStatMeshInstance::Draw); int i; if (!pMesh->Lods.Num()) return; /*const*/ CStaticMeshLod& Mesh = pMesh->Lods[LodNum]; //?? not 'const' because of BuildTangents(); change this? int NumSections = Mesh.Sections.Num(); if (!NumSections || !Mesh.NumVerts) return; // if (!Mesh.HasNormals) Mesh.BuildNormals(); if (!Mesh.HasTangents) Mesh.BuildTangents(); // copy of CSkelMeshInstance::Draw sorting code #if SORT_BY_OPACITY // sort sections by material opacity int SectionMap[MAX_MESHMATERIALS]; int secPlace = 0; for (int opacity = 0; opacity < 2; opacity++) { for (i = 0; i < NumSections; i++) { UUnrealMaterial *Mat = Mesh.Sections[i].Material; int op = 0; // sort value if (Mat && Mat->IsTranslucent()) op = 1; if (op == opacity) SectionMap[secPlace++] = i; } } assert(secPlace == NumSections); #endif // SORT_BY_OPACITY // draw mesh glEnable(GL_LIGHTING); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Position); glNormalPointer(GL_FLOAT, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Normal); glTexCoordPointer(2, GL_FLOAT, sizeof(CStaticMeshVertex), &Mesh.Verts[0].UV[UVIndex].U); /*?? Can move tangent/binormal setup here too, but this will require to force shader to use fixed attribute locations (use glBindAttribLocation before glLinkProgram instead of querying atttribute via glGetAtribLocation). In this case: - can remove GCurrentShader - can eliminate hasTangent checks below and always bind attributes (when supports GL2.0) - can share code between SkeletalMesh and StaticMesh: - sort sections - setup arrays; differences: - position and normals are taken from different arrays in static and skeletal meshes - ShowInfluences in SkeletalMeshInstance should disable material binding - draw sections using glDrawElements() - un-setup arrays - use VAO+VBO for rendering */ for (i = 0; i < NumSections; i++) { #if SORT_BY_OPACITY int MaterialIndex = SectionMap[i]; #else int MaterialIndex = i; #endif const CMeshSection &Sec = Mesh.Sections[MaterialIndex]; if (!Sec.NumFaces) continue; SetMaterial(Sec.Material, MaterialIndex); // check tangent space GLint aTangent = -1, aBinormal = -1; bool hasTangent = false; const CShader *Sh = GCurrentShader; if (Sh) { aTangent = Sh->GetAttrib("tangent"); aBinormal = Sh->GetAttrib("binormal"); hasTangent = (aTangent >= 0 && aBinormal >= 0); } if (hasTangent) { glEnableVertexAttribArray(aTangent); glEnableVertexAttribArray(aBinormal); glVertexAttribPointer(aTangent, 3, GL_FLOAT, GL_FALSE, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Tangent); glVertexAttribPointer(aBinormal, 3, GL_FLOAT, GL_FALSE, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Binormal); } // draw //?? place this code into CIndexBuffer? if (Mesh.Indices.Is32Bit()) glDrawElements(GL_TRIANGLES, Sec.NumFaces * 3, GL_UNSIGNED_INT, &Mesh.Indices.Indices32[Sec.FirstIndex]); else glDrawElements(GL_TRIANGLES, Sec.NumFaces * 3, GL_UNSIGNED_SHORT, &Mesh.Indices.Indices16[Sec.FirstIndex]); // disable tangents if (hasTangent) { glDisableVertexAttribArray(aTangent); glDisableVertexAttribArray(aBinormal); } } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_LIGHTING); BindDefaultMaterial(true); // draw mesh normals if (flags & DF_SHOW_NORMALS) { int NumVerts = Mesh.NumVerts; glBegin(GL_LINES); glColor3f(0.5, 1, 0); for (i = 0; i < NumVerts; i++) { glVertex3fv(Mesh.Verts[i].Position.v); CVec3 tmp; VectorMA(Mesh.Verts[i].Position, 2, Mesh.Verts[i].Normal, tmp); glVertex3fv(tmp.v); } #if SHOW_TANGENTS glColor3f(0, 0.5f, 1); for (i = 0; i < NumVerts; i++) { const CVec3 &v = Mesh.Verts[i].Position; glVertex3fv(v.v); CVec3 tmp; VectorMA(v, 2, Mesh.Verts[i].Tangent, tmp); glVertex3fv(tmp.v); } glColor3f(1, 0, 0.5f); for (i = 0; i < NumVerts; i++) { const CVec3 &v = Mesh.Verts[i].Position; glVertex3fv(v.v); CVec3 tmp; VectorMA(v, 2, Mesh.Verts[i].Binormal, tmp); glVertex3fv(tmp.v); } #endif // SHOW_TANGENTS glEnd(); } unguard; }
void CSkelMeshViewer::Draw3D(float TimeDelta) { guard(CSkelMeshViewer::Draw3D); assert(Inst); CSkelMeshInstance *MeshInst = static_cast<CSkelMeshInstance*>(Inst); // tick animations MeshInst->UpdateAnimation(TimeDelta); #if HIGHLIGHT_CURRENT if (TimeSinceCreate < 0) TimeSinceCreate += 1.0f; // ignore this frame for highlighting else TimeSinceCreate += TimeDelta; float lightAmbient[4]; float boost = 0; float highlightTime = max(TimeSinceCreate, 0); if (TaggedMeshes.Num() && highlightTime < HIGHLIGHT_DURATION) { if (highlightTime > HIGHLIGHT_DURATION / 2) highlightTime = HIGHLIGHT_DURATION - highlightTime; // fade boost = HIGHLIGHT_STRENGTH * highlightTime / (HIGHLIGHT_DURATION / 2); glGetMaterialfv(GL_FRONT, GL_AMBIENT, lightAmbient); lightAmbient[0] += boost; lightAmbient[1] += boost; lightAmbient[2] += boost; glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); glMaterialfv(GL_FRONT, GL_AMBIENT, lightAmbient); } #endif // HIGHLIGHT_CURRENT // draw main mesh CMeshViewer::Draw3D(TimeDelta); #if HIGHLIGHT_CURRENT if (boost > 0) { lightAmbient[0] -= boost; lightAmbient[1] -= boost; lightAmbient[2] -= boost; glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); glMaterialfv(GL_FRONT, GL_AMBIENT, lightAmbient); } #endif // HIGHLIGHT_CURRENT int i; #if SHOW_BOUNDS //?? separate function for drawing wireframe Box (pass CCoords, Mins and Maxs to function) BindDefaultMaterial(true); glBegin(GL_LINES); CVec3 verts[8]; static const int inds[] = { 0,1, 2,3, 0,2, 1,3, 4,5, 6,7, 4,6, 5,7, 0,4, 1,5, 2,6, 3,7 }; const FBox &B = MeshInst->pMesh->BoundingBox; for (i = 0; i < 8; i++) { CVec3 &v = verts[i]; v[0] = (i & 1) ? B.Min.X : B.Max.X; v[1] = (i & 2) ? B.Min.Y : B.Max.Y; v[2] = (i & 4) ? B.Min.Z : B.Max.Z; MeshInst->BaseTransformScaled.TransformPointSlow(v, v); } // can use glDrawElements(), but this will require more GL setup glColor3f(0.5,0.5,1); for (i = 0; i < ARRAY_COUNT(inds) / 2; i++) { glVertex3fv(verts[inds[i*2 ]].v); glVertex3fv(verts[inds[i*2+1]].v); } glEnd(); glColor3f(1, 1, 1); #endif // SHOW_BOUNDS for (i = 0; i < TaggedMeshes.Num(); i++) { CSkelMeshInstance *mesh = TaggedMeshes[i]; if (mesh->pMesh == MeshInst->pMesh) continue; // avoid duplicates mesh->UpdateAnimation(TimeDelta); DrawMesh(mesh); } //?? make this common - place into DrawMesh() ? //?? problem: overdraw of skeleton when displaying multiple meshes //?? (especially when ShowInfluences is on, and meshes has different bone counts - the same bone //?? will be painted multiple times with different colors) if (ShowSkel) MeshInst->DrawSkeleton(ShowLabels, (DrawFlags & DF_SHOW_INFLUENCES) != 0); if (ShowAttach) MeshInst->DrawAttachments(); if (IsFollowingMesh) FocusCameraOnPoint(MeshInst->GetMeshOrigin()); unguard; }
void CMaterialViewer::Draw3D(float TimeDelta) { if (IsTexture && ShowChannels) return; static const CVec3 origin = { -150, 100, 100 }; // static const CVec3 origin = { -150, 50, 50 }; CVec3 lightPosV; viewAxis.UnTransformVector(origin, lightPosV); #if 0 // show light source glDisable(GL_LIGHTING); BindDefaultMaterial(true); glBegin(GL_LINES); glColor3f(1, 0, 0); CVec3 tmp; tmp = lightPosV; tmp[0] -= 20; glVertex3fv(tmp.v); tmp[0] += 40; glVertex3fv(tmp.v); tmp = lightPosV; tmp[1] -= 20; glVertex3fv(tmp.v); tmp[1] += 40; glVertex3fv(tmp.v); tmp = lightPosV; tmp[2] -= 20; glVertex3fv(tmp.v); tmp[2] += 40; glVertex3fv(tmp.v); glEnd(); #endif glColor3f(1, 1, 1); if (!IsTexture) { glEnable(GL_LIGHTING); // no lighting for textures float lightPos[4]; lightPos[0] = lightPosV[0]; lightPos[1] = lightPosV[1]; lightPos[2] = lightPosV[2]; lightPos[3] = 0; glLightfv(GL_LIGHT0, GL_POSITION, lightPos); // glMaterialf(GL_FRONT, GL_SHININESS, 20); } // bind material UUnrealMaterial *Mat = static_cast<UUnrealMaterial*>(Object); Mat->SetMaterial(); // check tangent space GLint aNormal = -1; GLint aTangent = -1; // GLint aBinormal = -1; const CShader *Sh = GCurrentShader; if (Sh) { aNormal = Sh->GetAttrib("normal"); aTangent = Sh->GetAttrib("tangent"); // aBinormal = Sh->GetAttrib("binormal"); } // and draw box ... #define A 100 // vertex #define V000 {-A, -A, -A} #define V001 {-A, -A, A} #define V010 {-A, A, -A} #define V011 {-A, A, A} #define V100 { A, -A, -A} #define V101 { A, -A, A} #define V110 { A, A, -A} #define V111 { A, A, A} static const CVec3 box[] = { V001, V000, V010, V011, // near (x=-A) V111, V110, V100, V101, // far (x=+A) V101, V100, V000, V001, // left (y=-A) V011, V010, V110, V111, // right (y=+A) V010, V000, V100, V110, // bottom (z=-A) V001, V011, V111, V101, // top (z=+A) #undef A }; #define REP4(...) {__VA_ARGS__},{__VA_ARGS__},{__VA_ARGS__},{__VA_ARGS__} static const CVec4 normal[] = { REP4(-1, 0, 0, 1 ), REP4( 1, 0, 0, 1 ), REP4( 0,-1, 0, 1 ), REP4( 0, 1, 0, 1 ), REP4( 0, 0,-1, 1 ), REP4( 0, 0, 1, 1 ) }; static const CVec3 tangent[] = { REP4( 0,-1, 0 ), REP4( 0, 1, 0 ), REP4( 1, 0, 0 ), REP4(-1, 0, 0 ), REP4( 1, 0, 0 ), REP4(-1, 0, 0 ) }; // static const CVec3 binormal[] = // { // REP4( 0, 0, 1 ), // REP4( 0, 0, 1 ), // REP4( 0, 0, 1 ), // REP4( 0, 0, 1 ), // REP4( 0,-1, 0 ), // REP4( 0,-1, 0 ) // }; #undef REP4 static const float tex[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}, {0, 1}, {1, 1}, {1, 0} }; static const int inds[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, 12,13,14,15, 16,17,18,19, 20,21,22,23 }; #if 0 // verify tangents, should be suitable for binormal computation in shaders // (note: we're not verifying correspondence with UV coordinates) for (int i = 0; i < 24; i++) { CVec4 n4 = normal[i]; CVec3 n = n4.ToVec3(); CVec3 t = tangent[i]; CVec3 b = binormal[i]; CVec3 b2; cross(n, t, b2); VectorScale(b2, n4[3], b2); float dd = VectorDistance(b2, b); if (dd > 0.001f) appPrintf("dist[%d] = %g\n", i, dd); } #endif glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(CVec3), box); glNormalPointer(GL_FLOAT, sizeof(CVec4), normal); glTexCoordPointer(2, GL_FLOAT, 0, tex); if (aNormal >= 0) { glEnableVertexAttribArray(aNormal); // send 4 components to decode binormal in shader glVertexAttribPointer(aNormal, 4, GL_FLOAT, GL_FALSE, sizeof(CVec4), normal); } if (aTangent >= 0) { glEnableVertexAttribArray(aTangent); glVertexAttribPointer(aTangent, 3, GL_FLOAT, GL_FALSE, sizeof(CVec3), tangent); } // if (aBinormal >= 0) // { // glEnableVertexAttribArray(aBinormal); // glVertexAttribPointer(aBinormal, 3, GL_FLOAT, GL_FALSE, sizeof(CVec3), binormal); // } glDrawElements(GL_QUADS, ARRAY_COUNT(inds), GL_UNSIGNED_INT, inds); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); // disable tangents if (aNormal >= 0) glDisableVertexAttribArray(aNormal); if (aTangent >= 0) glDisableVertexAttribArray(aTangent); // if (aBinormal >= 0) // glDisableVertexAttribArray(aBinormal); BindDefaultMaterial(true); #if 0 glBegin(GL_LINES); glColor3f(0.2, 0.2, 1); for (int i = 0; i < ARRAY_COUNT(box); i++) { glVertex3fv(box[i].v); CVec3 tmp; VectorMA(box[i], 20, normal[i], tmp); glVertex3fv(tmp.v); } glEnd(); glColor3f(1, 1, 1); #endif }
void CVertMeshInstance::Draw(unsigned flags) { guard(CVertMeshInstance::Draw); if (!Sections.Num()) return; // empty mesh int i; // get 2 frames for interpolation int FrameNum1, FrameNum2; float frac; if (AnimIndex != INDEX_NONE) { const FMeshAnimSeq &A = pMesh->AnimSeqs[AnimIndex]; FrameNum1 = appFloor(AnimTime); FrameNum2 = FrameNum1 + 1; if (FrameNum2 >= A.NumFrames) FrameNum2 = 0; frac = AnimTime - FrameNum1; FrameNum1 += A.StartFrame; FrameNum2 += A.StartFrame; // clamp frame numbers (has mesh with wrong frame count in last animation: // UT1/BotPack/cdgunmainM; this animation is shown in UnrealEd as lerping to null size) if (FrameNum1 >= pMesh->FrameCount) FrameNum1 = pMesh->FrameCount-1; if (FrameNum2 >= pMesh->FrameCount) FrameNum2 = pMesh->FrameCount-1; } else { FrameNum1 = 0; FrameNum2 = 0; frac = 0; } int base1 = pMesh->VertexCount * FrameNum1; int base2 = pMesh->VertexCount * FrameNum2; float backLerp = 1 - frac; CVec3 Scale1, Scale2; Scale1 = Scale2 = CVT(pMesh->MeshScale); Scale1.Scale(backLerp); Scale2.Scale(frac); // compute deformed mesh const FMeshWedge *W = &pMesh->Wedges[0]; CVec3 *pVec = Verts; CVec3 *pNormal = Normals; for (i = 0; i < pMesh->Wedges.Num(); i++, pVec++, pNormal++, W++) { CVec3 tmp; #if 0 // path with no frame lerp // vertex const FMeshVert &V = pMesh->Verts[base1 + W->iVertex]; tmp[0] = V.X * pMesh->MeshScale.X; tmp[1] = V.Y * pMesh->MeshScale.Y; tmp[2] = V.Z * pMesh->MeshScale.Z; BaseTransform.TransformPoint(tmp, *pVec); // normal const FMeshNorm &N = pMesh->Normals[base1 + W->iVertex]; tmp[0] = (N.X - 512.0f) / 512; tmp[1] = (N.Y - 512.0f) / 512; tmp[2] = (N.Z - 512.0f) / 512; BaseTransform.axis.TransformVector(tmp, *pNormal); #else // vertex const FMeshVert &V1 = pMesh->Verts[base1 + W->iVertex]; const FMeshVert &V2 = pMesh->Verts[base2 + W->iVertex]; tmp[0] = V1.X * Scale1[0] + V2.X * Scale2[0]; tmp[1] = V1.Y * Scale1[1] + V2.Y * Scale2[1]; tmp[2] = V1.Z * Scale1[2] + V2.Z * Scale2[2]; BaseTransform.TransformPoint(tmp, *pVec); // normal const FMeshNorm &N1 = pMesh->Normals[base1 + W->iVertex]; const FMeshNorm &N2 = pMesh->Normals[base2 + W->iVertex]; tmp[0] = (N1.X * backLerp + N2.X * frac - 512.0f) / 512; tmp[1] = (N1.Y * backLerp + N2.Y * frac - 512.0f) / 512; tmp[2] = (N1.Z * backLerp + N2.Z * frac - 512.0f) / 512; BaseTransform.axis.TransformVector(tmp, *pNormal); #endif } #if 0 glBegin(GL_POINTS); for (i = 0; i < pMesh->Wedges.Num(); i++) { glVertex3fv(Verts[i].v); } glEnd(); return; #endif // draw mesh glEnable(GL_LIGHTING); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(CVec3), Verts); glNormalPointer(GL_FLOAT, sizeof(CVec3), Normals); glTexCoordPointer(2, GL_FLOAT, sizeof(FMeshWedge), &pMesh->Wedges[0].TexUV.U); for (i = 0; i < Sections.Num(); i++) { const CMeshSection &Sec = Sections[i]; if (!Sec.NumFaces) continue; SetMaterial(Sec.Material, i); glDrawElements(GL_TRIANGLES, Sec.NumFaces * 3, GL_UNSIGNED_SHORT, &Indices[Sec.FirstIndex]); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_LIGHTING); BindDefaultMaterial(true); // draw mesh normals if (flags & DF_SHOW_NORMALS) { glBegin(GL_LINES); glColor3f(0.5, 1, 0); for (i = 0; i < pMesh->Wedges.Num(); i++) { glVertex3fv(Verts[i].v); CVec3 tmp; VectorMA(Verts[i], 2, Normals[i], tmp); glVertex3fv(tmp.v); } glEnd(); } unguard; }
void CStatMeshInstance::Draw(unsigned flags) { guard(CStatMeshInstance::Draw); int i; if (!pMesh->Lods.Num()) return; /*const*/ CStaticMeshLod& Mesh = pMesh->Lods[LodNum]; //?? not 'const' because of BuildTangents(); change this? int NumSections = Mesh.Sections.Num(); if (!NumSections || !Mesh.NumVerts) return; // if (!Mesh.HasNormals) Mesh.BuildNormals(); if (!Mesh.HasTangents) Mesh.BuildTangents(); // copy of CSkelMeshInstance::Draw sorting code #if SORT_BY_OPACITY // sort sections by material opacity int SectionMap[MAX_MESHMATERIALS]; int secPlace = 0; for (int opacity = 0; opacity < 2; opacity++) { for (i = 0; i < NumSections; i++) { UUnrealMaterial *Mat = Mesh.Sections[i].Material; int op = 0; // sort value if (Mat && Mat->IsTranslucent()) op = 1; if (op == opacity) SectionMap[secPlace++] = i; } } assert(secPlace == NumSections); #endif // SORT_BY_OPACITY // draw mesh glEnable(GL_LIGHTING); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Position); glNormalPointer(GL_BYTE, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Normal); if (UVIndex == 0) { glTexCoordPointer(2, GL_FLOAT, sizeof(CStaticMeshVertex), &Mesh.Verts[0].UV.U); } else { glTexCoordPointer(2, GL_FLOAT, sizeof(CMeshUVFloat), &Mesh.ExtraUV[UVIndex-1][0].U); } /*?? Can move tangent/binormal setup here too, but this will require to force shader to use fixed attribute locations (use glBindAttribLocation before glLinkProgram instead of querying atttribute via glGetAtribLocation). In this case: - can remove GCurrentShader - can eliminate hasTangent checks below and always bind attributes (when supports GL2.0) - can share code between SkeletalMesh and StaticMesh: - sort sections - setup arrays; differences: - position and normals are taken from different arrays in static and skeletal meshes - ShowInfluences in SkeletalMeshInstance should disable material binding - draw sections using glDrawElements() - un-setup arrays - use VAO+VBO for rendering */ for (i = 0; i < NumSections; i++) { #if SORT_BY_OPACITY int MaterialIndex = SectionMap[i]; #else int MaterialIndex = i; #endif const CMeshSection &Sec = Mesh.Sections[MaterialIndex]; if (!Sec.NumFaces) continue; SetMaterial(Sec.Material, MaterialIndex); // check tangent space GLint aNormal = -1; GLint aTangent = -1; // GLint aBinormal = -1; const CShader *Sh = GCurrentShader; if (Sh) { aNormal = Sh->GetAttrib("normal"); aTangent = Sh->GetAttrib("tangent"); // aBinormal = Sh->GetAttrib("binormal"); } if (aNormal >= 0) { glEnableVertexAttribArray(aNormal); // send 4 components to decode binormal in shader glVertexAttribPointer(aNormal, 4, GL_BYTE, GL_FALSE, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Normal); } if (aTangent >= 0) { glEnableVertexAttribArray(aTangent); glVertexAttribPointer(aTangent, 3, GL_BYTE, GL_FALSE, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Tangent); } /* if (aBinormal >= 0) { glEnableVertexAttribArray(aBinormal); glVertexAttribPointer(aBinormal, 3, GL_BYTE, GL_FALSE, sizeof(CStaticMeshVertex), &Mesh.Verts[0].Binormal); } */ // draw //?? place this code into CIndexBuffer? if (Mesh.Indices.Is32Bit()) glDrawElements(GL_TRIANGLES, Sec.NumFaces * 3, GL_UNSIGNED_INT, &Mesh.Indices.Indices32[Sec.FirstIndex]); else glDrawElements(GL_TRIANGLES, Sec.NumFaces * 3, GL_UNSIGNED_SHORT, &Mesh.Indices.Indices16[Sec.FirstIndex]); // disable tangents if (aNormal >= 0) glDisableVertexAttribArray(aNormal); if (aTangent >= 0) glDisableVertexAttribArray(aTangent); // if (aBinormal >= 0) // glDisableVertexAttribArray(aBinormal); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_LIGHTING); BindDefaultMaterial(true); // draw mesh normals if (flags & DF_SHOW_NORMALS) { //!! TODO: performance issues when displaying a large mesh (1M+ triangles) with normals/tangents. //!! Possible solution: //!! 1. use vertex buffer for normals, use single draw call //!! 2. cache normal data between render frames //!! 3. DecodeTangents() will do Unpack() for normal and tangent too, so this work is duplicated here int NumVerts = Mesh.NumVerts; glBegin(GL_LINES); glColor3f(0.5, 1, 0); CVecT tmp, unpacked; const float VisualLength = 2.0f; for (i = 0; i < NumVerts; i++) { glVertex3fv(Mesh.Verts[i].Position.v); Unpack(unpacked, Mesh.Verts[i].Normal); VectorMA(Mesh.Verts[i].Position, VisualLength, unpacked, tmp); glVertex3fv(tmp.v); } #if SHOW_TANGENTS glColor3f(0, 0.5f, 1); for (i = 0; i < NumVerts; i++) { const CVec3 &v = Mesh.Verts[i].Position; glVertex3fv(v.v); Unpack(unpacked, Mesh.Verts[i].Tangent); VectorMA(v, VisualLength, unpacked, tmp); glVertex3fv(tmp.v); } glColor3f(1, 0, 0.5f); for (i = 0; i < NumVerts; i++) { const CMeshVertex& vert = Mesh.Verts[i]; // decode binormal CVecT normal, tangent, binormal; vert.DecodeTangents(normal, tangent, binormal); // render const CVecT &v = vert.Position; glVertex3fv(v.v); VectorMA(v, VisualLength, binormal, tmp); glVertex3fv(tmp.v); } #endif // SHOW_TANGENTS glEnd(); } unguard; }