aboutsummaryrefslogtreecommitdiff
path: root/core/mesh.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/mesh.cpp')
-rw-r--r--core/mesh.cpp968
1 files changed, 968 insertions, 0 deletions
diff --git a/core/mesh.cpp b/core/mesh.cpp
new file mode 100644
index 0000000..371e664
--- /dev/null
+++ b/core/mesh.cpp
@@ -0,0 +1,968 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2013-2016 NVIDIA Corporation. All rights reserved.
+
+#include "mesh.h"
+#include "platform.h"
+
+#include <map>
+#include <fstream>
+#include <iostream>
+
+using namespace std;
+
+void Mesh::DuplicateVertex(uint32_t i)
+{
+ assert(m_positions.size() > i);
+ m_positions.push_back(m_positions[i]);
+
+ if (m_normals.size() > i)
+ m_normals.push_back(m_normals[i]);
+
+ if (m_colours.size() > i)
+ m_colours.push_back(m_colours[i]);
+
+ if (m_texcoords[0].size() > i)
+ m_texcoords[0].push_back(m_texcoords[0][i]);
+
+ if (m_texcoords[1].size() > i)
+ m_texcoords[1].push_back(m_texcoords[1][i]);
+
+}
+
+void Mesh::Normalize(float s)
+{
+ Vec3 lower, upper;
+ GetBounds(lower, upper);
+ Vec3 edges = upper-lower;
+
+ Transform(TranslationMatrix(Point3(-lower)));
+
+ float maxEdge = max(edges.x, max(edges.y, edges.z));
+ Transform(ScaleMatrix(s/maxEdge));
+}
+
+void Mesh::CalculateNormals()
+{
+ m_normals.resize(0);
+ m_normals.resize(m_positions.size());
+
+ int numTris = int(GetNumFaces());
+
+ for (int i=0; i < numTris; ++i)
+ {
+ int a = m_indices[i*3+0];
+ int b = m_indices[i*3+1];
+ int c = m_indices[i*3+2];
+
+ Vec3 n = Cross(m_positions[b]-m_positions[a], m_positions[c]-m_positions[a]);
+
+ m_normals[a] += n;
+ m_normals[b] += n;
+ m_normals[c] += n;
+ }
+
+ int numVertices = int(GetNumVertices());
+
+ for (int i=0; i < numVertices; ++i)
+ m_normals[i] = ::Normalize(m_normals[i]);
+}
+
+namespace
+{
+
+ enum PlyFormat
+ {
+ eAscii,
+ eBinaryBigEndian
+ };
+
+ template <typename T>
+ T PlyRead(ifstream& s, PlyFormat format)
+ {
+ T data = eAscii;
+
+ switch (format)
+ {
+ case eAscii:
+ {
+ s >> data;
+ break;
+ }
+ case eBinaryBigEndian:
+ {
+ char c[sizeof(T)];
+ s.read(c, sizeof(T));
+ reverse(c, c+sizeof(T));
+ data = *(T*)c;
+ break;
+ }
+ default:
+ assert(0);
+ }
+
+ return data;
+ }
+
+} // namespace anonymous
+
+
+Mesh* ImportMesh(const char* path)
+{
+ std::string ext = GetExtension(path);
+
+ Mesh* mesh = NULL;
+
+ if (ext == "ply")
+ mesh = ImportMeshFromPly(path);
+ else if (ext == "obj")
+ mesh = ImportMeshFromObj(path);
+
+
+ return mesh;
+}
+
+Mesh* ImportMeshFromBin(const char* path)
+{
+ double start = GetSeconds();
+
+ FILE* f = fopen(path, "rb");
+
+ if (f)
+ {
+ int numVertices;
+ int numIndices;
+
+ size_t len;
+ len = fread(&numVertices, sizeof(numVertices), 1, f);
+ len = fread(&numIndices, sizeof(numIndices), 1, f);
+
+ Mesh* m = new Mesh();
+ m->m_positions.resize(numVertices);
+ m->m_normals.resize(numVertices);
+ m->m_indices.resize(numIndices);
+
+ len = fread(&m->m_positions[0], sizeof(Vec3)*numVertices, 1, f);
+ len = fread(&m->m_normals[0], sizeof(Vec3)*numVertices, 1, f);
+ len = fread(&m->m_indices[0], sizeof(int)*numIndices, 1, f);
+
+ (void)len;
+
+ fclose(f);
+
+ double end = GetSeconds();
+
+ printf("Imported mesh %s in %f ms\n", path, (end-start)*1000.0f);
+
+ return m;
+ }
+
+ return NULL;
+}
+
+void ExportMeshToBin(const char* path, const Mesh* m)
+{
+ FILE* f = fopen(path, "wb");
+
+ if (f)
+ {
+ int numVertices = int(m->m_positions.size());
+ int numIndices = int(m->m_indices.size());
+
+ fwrite(&numVertices, sizeof(numVertices), 1, f);
+ fwrite(&numIndices, sizeof(numIndices), 1, f);
+
+ // write data blocks
+ fwrite(&m->m_positions[0], sizeof(Vec3)*numVertices, 1, f);
+ fwrite(&m->m_normals[0], sizeof(Vec3)*numVertices, 1, f);
+ fwrite(&m->m_indices[0], sizeof(int)*numIndices, 1, f);
+
+ fclose(f);
+ }
+}
+
+Mesh* ImportMeshFromPly(const char* path)
+{
+ ifstream file(path, ios_base::in | ios_base::binary);
+
+ if (!file)
+ return NULL;
+
+ // some scratch memory
+ const uint32_t kMaxLineLength = 1024;
+ char buffer[kMaxLineLength];
+
+ //double startTime = GetSeconds();
+
+ file >> buffer;
+ if (strcmp(buffer, "ply") != 0)
+ return NULL;
+
+ PlyFormat format = eAscii;
+
+ uint32_t numFaces = 0;
+ uint32_t numVertices = 0;
+
+ const uint32_t kMaxProperties = 16;
+ uint32_t numProperties = 0;
+ float properties[kMaxProperties];
+
+ bool vertexElement = false;
+
+ while (file)
+ {
+ file >> buffer;
+
+ if (strcmp(buffer, "element") == 0)
+ {
+ file >> buffer;
+
+ if (strcmp(buffer, "face") == 0)
+ {
+ vertexElement = false;
+ file >> numFaces;
+ }
+
+ else if (strcmp(buffer, "vertex") == 0)
+ {
+ vertexElement = true;
+ file >> numVertices;
+ }
+ }
+ else if (strcmp(buffer, "format") == 0)
+ {
+ file >> buffer;
+ if (strcmp(buffer, "ascii") == 0)
+ {
+ format = eAscii;
+ }
+ else if (strcmp(buffer, "binary_big_endian") == 0)
+ {
+ format = eBinaryBigEndian;
+ }
+ else
+ {
+ printf("Ply: unknown format\n");
+ return NULL;
+ }
+ }
+ else if (strcmp(buffer, "property") == 0)
+ {
+ if (vertexElement)
+ ++numProperties;
+ }
+ else if (strcmp(buffer, "end_header") == 0)
+ {
+ break;
+ }
+ }
+
+ // eat newline
+ char nl;
+ file.read(&nl, 1);
+
+ // debug
+#if ENABLE_VERBOSE_OUTPUT
+ printf ("Loaded mesh: %s numFaces: %d numVertices: %d format: %d numProperties: %d\n", path, numFaces, numVertices, format, numProperties);
+#endif
+
+ Mesh* mesh = new Mesh;
+
+ mesh->m_positions.resize(numVertices);
+ mesh->m_normals.resize(numVertices);
+ mesh->m_colours.resize(numVertices, Colour(1.0f, 1.0f, 1.0f, 1.0f));
+
+ mesh->m_indices.reserve(numFaces*3);
+
+ // read vertices
+ for (uint32_t v=0; v < numVertices; ++v)
+ {
+ for (uint32_t i=0; i < numProperties; ++i)
+ {
+ properties[i] = PlyRead<float>(file, format);
+ }
+
+ mesh->m_positions[v] = Point3(properties[0], properties[1], properties[2]);
+ mesh->m_normals[v] = Vector3(0.0f, 0.0f, 0.0f);
+ }
+
+ // read indices
+ for (uint32_t f=0; f < numFaces; ++f)
+ {
+ uint32_t numIndices = (format == eAscii)?PlyRead<uint32_t>(file, format):PlyRead<uint8_t>(file, format);
+ uint32_t indices[4];
+
+ for (uint32_t i=0; i < numIndices; ++i)
+ {
+ indices[i] = PlyRead<uint32_t>(file, format);
+ }
+
+ switch (numIndices)
+ {
+ case 3:
+ mesh->m_indices.push_back(indices[0]);
+ mesh->m_indices.push_back(indices[1]);
+ mesh->m_indices.push_back(indices[2]);
+ break;
+ case 4:
+ mesh->m_indices.push_back(indices[0]);
+ mesh->m_indices.push_back(indices[1]);
+ mesh->m_indices.push_back(indices[2]);
+
+ mesh->m_indices.push_back(indices[2]);
+ mesh->m_indices.push_back(indices[3]);
+ mesh->m_indices.push_back(indices[0]);
+ break;
+
+ default:
+ assert(!"invalid number of indices, only support tris and quads");
+ break;
+ };
+
+ // calculate vertex normals as we go
+ Point3& v0 = mesh->m_positions[indices[0]];
+ Point3& v1 = mesh->m_positions[indices[1]];
+ Point3& v2 = mesh->m_positions[indices[2]];
+
+ Vector3 n = SafeNormalize(Cross(v1-v0, v2-v0), Vector3(0.0f, 1.0f, 0.0f));
+
+ for (uint32_t i=0; i < numIndices; ++i)
+ {
+ mesh->m_normals[indices[i]] += n;
+ }
+ }
+
+ for (uint32_t i=0; i < numVertices; ++i)
+ {
+ mesh->m_normals[i] = SafeNormalize(mesh->m_normals[i], Vector3(0.0f, 1.0f, 0.0f));
+ }
+
+ //cout << "Imported mesh " << path << " in " << (GetSeconds()-startTime)*1000.f << "ms" << endl;
+
+ return mesh;
+
+}
+
+// map of Material name to Material
+struct VertexKey
+{
+ VertexKey() : v(0), vt(0), vn(0) {}
+
+ uint32_t v, vt, vn;
+
+ bool operator == (const VertexKey& rhs) const
+ {
+ return v == rhs.v && vt == rhs.vt && vn == rhs.vn;
+ }
+
+ bool operator < (const VertexKey& rhs) const
+ {
+ if (v != rhs.v)
+ return v < rhs.v;
+ else if (vt != rhs.vt)
+ return vt < rhs.vt;
+ else
+ return vn < rhs.vn;
+ }
+};
+
+Mesh* ImportMeshFromObj(const char* path)
+{
+ ifstream file(path);
+
+ if (!file)
+ return NULL;
+
+ Mesh* m = new Mesh();
+
+ vector<Point3> positions;
+ vector<Vector3> normals;
+ vector<Vector2> texcoords;
+ vector<Vector3> colors;
+ vector<uint32_t>& indices = m->m_indices;
+
+ //typedef unordered_map<VertexKey, uint32_t, MemoryHash<VertexKey> > VertexMap;
+ typedef map<VertexKey, uint32_t> VertexMap;
+ VertexMap vertexLookup;
+
+ // some scratch memory
+ const uint32_t kMaxLineLength = 1024;
+ char buffer[kMaxLineLength];
+
+ //double startTime = GetSeconds();
+
+ while (file)
+ {
+ file >> buffer;
+
+ if (strcmp(buffer, "vn") == 0)
+ {
+ // normals
+ float x, y, z;
+ file >> x >> y >> z;
+
+ normals.push_back(Vector3(x, y, z));
+ }
+ else if (strcmp(buffer, "vt") == 0)
+ {
+ // texture coords
+ float u, v;
+ file >> u >> v;
+
+ texcoords.push_back(Vector2(u, v));
+ }
+ else if (buffer[0] == 'v')
+ {
+ // positions
+ float x, y, z;
+ file >> x >> y >> z;
+
+ positions.push_back(Point3(x, y, z));
+ }
+ else if (buffer[0] == 's' || buffer[0] == 'g' || buffer[0] == 'o')
+ {
+ // ignore smoothing groups, groups and objects
+ char linebuf[256];
+ file.getline(linebuf, 256);
+ }
+ else if (strcmp(buffer, "mtllib") == 0)
+ {
+ // ignored
+ std::string MaterialFile;
+ file >> MaterialFile;
+ }
+ else if (strcmp(buffer, "usemtl") == 0)
+ {
+ // read Material name
+ std::string materialName;
+ file >> materialName;
+ }
+ else if (buffer[0] == 'f')
+ {
+ // faces
+ uint32_t faceIndices[4];
+ uint32_t faceIndexCount = 0;
+
+ for (int i=0; i < 4; ++i)
+ {
+ VertexKey key;
+
+ file >> key.v;
+
+ if (!file.eof())
+ {
+ // failed to read another index continue on
+ if (file.fail())
+ {
+ file.clear();
+ break;
+ }
+
+ if (file.peek() == '/')
+ {
+ file.ignore();
+
+ if (file.peek() != '/')
+ {
+ file >> key.vt;
+ }
+
+ if (file.peek() == '/')
+ {
+ file.ignore();
+ file >> key.vn;
+ }
+ }
+
+ // find / add vertex, index
+ VertexMap::iterator iter = vertexLookup.find(key);
+
+ if (iter != vertexLookup.end())
+ {
+ faceIndices[faceIndexCount++] = iter->second;
+ }
+ else
+ {
+ // add vertex
+ uint32_t newIndex = uint32_t(m->m_positions.size());
+ faceIndices[faceIndexCount++] = newIndex;
+
+ vertexLookup.insert(make_pair(key, newIndex));
+
+ // push back vertex data
+ assert(key.v > 0);
+
+ m->m_positions.push_back(positions[key.v-1]);
+
+ // obj format doesn't support mesh colours so add default value
+ m->m_colours.push_back(Colour(1.0f, 1.0f, 1.0f));
+
+ // normal [optional]
+ if (key.vn)
+ {
+ m->m_normals.push_back(normals[key.vn-1]);
+ }
+
+ // texcoord [optional]
+ if (key.vt)
+ {
+ m->m_texcoords[0].push_back(texcoords[key.vt-1]);
+ }
+ }
+ }
+ }
+
+ if (faceIndexCount == 3)
+ {
+ // a triangle
+ indices.insert(indices.end(), faceIndices, faceIndices+3);
+ }
+ else if (faceIndexCount == 4)
+ {
+ // a quad, triangulate clockwise
+ indices.insert(indices.end(), faceIndices, faceIndices+3);
+
+ indices.push_back(faceIndices[2]);
+ indices.push_back(faceIndices[3]);
+ indices.push_back(faceIndices[0]);
+ }
+ else
+ {
+ cout << "Face with more than 4 vertices are not supported" << endl;
+ }
+
+ }
+ else if (buffer[0] == '#')
+ {
+ // comment
+ char linebuf[256];
+ file.getline(linebuf, 256);
+ }
+ }
+
+ // calculate normals if none specified in file
+ m->m_normals.resize(m->m_positions.size());
+
+ const uint32_t numFaces = uint32_t(indices.size())/3;
+ for (uint32_t i=0; i < numFaces; ++i)
+ {
+ uint32_t a = indices[i*3+0];
+ uint32_t b = indices[i*3+1];
+ uint32_t c = indices[i*3+2];
+
+ Point3& v0 = m->m_positions[a];
+ Point3& v1 = m->m_positions[b];
+ Point3& v2 = m->m_positions[c];
+
+ Vector3 n = SafeNormalize(Cross(v1-v0, v2-v0), Vector3(0.0f, 1.0f, 0.0f));
+
+ m->m_normals[a] += n;
+ m->m_normals[b] += n;
+ m->m_normals[c] += n;
+ }
+
+ for (uint32_t i=0; i < m->m_normals.size(); ++i)
+ {
+ m->m_normals[i] = SafeNormalize(m->m_normals[i], Vector3(0.0f, 1.0f, 0.0f));
+ }
+
+ //cout << "Imported mesh " << path << " in " << (GetSeconds()-startTime)*1000.f << "ms" << endl;
+
+ return m;
+}
+
+void ExportToObj(const char* path, const Mesh& m)
+{
+ ofstream file(path);
+
+ if (!file)
+ return;
+
+ file << "# positions" << endl;
+
+ for (uint32_t i=0; i < m.m_positions.size(); ++i)
+ {
+ Point3 v = m.m_positions[i];
+ file << "v " << v.x << " " << v.y << " " << v.z << endl;
+ }
+
+ file << "# texcoords" << endl;
+
+ for (uint32_t i=0; i < m.m_texcoords[0].size(); ++i)
+ {
+ Vec2 t = m.m_texcoords[0][i];
+ file << "vt " << t.x << " " << t.y << endl;
+ }
+
+ file << "# normals" << endl;
+
+ for (uint32_t i=0; i < m.m_normals.size(); ++i)
+ {
+ Vec3 n = m.m_normals[0][i];
+ file << "vn " << n.x << " " << n.y << " " << n.z << endl;
+ }
+
+ file << "# faces" << endl;
+
+ for (uint32_t i=0; i < m.m_indices.size()/3; ++i)
+ {
+ uint32_t j = i+1;
+
+ // no sharing, assumes there is a unique position, texcoord and normal for each vertex
+ file << "f " << j << "/" << j << "/" << j << endl;
+ }
+}
+
+void Mesh::AddMesh(const Mesh& m)
+{
+ uint32_t offset = uint32_t(m_positions.size());
+
+ // add new vertices
+ m_positions.insert(m_positions.end(), m.m_positions.begin(), m.m_positions.end());
+ m_normals.insert(m_normals.end(), m.m_normals.begin(), m.m_normals.end());
+ m_colours.insert(m_colours.end(), m.m_colours.begin(), m.m_colours.end());
+
+ // add new indices with offset
+ for (uint32_t i=0; i < m.m_indices.size(); ++i)
+ {
+ m_indices.push_back(m.m_indices[i]+offset);
+ }
+}
+
+void Mesh::Transform(const Matrix44& m)
+{
+ for (uint32_t i=0; i < m_positions.size(); ++i)
+ {
+ m_positions[i] = m*m_positions[i];
+ m_normals[i] = m*m_normals[i];
+ }
+}
+
+void Mesh::GetBounds(Vector3& outMinExtents, Vector3& outMaxExtents) const
+{
+ Point3 minExtents(FLT_MAX);
+ Point3 maxExtents(-FLT_MAX);
+
+ // calculate face bounds
+ for (uint32_t i=0; i < m_positions.size(); ++i)
+ {
+ const Point3& a = m_positions[i];
+
+ minExtents = Min(a, minExtents);
+ maxExtents = Max(a, maxExtents);
+ }
+
+ outMinExtents = Vector3(minExtents);
+ outMaxExtents = Vector3(maxExtents);
+}
+
+Mesh* CreateTriMesh(float size, float y)
+{
+ uint32_t indices[] = { 0, 1, 2 };
+ Point3 positions[3];
+ Vector3 normals[3];
+
+ positions[0] = Point3(-size, y, size);
+ positions[1] = Point3(size, y, size);
+ positions[2] = Point3(size, y, -size);
+
+ normals[0] = Vector3(0.0f, 1.0f, 0.0f);
+ normals[1] = Vector3(0.0f, 1.0f, 0.0f);
+ normals[2] = Vector3(0.0f, 1.0f, 0.0f);
+
+ Mesh* m = new Mesh();
+ m->m_indices.insert(m->m_indices.begin(), indices, indices + 3);
+ m->m_positions.insert(m->m_positions.begin(), positions, positions + 3);
+ m->m_normals.insert(m->m_normals.begin(), normals, normals + 3);
+
+ return m;
+}
+
+Mesh* CreateCubeMesh()
+{
+ const Point3 vertices[24] =
+ {
+ Point3(0.5, 0.5, 0.5),
+ Point3(-0.5, 0.5, 0.5),
+ Point3(0.5, -0.5, 0.5),
+ Point3(-0.5, -0.5, 0.5),
+ Point3(0.5, 0.5, -0.5),
+ Point3(-0.5, 0.5, -0.5),
+ Point3(0.5, -0.5, -0.5),
+ Point3(-0.5, -0.5, -0.5),
+ Point3(0.5, 0.5, 0.5),
+ Point3(0.5, -0.5, 0.5),
+ Point3(0.5, 0.5, 0.5),
+ Point3(0.5, 0.5, -0.5),
+ Point3(-0.5, 0.5, 0.5),
+ Point3(-0.5, 0.5, -0.5),
+ Point3(0.5, -0.5, -0.5),
+ Point3(0.5, 0.5, -0.5),
+ Point3(-0.5, -0.5, -0.5),
+ Point3(0.5, -0.5, -0.5),
+ Point3(-0.5, -0.5, 0.5),
+ Point3(0.5, -0.5, 0.5),
+ Point3(-0.5, -0.5, -0.5),
+ Point3(-0.5, -0.5, 0.5),
+ Point3(-0.5, 0.5, -0.5),
+ Point3(-0.5, 0.5, 0.5)
+ };
+
+ const Vec3 normals[24] =
+ {
+ Vec3(0.0f, 0.0f, 1.0f),
+ Vec3(0.0f, 0.0f, 1.0f),
+ Vec3(0.0f, 0.0f, 1.0f),
+ Vec3(0.0f, 0.0f, 1.0f),
+ Vec3(1.0f, 0.0f, 0.0f),
+ Vec3(0.0f, 1.0f, 0.0f),
+ Vec3(1.0f, 0.0f, 0.0f),
+ Vec3(0.0f, 0.0f, -1.0f),
+ Vec3(1.0f, 0.0f, 0.0f),
+ Vec3(1.0f, 0.0f, 0.0f),
+ Vec3(0.0f, 1.0f, 0.0f),
+ Vec3(0.0f, 1.0f, 0.0f),
+ Vec3(0.0f, 1.0f, 0.0f),
+ Vec3(0.0f, 0.0f, -1.0f),
+ Vec3(0.0f, 0.0f, -1.0f),
+ Vec3(-0.0f, -0.0f, -1.0f),
+ Vec3(0.0f, -1.0f, 0.0f),
+ Vec3(0.0f, -1.0f, 0.0f),
+ Vec3(0.0f, -1.0f, 0.0f),
+ Vec3(-0.0f, -1.0f, -0.0f),
+ Vec3(-1.0f, 0.0f, 0.0f),
+ Vec3(-1.0f, 0.0f, 0.0f),
+ Vec3(-1.0f, 0.0f, 0.0f),
+ Vec3(-1.0f, -0.0f, -0.0f)
+ };
+
+ const int indices[36] =
+ {
+ 0, 1, 2,
+ 3, 2, 1,
+ 8, 9, 4,
+ 6, 4, 9,
+ 10, 11, 12,
+ 5, 12, 11,
+ 7, 13, 14,
+ 15, 14, 13,
+ 16, 17, 18,
+ 19, 18, 17,
+ 20, 21, 22,
+ 23, 22, 21
+ };
+
+ Mesh* m = new Mesh();
+ m->m_positions.assign(vertices, vertices+24);
+ m->m_normals.assign(normals, normals+24);
+ m->m_indices.assign(indices, indices+36);
+
+ return m;
+
+}
+
+Mesh* CreateQuadMesh(float size, float y)
+{
+ uint32_t indices[] = { 0, 1, 2, 2, 3, 0 };
+ Point3 positions[4];
+ Vector3 normals[4];
+
+ positions[0] = Point3(-size, y, size);
+ positions[1] = Point3(size, y, size);
+ positions[2] = Point3(size, y, -size);
+ positions[3] = Point3(-size, y, -size);
+
+ normals[0] = Vector3(0.0f, 1.0f, 0.0f);
+ normals[1] = Vector3(0.0f, 1.0f, 0.0f);
+ normals[2] = Vector3(0.0f, 1.0f, 0.0f);
+ normals[3] = Vector3(0.0f, 1.0f, 0.0f);
+
+ Mesh* m = new Mesh();
+ m->m_indices.insert(m->m_indices.begin(), indices, indices+6);
+ m->m_positions.insert(m->m_positions.begin(), positions, positions+4);
+ m->m_normals.insert(m->m_normals.begin(), normals, normals+4);
+
+ return m;
+}
+
+Mesh* CreateDiscMesh(float radius, uint32_t segments)
+{
+ const uint32_t numVerts = 1 + segments;
+
+ Mesh* m = new Mesh();
+ m->m_positions.resize(numVerts);
+ m->m_normals.resize(numVerts);
+
+ m->m_positions[0] = Point3(0.0f);
+ m->m_positions[1] = Point3(0.0f, 0.0f, radius);
+
+ for (uint32_t i=1; i <= segments; ++i)
+ {
+ uint32_t nextVert = (i+1)%numVerts;
+
+ if (nextVert == 0)
+ nextVert = 1;
+
+ m->m_positions[nextVert] = Point3(radius*Sin((float(i)/segments)*k2Pi), 0.0f, radius*Cos((float(i)/segments)*k2Pi));
+ m->m_normals[nextVert] = Vector3(0.0f, 1.0f, 0.0f);
+
+ m->m_indices.push_back(0);
+ m->m_indices.push_back(i);
+ m->m_indices.push_back(nextVert);
+ }
+
+ return m;
+}
+
+Mesh* CreateTetrahedron(float ground, float height)
+{
+ Mesh* m = new Mesh();
+
+ const float dimValue = 1.0f / sqrtf(2.0f);
+ const Point3 vertices[4] =
+ {
+ Point3(-1.0f, ground, -dimValue),
+ Point3(1.0f, ground, -dimValue),
+ Point3(0.0f, ground + height, dimValue),
+ Point3(0.0f, ground, dimValue)
+ };
+
+ const int indices[12] =
+ {
+ //winding order is counter-clockwise
+ 0, 2, 1,
+ 2, 3, 1,
+ 2, 0, 3,
+ 3, 0, 1
+ };
+
+ m->m_positions.assign(vertices, vertices+4);
+ m->m_indices.assign(indices, indices+12);
+
+ m->CalculateNormals();
+
+ return m;
+}
+
+
+Mesh* CreateSphere(int slices, int segments, float radius)
+{
+ float dTheta = kPi / slices;
+ float dPhi = k2Pi / segments;
+
+ int vertsPerRow = segments + 1;
+
+ Mesh* mesh = new Mesh();
+
+ for (int i = 0; i <= slices; ++i)
+ {
+ float theta = dTheta*i;
+
+ for (int j = 0; j <= segments; ++j)
+ {
+ float phi = dPhi*j;
+
+ float x = sinf(theta)*cosf(phi);
+ float y = cosf(theta);
+ float z = sinf(theta)*sinf(phi);
+
+ mesh->m_positions.push_back(Point3(x, y, z)*radius);
+ mesh->m_normals.push_back(Vec3(x, y, z));
+
+ if (i > 0 && j > 0)
+ {
+ int a = i*vertsPerRow + j;
+ int b = (i - 1)*vertsPerRow + j;
+ int c = (i - 1)*vertsPerRow + j - 1;
+ int d = i*vertsPerRow + j - 1;
+
+ // add a quad for this slice
+ mesh->m_indices.push_back(b);
+ mesh->m_indices.push_back(a);
+ mesh->m_indices.push_back(d);
+
+ mesh->m_indices.push_back(b);
+ mesh->m_indices.push_back(d);
+ mesh->m_indices.push_back(c);
+ }
+ }
+ }
+
+ return mesh;
+}
+
+Mesh* CreateCapsule(int slices, int segments, float radius, float halfHeight)
+{
+ float dTheta = kPi / (slices * 2);
+ float dPhi = k2Pi / segments;
+
+ int vertsPerRow = segments + 1;
+
+ Mesh* mesh = new Mesh();
+
+ float theta = 0.0f;
+
+ for (int i = 0; i <= 2 * slices + 1; ++i)
+ {
+ for (int j = 0; j <= segments; ++j)
+ {
+ float phi = dPhi*j;
+
+ float x = sinf(theta)*cosf(phi);
+ float y = cosf(theta);
+ float z = sinf(theta)*sinf(phi);
+
+ // add y offset based on which hemisphere we're in
+ float yoffset = (i < slices) ? halfHeight : -halfHeight;
+
+ mesh->m_positions.push_back(Point3(x, y, z)*radius + Vec3(0.0f, yoffset, 0.0f));
+ mesh->m_normals.push_back(Vec3(x, y, z));
+
+ if (i > 0 && j > 0)
+ {
+ int a = i*vertsPerRow + j;
+ int b = (i - 1)*vertsPerRow + j;
+ int c = (i - 1)*vertsPerRow + j - 1;
+ int d = i*vertsPerRow + j - 1;
+
+ // add a quad for this slice
+ mesh->m_indices.push_back(b);
+ mesh->m_indices.push_back(a);
+ mesh->m_indices.push_back(d);
+
+ mesh->m_indices.push_back(b);
+ mesh->m_indices.push_back(d);
+ mesh->m_indices.push_back(c);
+ }
+ }
+
+ // don't update theta for the middle slice
+ if (i != slices)
+ theta += dTheta;
+ }
+
+ return mesh;
+}