youqiwong's picture
Upload folder using huggingface_hub
0c51b93 verified
// 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, Vec3(0.0f, 1.0f, 0.0f));
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_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;
}