#include "lc_global.h" #include "lc_mesh.h" #include "lc_colors.h" #include "lc_texture.h" #include "lc_file.h" #include "lc_math.h" #include "lc_application.h" #include "lc_library.h" lcMesh::lcMesh() { mSections = NULL; mNumSections = 0; mNumVertices = 0; mNumTexturedVertices = 0; mIndexType = 0; } lcMesh::~lcMesh() { delete[] mSections; } void lcMesh::Create(int NumSections, int NumVertices, int NumTexturedVertices, int NumIndices) { mSections = new lcMeshSection[NumSections]; mNumSections = NumSections; mNumVertices = NumVertices; mNumTexturedVertices = NumTexturedVertices; mVertexBuffer.SetSize(NumVertices * sizeof(lcVertex) + NumTexturedVertices * sizeof(lcVertexTextured)); if (NumVertices < 0x10000 && NumTexturedVertices < 0x10000) { mIndexType = GL_UNSIGNED_SHORT; mIndexBuffer.SetSize(NumIndices * sizeof(GLushort)); } else { mIndexType = GL_UNSIGNED_INT; mIndexBuffer.SetSize(NumIndices * sizeof(GLuint)); } } void lcMesh::CreateBox() { Create(2, 8, 0, 36 + 24); lcVector3 Min(-0.4f, -0.4f, -0.96f); lcVector3 Max(0.4f, 0.4f, 0.16f); float* Verts = (float*)mVertexBuffer.mData; lcuint16* Indices = (lcuint16*)mIndexBuffer.mData; *Verts++ = Min[0]; *Verts++ = Min[1]; *Verts++ = Min[2]; *Verts++ = Min[0]; *Verts++ = Max[1]; *Verts++ = Min[2]; *Verts++ = Max[0]; *Verts++ = Max[1]; *Verts++ = Min[2]; *Verts++ = Max[0]; *Verts++ = Min[1]; *Verts++ = Min[2]; *Verts++ = Min[0]; *Verts++ = Min[1]; *Verts++ = Max[2]; *Verts++ = Min[0]; *Verts++ = Max[1]; *Verts++ = Max[2]; *Verts++ = Max[0]; *Verts++ = Max[1]; *Verts++ = Max[2]; *Verts++ = Max[0]; *Verts++ = Min[1]; *Verts++ = Max[2]; lcMeshSection* Section = &mSections[0]; Section->ColorIndex = gDefaultColor; Section->IndexOffset = 0; Section->NumIndices = 36; Section->PrimitiveType = GL_TRIANGLES; Section->Texture = NULL; *Indices++ = 0; *Indices++ = 1; *Indices++ = 2; *Indices++ = 0; *Indices++ = 2; *Indices++ = 3; *Indices++ = 7; *Indices++ = 6; *Indices++ = 5; *Indices++ = 7; *Indices++ = 5; *Indices++ = 4; *Indices++ = 0; *Indices++ = 1; *Indices++ = 5; *Indices++ = 0; *Indices++ = 5; *Indices++ = 4; *Indices++ = 2; *Indices++ = 3; *Indices++ = 7; *Indices++ = 2; *Indices++ = 7; *Indices++ = 6; *Indices++ = 0; *Indices++ = 3; *Indices++ = 7; *Indices++ = 0; *Indices++ = 7; *Indices++ = 4; *Indices++ = 1; *Indices++ = 2; *Indices++ = 6; *Indices++ = 1; *Indices++ = 6; *Indices++ = 5; Section = &mSections[1]; Section->ColorIndex = gEdgeColor; Section->IndexOffset = 36 * 2; Section->NumIndices = 24; Section->PrimitiveType = GL_LINES; Section->Texture = NULL; *Indices++ = 0; *Indices++ = 1; *Indices++ = 1; *Indices++ = 2; *Indices++ = 2; *Indices++ = 3; *Indices++ = 3; *Indices++ = 0; *Indices++ = 4; *Indices++ = 5; *Indices++ = 5; *Indices++ = 6; *Indices++ = 6; *Indices++ = 7; *Indices++ = 7; *Indices++ = 4; *Indices++ = 0; *Indices++ = 4; *Indices++ = 1; *Indices++ = 5; *Indices++ = 2; *Indices++ = 6; *Indices++ = 3; *Indices++ = 7; UpdateBuffers(); } void lcMesh::Render(int DefaultColorIdx, bool Selected, bool Focused) { char* ElementsOffset; char* BufferOffset; if (GL_HasVertexBufferObject()) { glBindBuffer(GL_ARRAY_BUFFER_ARB, mVertexBuffer.mBuffer); BufferOffset = NULL; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, mIndexBuffer.mBuffer); ElementsOffset = NULL; } else { BufferOffset = (char*)mVertexBuffer.mData; ElementsOffset = (char*)mIndexBuffer.mData; } glVertexPointer(3, GL_FLOAT, 0, BufferOffset); for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection* Section = &mSections[SectionIdx]; int ColorIdx = Section->ColorIndex; if (Section->PrimitiveType == GL_TRIANGLES) { if (ColorIdx == gDefaultColor) ColorIdx = DefaultColorIdx; if (lcIsColorTranslucent(ColorIdx)) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // TODO: cache states glEnable(GL_BLEND); glDepthMask(GL_FALSE); } else { glDepthMask(GL_TRUE); glDisable(GL_BLEND); } lcSetColor(ColorIdx); } else { glDepthMask(GL_TRUE); // TODO: cache states glDisable(GL_BLEND); if (Focused) lcSetColorFocused(); else if (Selected) lcSetColorSelected(); else if (ColorIdx == gEdgeColor) lcSetEdgeColor(DefaultColorIdx); else lcSetColor(ColorIdx); } if (mNumTexturedVertices) { if (Section->Texture) // TODO: cache states { glVertexPointer(3, GL_FLOAT, sizeof(lcVertexTextured), BufferOffset + (mNumVertices * sizeof(lcVertex))); glTexCoordPointer(2, GL_FLOAT, sizeof(lcVertexTextured), BufferOffset + ((mNumVertices + 1) * sizeof(lcVertex))); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glBindTexture(GL_TEXTURE_2D, Section->Texture->mTexture); glEnable(GL_TEXTURE_2D); } else { glDisableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(3, GL_FLOAT, 0, BufferOffset); glTexCoordPointer(2, GL_FLOAT, 0, NULL); glDisable(GL_TEXTURE_2D); } } glDrawElements(Section->PrimitiveType, Section->NumIndices, mIndexType, ElementsOffset + Section->IndexOffset); } glDepthMask(GL_TRUE); // TODO: cache states glDisable(GL_BLEND); if (GL_HasVertexBufferObject()) { glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); } else glVertexPointer(3, GL_FLOAT, 0, NULL); if (mNumTexturedVertices) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, NULL); glDisable(GL_TEXTURE_2D); } } template bool lcMesh::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDist, lcVector3& Intersection) { float* Verts = (float*)mVertexBuffer.mData; bool Hit = false; for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection* Section = &mSections[SectionIdx]; if (Section->PrimitiveType != GL_TRIANGLES) continue; IndexType* Indices = (IndexType*)mIndexBuffer.mData + Section->IndexOffset / sizeof(IndexType); for (int Idx = 0; Idx < Section->NumIndices; Idx += 3) { float* p1 = Verts + Indices[Idx + 0] * 3; float* p2 = Verts + Indices[Idx + 1] * 3; float* p3 = Verts + Indices[Idx + 2] * 3; lcVector3 v1(p1[0], p1[1], p1[2]); lcVector3 v2(p2[0], p2[1], p2[2]); lcVector3 v3(p3[0], p3[1], p3[2]); if (lcLineTriangleMinIntersection(v1, v2, v3, Start, End, &MinDist, &Intersection)) Hit = true; } } return Hit; } bool lcMesh::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDist, lcVector3& Intersection) { if (mIndexType == GL_UNSIGNED_SHORT) return MinIntersectDist(Start, End, MinDist, Intersection); else return MinIntersectDist(Start, End, MinDist, Intersection); } template bool lcMesh::IntersectsPlanes(const lcVector4 Planes[6]) { float* Verts = (float*)mVertexBuffer.mData; for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection* Section = &mSections[SectionIdx]; if (Section->PrimitiveType != GL_TRIANGLES) continue; IndexType* Indices = (IndexType*)mIndexBuffer.mData + Section->IndexOffset / sizeof(IndexType); for (int Idx = 0; Idx < Section->NumIndices; Idx += 3) if (lcTriangleIntersectsPlanes(&Verts[Indices[Idx]*3], &Verts[Indices[Idx+1]*3], &Verts[Indices[Idx+2]*3], Planes)) return true; } return false; } bool lcMesh::IntersectsPlanes(const lcVector4 Planes[6]) { if (mIndexType == GL_UNSIGNED_SHORT) return IntersectsPlanes(Planes); else return IntersectsPlanes(Planes); } template void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char* ColorTable) { char Line[1024]; sprintf(Line, "#declare lc_%s = union {\n", MeshName); File.WriteLine(Line); float* Verts = (float*)mVertexBuffer.mData; for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection* Section = &mSections[SectionIdx]; if (Section->PrimitiveType != GL_TRIANGLES) continue; IndexType* Indices = (IndexType*)mIndexBuffer.mData + Section->IndexOffset / sizeof(IndexType); File.WriteLine(" mesh {\n"); for (int Idx = 0; Idx < Section->NumIndices; Idx += 3) { sprintf(Line, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n", -Verts[Indices[Idx+0]*3+1], -Verts[Indices[Idx+0]*3], Verts[Indices[Idx+0]*3+2], -Verts[Indices[Idx+1]*3+1], -Verts[Indices[Idx+1]*3], Verts[Indices[Idx+1]*3+2], -Verts[Indices[Idx+2]*3+1], -Verts[Indices[Idx+2]*3], Verts[Indices[Idx+2]*3+2]); File.WriteLine(Line); } if (Section->ColorIndex != gDefaultColor) { sprintf(Line, "material { texture { %s normal { bumps 0.1 scale 2 } } }", &ColorTable[Section->ColorIndex * LC_MAX_COLOR_NAME]); File.WriteLine(Line); } File.WriteLine(" }\n"); } } void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char* ColorTable) { if (mIndexType == GL_UNSIGNED_SHORT) ExportPOVRay(File, MeshName, ColorTable); else ExportPOVRay(File, MeshName, ColorTable); } template void lcMesh::ExportWavefrontIndices(lcFile& File, int DefaultColorIndex, int VertexOffset) { char Line[1024]; for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection* Section = &mSections[SectionIdx]; if (Section->PrimitiveType != GL_TRIANGLES) continue; IndexType* Indices = (IndexType*)mIndexBuffer.mData + Section->IndexOffset / sizeof(IndexType); if (Section->ColorIndex == gDefaultColor) sprintf(Line, "usemtl %s\n", gColorList[DefaultColorIndex].SafeName); else sprintf(Line, "usemtl %s\n", gColorList[Section->ColorIndex].SafeName); File.WriteLine(Line); for (int Idx = 0; Idx < Section->NumIndices; Idx += 3) { long int idx1 = Indices[Idx + 0] + VertexOffset; long int idx2 = Indices[Idx + 1] + VertexOffset; long int idx3 = Indices[Idx + 2] + VertexOffset; if (idx1 != idx2 && idx1 != idx3 && idx2 != idx3) sprintf(Line, "f %ld %ld %ld\n", idx1, idx2, idx3); File.WriteLine(Line); } } File.WriteLine("\n"); } void lcMesh::ExportWavefrontIndices(lcFile& File, int DefaultColorIndex, int VertexOffset) { if (mIndexType == GL_UNSIGNED_SHORT) ExportWavefrontIndices(File, DefaultColorIndex, VertexOffset); else ExportWavefrontIndices(File, DefaultColorIndex, VertexOffset); } bool lcMesh::FileLoad(lcFile& File) { if (File.ReadU32() != LC_FILE_ID || File.ReadU32() != LC_MESH_FILE_ID || File.ReadU32() != LC_MESH_FILE_VERSION) return false; lcuint32 NumVertices, NumTexturedVertices, NumIndices; lcuint16 NumSections; if (!File.ReadU16(&NumSections, 1) || !File.ReadU32(&NumVertices, 1) || !File.ReadU32(&NumTexturedVertices, 1) || !File.ReadU32(&NumIndices, 1)) return false; Create(NumSections, NumVertices, NumTexturedVertices, NumIndices); for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection& Section = mSections[SectionIdx]; lcuint32 ColorCode, IndexOffset; lcuint16 Triangles, Length; if (!File.ReadU32(&ColorCode, 1) || !File.ReadU32(&IndexOffset, 1) || !File.ReadU32(&NumIndices, 1) || !File.ReadU16(&Triangles, 1)) return false; Section.ColorIndex = lcGetColorIndex(ColorCode); Section.IndexOffset = IndexOffset; Section.NumIndices = NumIndices; Section.PrimitiveType = Triangles ? GL_TRIANGLES : GL_LINES; if (!File.ReadU16(&Length, 1)) return false; if (Length) { if (Length >= LC_TEXTURE_NAME_LEN) return false; char FileName[LC_TEXTURE_NAME_LEN]; File.ReadBuffer(FileName, Length); FileName[Length] = 0; Section.Texture = lcGetPiecesLibrary()->FindTexture(FileName); } else Section.Texture = NULL; } File.ReadFloats((float*)mVertexBuffer.mData, 3 * mNumVertices + 5 * mNumTexturedVertices); if (mIndexType == GL_UNSIGNED_SHORT) File.ReadU16((lcuint16*)mIndexBuffer.mData, mIndexBuffer.mSize / 2); else File.ReadU32((lcuint32*)mIndexBuffer.mData, mIndexBuffer.mSize / 4); UpdateBuffers(); return true; } void lcMesh::FileSave(lcFile& File) { File.WriteU32(LC_FILE_ID); File.WriteU32(LC_MESH_FILE_ID); File.WriteU32(LC_MESH_FILE_VERSION); File.WriteU16(mNumSections); File.WriteU32(mNumVertices); File.WriteU32(mNumTexturedVertices); File.WriteU32(mIndexBuffer.mSize / (mIndexType == GL_UNSIGNED_SHORT ? 2 : 4)); for (int SectionIdx = 0; SectionIdx < mNumSections; SectionIdx++) { lcMeshSection& Section = mSections[SectionIdx]; File.WriteU32(lcGetColorCode(Section.ColorIndex)); File.WriteU32(Section.IndexOffset); File.WriteU32(Section.NumIndices); File.WriteU16(Section.PrimitiveType == GL_TRIANGLES ? 1 : 0); if (Section.Texture) { int Length = strlen(Section.Texture->mName); File.WriteU16(Length); File.WriteBuffer(Section.Texture->mName, Length); } else File.WriteU16(0); } File.WriteFloats((float*)mVertexBuffer.mData, 3 * mNumVertices + 5 * mNumTexturedVertices); if (mIndexType == GL_UNSIGNED_SHORT) File.WriteU16((lcuint16*)mIndexBuffer.mData, mIndexBuffer.mSize / 2); else File.WriteU32((lcuint32*)mIndexBuffer.mData, mIndexBuffer.mSize / 4); } int lcTranslucentRenderMeshCompare(const lcRenderMesh& a, const lcRenderMesh& b) { if (a.Distance > b.Distance) return 1; else return -1; } int lcOpaqueRenderMeshCompare(const lcRenderMesh& a, const lcRenderMesh& b) { if (a.Mesh > b.Mesh) return 1; else return -1; }