#include "stdafx.h" Mesh::Mesh() : IndexCount(0) , VertexCount(0) { glGenVertexArrays(1, &VertexArrayObject); glBindVertexArray(VertexArrayObject); glGenBuffers(1, &VertexArrayBuffer); glBindBuffer(GL_ARRAY_BUFFER, VertexArrayBuffer); glBufferData(GL_ARRAY_BUFFER, MaxVertexBuffer * sizeof(MeshVertex), 0, GL_STATIC_DRAW); glGenBuffers(1, &IndexBufferObject); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject); glBufferData(GL_ELEMENT_ARRAY_BUFFER, MaxIndexBuffer * sizeof(unsigned short), NULL, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(0); } void Mesh::Render() { glBindVertexArray(VertexArrayObject); unsigned int positionId = glGetAttribLocation(currentProgramId, "inPosition"); unsigned int normalId = glGetAttribLocation(currentProgramId, "inNormal"); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject); glBindBuffer(GL_ARRAY_BUFFER, VertexArrayBuffer); glEnableVertexAttribArray(positionId); glVertexAttribPointer(positionId, 4, GL_FLOAT, GL_FALSE, sizeof(MeshVertex), (const GLvoid*)0); glEnableVertexAttribArray(normalId); glVertexAttribPointer(normalId, 4, GL_FLOAT, GL_FALSE, sizeof(MeshVertex), (const GLvoid*)16); glDrawElements(GL_TRIANGLES, IndexCount, GL_UNSIGNED_SHORT, 0); glBindVertexArray(0); } void Mesh::Lock() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject); glBindBuffer(GL_ARRAY_BUFFER, VertexArrayBuffer); IndexBuffer = (unsigned short*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_WRITE); VertexBuffer = (MeshVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); } void Mesh::Unlock() { glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); } void Mesh::GenerateNormals() { for (int i = 0; i < VertexCount; ++i) { VertexBuffer[i].Normal = vec4(0, 0, 0, 1); } for (int i = 0; i < IndexCount; ) { int count = IndexBuffer[i]; ++i; for (int j = 0; j < count; ++j) { int index = IndexBuffer[i + j]; int indexPrev = IndexBuffer[i + ((j + count - 1) % count)]; int indexNext = IndexBuffer[i + ((j + 1) % count)]; vec4 p1(VertexBuffer[indexPrev].Position - VertexBuffer[index].Position); vec4 p2(VertexBuffer[indexNext].Position - VertexBuffer[index].Position); VertexBuffer[index].Normal += cross(p1, p2); } i += count; } } void Mesh::NormalizeNormals() { for (int i = 0; i < VertexCount; ++i) { VertexBuffer[i].Normal = normalize(VertexBuffer[i].Normal); } } void Mesh::Triangulate() // Converts polygons into triangle fans { OP.Reset(); OP.NextVertexCount = 0; OP.NextFaceCount = 0; for (int i = 0; i < IndexCount;) { int count = IndexBuffer[i++]; int firstVertex = IndexBuffer[i++]; int secondVertex = IndexBuffer[i++]; int j = 0; do { OP.NextIB[OP.NextIndexCount++] = firstVertex; OP.NextIB[OP.NextIndexCount++] = secondVertex; OP.NextIB[OP.NextIndexCount++] = IndexBuffer[i + j]; OP.NextFaceCount++; secondVertex = IndexBuffer[i + j++]; } while (j < count - 2); i += count - 2; } memcpy(IndexBuffer, OP.NextIB, MaxIndexBuffer * sizeof(unsigned short)); IndexCount = OP.NextIndexCount; FaceCount = OP.NextFaceCount; } void Mesh::CatmullClarkSubdivide() { if (IndexCount >= MaxIndexBuffer / 4 || VertexCount >= MaxVertexBuffer * 9 / 4) { return; } // neuer VB ist so: // alter VB bzw. verschobene Originalpunkte // ein Vertex pro Face (FacePunkte) // ein Vertex pro Edge (EdgePunkte) //neuer IB ist so: // 4 / alte Ecke0 / EdgePunkt / FaceFacePunkt / EdgePunkt // 4 / alte Ecke1 / EdgePunkt / FaceFacePunkt / EdgePunkt //... // 4 / alte EckeN / EdgePunkt / FaceFacePunkt / EdgePunkt (N = = Vertexcount) //beim kopieren dann ueberall noch 4 OP.Reset(); OP.NextVertexCount = VertexCount + FaceCount; //Basis Vertices sind die alte Ecken und die neuen FacePunkte int face = 0; for (int i = 0; i < IndexCount;) { int count = IndexBuffer[i++]; vec4& fp = OP.NextVB[VertexCount + face].Position; unsigned short int* ib = OP.NextIB + OP.NextIndexCount; for (int j = 0; j < count; ++j) { int index = IndexBuffer[i + j]; fp += VertexBuffer[index].Position; ib[j * 5] = 4; ib[j * 5 + 1] = index; ib[j * 5 + 3] = VertexCount + face; // erste Haelfte IB fertig } fp /= (float)count; // jetzt ist der FacePunkt fertig for (int j = 0; j < count; ++j) { int index = IndexBuffer[i + j]; int indexNext = IndexBuffer[i + ((j + 1) % count)]; int Edge = OP.MakeEdgeID(index, indexNext); OP.NextVB[OP.EdgeID[Edge]].Position += fp; // Summe aller FacePunkte zu dieser Ecke sind hier ib[j * 5 + 2] = OP.EdgeID[Edge]; ib[(j * 5 + 9) % (count * 5)] = OP.EdgeID[Edge]; // IB fertig! OP.NextVB[index].Position += fp; //Summe aller FacePunkte die an diesen Originalpunkt grenzen } i += count; face++; OP.NextIndexCount += count * 5; OP.NextFaceCount += count; } // Originalpunkte verschieben for (int i = 0; i < VertexCount; ++i) { bool openEdges = false; // gab es offene Kanten (d.h. einseitig benutze?) vec4 edgesVector(0,0,0,0); vec4 openEdgesVector(0,0,0,0); int edgeCount = 0; int openEdgeCount = 0; for (int j = 0; j < OP.EdgeCount; ++j) { int V1 = (OP.EdgeConfiguration[j] & 0x0000ffff); int V2 = (OP.EdgeConfiguration[j] & 0xffff0000) >> 16; if (V1 == i || V2 == i) { vec4 mid = VertexBuffer[V1].Position + VertexBuffer[V2].Position; if (OP.EdgeUsage[j] == 1) { openEdges = true; openEdgeCount++; openEdgesVector += mid; } edgeCount++; edgesVector += mid; } } if (openEdges) { OP.NextVB[i].Position = openEdgesVector / (float)(openEdgeCount * 4) + VertexBuffer[i].Position * 0.5f; } else { OP.NextVB[i].Position = OP.NextVB[i].Position / (float)edgeCount + edgesVector / (float)edgeCount + VertexBuffer[i].Position * (float)(edgeCount - 3); OP.NextVB[i].Position /= (float)edgeCount; } } //EdgePunkte anpassen for (int j = 0; j < OP.EdgeCount; ++j) { int V1 = (OP.EdgeConfiguration[j] & 0x0000ffff); int V2 = (OP.EdgeConfiguration[j] & 0xffff0000) >> 16; if (OP.EdgeUsage[j] < 2) { OP.NextVB[OP.EdgeID[j]].Position = vec4(0,0,0,0); OP.EdgeUsage[j] = 0; } OP.NextVB[OP.EdgeID[j]].Position += VertexBuffer[V1].Position; OP.NextVB[OP.EdgeID[j]].Position += VertexBuffer[V2].Position; OP.NextVB[OP.EdgeID[j]].Position /= (float)(OP.EdgeUsage[j] + 2); } memcpy(VertexBuffer, OP.NextVB, MaxVertexBuffer * sizeof(MeshVertex)); memcpy(IndexBuffer, OP.NextIB, MaxIndexBuffer * sizeof(unsigned short)); VertexCount = OP.NextVertexCount; IndexCount = OP.NextIndexCount; FaceCount = OP.NextFaceCount; } void Mesh::Extrude(float fExtend) { OP.Reset(); for (int i = 0; i < IndexCount;) { int count = IndexBuffer[i]; IndexBuffer[i + IndexCount] = count; ++i; for (int j = 0; j < count; ++j) { int index = IndexBuffer[i + j]; int indexNext = IndexBuffer[i + ((j + 1) % count)]; int ID = OP.MakeEdgeID(index, indexNext); if (index > indexNext) { OP.EdgeUsage[ID] += 0x80; } IndexBuffer[i + IndexCount + count - j - 1] = index + VertexCount; } i += count; } for (int i = 0; i < VertexCount; ++i) { VertexBuffer[i + VertexCount].Position = VertexBuffer[i].Position; VertexBuffer[i].Position.z -= fExtend; VertexBuffer[i + VertexCount].Position.z += fExtend; } IndexCount *= 2; FaceCount *= 2; for (int i = 0; i < OP.EdgeCount; ++i) { if ((OP.EdgeUsage[i] & 0x7f) == 1) { int iAdd = OP.EdgeUsage[i] == 1 ? 0 : VertexCount; int V1 = (OP.EdgeConfiguration[i] & 0x0000ffff); int V2 = (OP.EdgeConfiguration[i] & 0xffff0000) >> 16; IndexBuffer[IndexCount++] = 4; IndexBuffer[IndexCount++] = V1 + iAdd; iAdd = VertexCount - iAdd; IndexBuffer[IndexCount++] = V1 + iAdd; IndexBuffer[IndexCount++] = V2 + iAdd; iAdd = VertexCount - iAdd; IndexBuffer[IndexCount++] = V2 + iAdd; FaceCount += 1; } } VertexCount *= 2; } void Mesh::OpInfo::Reset() { memclear(NextVB, sizeof (NextVB)); memclear(NextIB, sizeof (NextIB)); memclear(EdgeConfiguration, sizeof (EdgeConfiguration)); memclear(EdgeUsage, sizeof (EdgeUsage)); memclear(EdgeID, sizeof (EdgeID)); NextVertexCount = 0; NextIndexCount = 0; NextFaceCount = 0; EdgeCount = 0; } int Mesh::OpInfo::MakeEdgeID (unsigned short VertexA, unsigned short VertexB) { unsigned int edgeConfiguration = VertexA > VertexB ? (VertexA << 16 | VertexB) : (VertexB << 16 | VertexA); for (int i = 0; i < EdgeCount; ++i) { if (EdgeConfiguration[i] == edgeConfiguration) { EdgeUsage[i]++; return i; } } EdgeConfiguration[EdgeCount] = edgeConfiguration; EdgeUsage[EdgeCount]++; EdgeID[EdgeCount] = NextVertexCount; NextVertexCount++; return EdgeCount++; } void Mesh::StandardSubdivide() { OP.Reset(); memcpy(OP.NextVB, VertexBuffer, VertexCount * sizeof(MeshVertex)); OP.NextVertexCount = VertexCount; int face = 0; for (int i = 0; i < IndexCount; ++face) { int count = IndexBuffer[i++]; for (int j = 0; j < count; ++j) { int prevIndex = j - 1; if (prevIndex < 0) prevIndex += count; prevIndex = IndexBuffer[i + prevIndex]; int index = IndexBuffer[i + j]; int nextIndex = IndexBuffer[i + ((j + 1) % count)]; int prevEdgeID = OP.EdgeID[OP.MakeEdgeID(prevIndex, index)]; int nextEdgeID = OP.EdgeID[OP.MakeEdgeID(index, nextIndex)]; OP.NextVB[prevEdgeID].Position = (VertexBuffer[prevIndex].Position + VertexBuffer[index].Position) / 2; OP.NextVB[nextEdgeID].Position = (VertexBuffer[nextIndex].Position + VertexBuffer[index].Position) / 2; OP.NextIB[OP.NextIndexCount + 0] = 4; OP.NextIB[OP.NextIndexCount + 1] = 0; // will be set later (face offset) OP.NextIB[OP.NextIndexCount + 2] = prevEdgeID; OP.NextIB[OP.NextIndexCount + 3] = index; OP.NextIB[OP.NextIndexCount + 4] = nextEdgeID; OP.NextIndexCount += 5; ++OP.NextFaceCount; } i += count; } int vbFaceOffset = OP.NextVertexCount; int ibIndex = 1; face = 0; for (int i = 0; i < IndexCount; ++face) { int count = IndexBuffer[i++]; vec4 fp; for (int j = 0; j < count; ++j) { fp += VertexBuffer[IndexBuffer[i + j]].Position; OP.NextIB[ibIndex] = vbFaceOffset + face; ibIndex += 5; } OP.NextVB[vbFaceOffset + face].Position = fp / (float)count; ++OP.NextVertexCount; i += count; } memcpy(VertexBuffer, OP.NextVB, MaxVertexBuffer * sizeof(MeshVertex)); memcpy(IndexBuffer, OP.NextIB, MaxIndexBuffer * sizeof(unsigned short)); VertexCount = OP.NextVertexCount; IndexCount = OP.NextIndexCount; FaceCount = OP.NextFaceCount; } void Mesh::CreateCube() { Lock(); VertexBuffer[0].Position = vec4(-2, -1, 0, 1); VertexBuffer[1].Position = vec4(-2, 1, 0, 1); VertexBuffer[2].Position = vec4(2, 1, 0, 1); VertexBuffer[3].Position = vec4(2, -1, 0, 1); VertexCount = 4; IndexBuffer[0] = 4; IndexBuffer[1] = 0; IndexBuffer[2] = 1; IndexBuffer[3] = 2; IndexBuffer[4] = 3; IndexCount = 5; FaceCount = 1; Extrude(1.0f); GenerateNormals(); Triangulate(); Homogenize(); Unlock(); } void Mesh::CreateSphere(unsigned int lod) { Lock(); VertexBuffer[0].Position = vec4(-1, -1, 0, 1); VertexBuffer[1].Position = vec4(-1, 1, 0, 1); VertexBuffer[2].Position = vec4(1, 1, 0, 1); VertexBuffer[3].Position = vec4(1, -1, 0, 1); VertexCount = 4; IndexBuffer[0] = 4; IndexBuffer[1] = 0; IndexBuffer[2] = 1; IndexBuffer[3] = 2; IndexBuffer[4] = 3; IndexCount = 5; FaceCount = 1; Extrude(1.0f); for (unsigned int i = 0; i < lod; ++i) CatmullClarkSubdivide(); GenerateNormals(); Triangulate(); Homogenize(); Unlock(); } void Mesh::Homogenize() { for (int i = 0; i < VertexCount; ++i) VertexBuffer[i].Position.w = 1; } void Mesh::Transform(const Matrix& transform) { Lock(); for (int i = 0; i < VertexCount; ++i) { VertexBuffer[i].Position = transform * VertexBuffer[i].Position; } Unlock(); }