You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
545 lines
16 KiB
545 lines
16 KiB
/*
|
|
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
#ifndef NV_GEOMETRY_INCLUDED
|
|
#define NV_GEOMETRY_INCLUDED
|
|
|
|
#include <nvmath/nvmath.h>
|
|
#include <stdint.h>
|
|
|
|
#include <cmath>
|
|
#include <vector>
|
|
|
|
namespace nvh {
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
\namespace nvh::geometry
|
|
The geometry namespace provides a few procedural mesh primitives
|
|
that are subdivided.
|
|
|
|
nvh::geometry::Mesh template uses the provided TVertex which must have a
|
|
constructor from nvh::geometry::Vertex. You can also use nvh::geometry::Vertex
|
|
directly.
|
|
|
|
It provides triangle indices, as well as outline line indices. The outline indices
|
|
are typical feature lines (rectangle for plane, some circles for sphere/torus).
|
|
|
|
All basic primitives are within -1,1 ranges along the axis they use
|
|
|
|
- nvh::geometry::Plane (x,y subdivision)
|
|
- nvh::geometry::Box (x,y,z subdivision, made of 6 planes)
|
|
- nvh::geometry::Sphere (lat,long subdivision)
|
|
- nvh::geometry::Torus (inner, outer circle subdivision)
|
|
- nvh::geometry::RandomMengerSponge (subdivision, tree depth, probability)
|
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
// single primitive
|
|
nvh::geometry::Box<nvh::geometry::Vertex> box(4,4,4);
|
|
|
|
// construct from primitives
|
|
|
|
\endcode
|
|
*/
|
|
|
|
|
|
namespace geometry {
|
|
struct Vertex
|
|
{
|
|
Vertex(nvmath::vec3f const& position, nvmath::vec3f const& normal, nvmath::vec2f const& texcoord)
|
|
: position(nvmath::vec4f(position, 1.0f))
|
|
, normal(nvmath::vec4f(normal, 0.0f))
|
|
, texcoord(nvmath::vec4f(texcoord, 0.0f, 0.0f))
|
|
{
|
|
}
|
|
|
|
nvmath::vec4f position;
|
|
nvmath::vec4f normal;
|
|
nvmath::vec4f texcoord;
|
|
};
|
|
|
|
|
|
// The provided TVertex must have a constructor from Vertex
|
|
|
|
template <class TVertex = Vertex>
|
|
class Mesh
|
|
{
|
|
public:
|
|
std::vector<TVertex> m_vertices;
|
|
std::vector<nvmath::vec3ui> m_indicesTriangles;
|
|
std::vector<nvmath::vec2ui> m_indicesOutline;
|
|
|
|
void append(Mesh<TVertex>& geo)
|
|
{
|
|
m_vertices.reserve(geo.m_vertices.size() + m_vertices.size());
|
|
m_indicesTriangles.reserve(geo.m_indicesTriangles.size() + m_indicesTriangles.size());
|
|
m_indicesOutline.reserve(geo.m_indicesOutline.size() + m_indicesOutline.size());
|
|
|
|
uint32_t offset = uint32_t(m_vertices.size());
|
|
|
|
for(size_t i = 0; i < geo.m_vertices.size(); i++)
|
|
{
|
|
m_vertices.push_back(geo.m_vertices[i]);
|
|
}
|
|
|
|
for(size_t i = 0; i < geo.m_indicesTriangles.size(); i++)
|
|
{
|
|
m_indicesTriangles.push_back(geo.m_indicesTriangles[i] + nvmath::vec3ui(offset));
|
|
}
|
|
|
|
for(size_t i = 0; i < geo.m_indicesOutline.size(); i++)
|
|
{
|
|
m_indicesOutline.push_back(geo.m_indicesOutline[i] + nvmath::vec2ui(offset));
|
|
}
|
|
}
|
|
|
|
void flipWinding()
|
|
{
|
|
for(size_t i = 0; i < m_indicesTriangles.size(); i++)
|
|
{
|
|
std::swap(m_indicesTriangles[i].x, m_indicesTriangles[i].z);
|
|
}
|
|
}
|
|
|
|
size_t getTriangleIndicesSize() const { return m_indicesTriangles.size() * sizeof(nvmath::vec3ui); }
|
|
|
|
uint32_t getTriangleIndicesCount() const { return (uint32_t)m_indicesTriangles.size() * 3; }
|
|
|
|
size_t getOutlineIndicesSize() const { return m_indicesOutline.size() * sizeof(nvmath::vec2ui); }
|
|
|
|
uint32_t getOutlineIndicesCount() const { return (uint32_t)m_indicesOutline.size() * 2; }
|
|
|
|
size_t getVerticesSize() const { return m_vertices.size() * sizeof(TVertex); }
|
|
|
|
uint32_t getVerticesCount() const { return (uint32_t)m_vertices.size(); }
|
|
};
|
|
|
|
template <class TVertex = Vertex>
|
|
class Plane : public Mesh<TVertex>
|
|
{
|
|
public:
|
|
static void add(Mesh<TVertex>& geo, const nvmath::mat4f& mat, int w, int h)
|
|
{
|
|
int xdim = w;
|
|
int ydim = h;
|
|
|
|
float xmove = 1.0f / (float)xdim;
|
|
float ymove = 1.0f / (float)ydim;
|
|
|
|
int width = (xdim + 1);
|
|
|
|
uint32_t vertOffset = (uint32_t)geo.m_vertices.size();
|
|
|
|
int x, y;
|
|
for(y = 0; y < ydim + 1; y++)
|
|
{
|
|
for(x = 0; x < xdim + 1; x++)
|
|
{
|
|
float xpos = ((float)x * xmove);
|
|
float ypos = ((float)y * ymove);
|
|
nvmath::vec3f pos;
|
|
nvmath::vec2f uv;
|
|
nvmath::vec3f normal;
|
|
|
|
pos[0] = (xpos - 0.5f) * 2.0f;
|
|
pos[1] = (ypos - 0.5f) * 2.0f;
|
|
pos[2] = 0;
|
|
|
|
uv[0] = xpos;
|
|
uv[1] = ypos;
|
|
|
|
normal[0] = 0.0f;
|
|
normal[1] = 0.0f;
|
|
normal[2] = 1.0f;
|
|
|
|
Vertex vert = Vertex(pos, normal, uv);
|
|
vert.position = mat * vert.position;
|
|
vert.normal = mat * vert.normal;
|
|
geo.m_vertices.push_back(TVertex(vert));
|
|
}
|
|
}
|
|
|
|
for(y = 0; y < ydim; y++)
|
|
{
|
|
for(x = 0; x < xdim; x++)
|
|
{
|
|
// upper tris
|
|
geo.m_indicesTriangles.push_back(nvmath::vec3ui((x) + (y + 1) * width + vertOffset, (x) + (y)*width + vertOffset,
|
|
(x + 1) + (y + 1) * width + vertOffset));
|
|
// lower tris
|
|
geo.m_indicesTriangles.push_back(nvmath::vec3ui((x + 1) + (y + 1) * width + vertOffset,
|
|
(x) + (y)*width + vertOffset, (x + 1) + (y)*width + vertOffset));
|
|
}
|
|
}
|
|
|
|
for(y = 0; y < ydim; y++)
|
|
{
|
|
geo.m_indicesOutline.push_back(nvmath::vec2ui((y)*width + vertOffset, (y + 1) * width + vertOffset));
|
|
}
|
|
for(y = 0; y < ydim; y++)
|
|
{
|
|
geo.m_indicesOutline.push_back(nvmath::vec2ui((y)*width + xdim + vertOffset, (y + 1) * width + xdim + vertOffset));
|
|
}
|
|
for(x = 0; x < xdim; x++)
|
|
{
|
|
geo.m_indicesOutline.push_back(nvmath::vec2ui((x) + vertOffset, (x + 1) + vertOffset));
|
|
}
|
|
for(x = 0; x < xdim; x++)
|
|
{
|
|
geo.m_indicesOutline.push_back(nvmath::vec2ui((x) + ydim * width + vertOffset, (x + 1) + ydim * width + vertOffset));
|
|
}
|
|
}
|
|
|
|
Plane(int segments = 1) { add(*this, nvmath::mat4f(1), segments, segments); }
|
|
};
|
|
|
|
template <class TVertex = Vertex>
|
|
class Box : public Mesh<TVertex>
|
|
{
|
|
public:
|
|
static void add(Mesh<TVertex>& geo, const nvmath::mat4f& mat, int w, int h, int d)
|
|
{
|
|
int configs[6][2] = {
|
|
{w, h}, {w, h},
|
|
|
|
{d, h}, {d, h},
|
|
|
|
{w, d}, {w, d},
|
|
};
|
|
|
|
for(int side = 0; side < 6; side++)
|
|
{
|
|
nvmath::mat4f matrixRot(1);
|
|
|
|
switch(side)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
matrixRot = nvmath::rotation_mat4_y(nv_pi);
|
|
break;
|
|
case 2:
|
|
matrixRot = nvmath::rotation_mat4_y(nv_pi * 0.5f);
|
|
break;
|
|
case 3:
|
|
matrixRot = nvmath::rotation_mat4_y(nv_pi * 1.5f);
|
|
break;
|
|
case 4:
|
|
matrixRot = nvmath::rotation_mat4_x(nv_pi * 0.5f);
|
|
break;
|
|
case 5:
|
|
matrixRot = nvmath::rotation_mat4_x(nv_pi * 1.5f);
|
|
break;
|
|
}
|
|
|
|
nvmath::mat4f matrixMove = nvmath::translation_mat4(0.0f, 0.0f, 1.0f);
|
|
|
|
Plane<TVertex>::add(geo, mat * matrixRot * matrixMove, configs[side][0], configs[side][1]);
|
|
}
|
|
}
|
|
|
|
Box(int segments = 1) { add(*this, nvmath::mat4f(1), segments, segments, segments); }
|
|
};
|
|
|
|
template <class TVertex = Vertex>
|
|
class Sphere : public Mesh<TVertex>
|
|
{
|
|
public:
|
|
static void add(Mesh<TVertex>& geo, const nvmath::mat4f& mat, int w, int h)
|
|
{
|
|
int xydim = w;
|
|
int zdim = h;
|
|
|
|
uint32_t vertOffset = (uint32_t)geo.m_vertices.size();
|
|
|
|
float xyshift = 1.0f / (float)xydim;
|
|
float zshift = 1.0f / (float)zdim;
|
|
int width = xydim + 1;
|
|
|
|
|
|
int index = 0;
|
|
int xy, z;
|
|
for(z = 0; z < zdim + 1; z++)
|
|
{
|
|
for(xy = 0; xy < xydim + 1; xy++)
|
|
{
|
|
nvmath::vec3f pos;
|
|
nvmath::vec3f normal;
|
|
nvmath::vec2f uv;
|
|
float curxy = xyshift * (float)xy;
|
|
float curz = zshift * (float)z;
|
|
float anglexy = curxy * nv_pi * 2.0f;
|
|
float anglez = (1.0f - curz) * nv_pi;
|
|
pos[0] = cosf(anglexy) * sinf(anglez);
|
|
pos[1] = sinf(anglexy) * sinf(anglez);
|
|
pos[2] = cosf(anglez);
|
|
normal = pos;
|
|
uv[0] = curxy;
|
|
uv[1] = curz;
|
|
|
|
Vertex vert = Vertex(pos, normal, uv);
|
|
vert.position = mat * vert.position;
|
|
vert.normal = mat * vert.normal;
|
|
|
|
geo.m_vertices.push_back(TVertex(vert));
|
|
}
|
|
}
|
|
|
|
int vertex = 0;
|
|
for(z = 0; z < zdim; z++)
|
|
{
|
|
for(xy = 0; xy < xydim; xy++, vertex++)
|
|
{
|
|
nvmath::vec3ui indices;
|
|
if(z != zdim - 1)
|
|
{
|
|
indices[2] = vertex + vertOffset;
|
|
indices[1] = vertex + width + vertOffset;
|
|
indices[0] = vertex + width + 1 + vertOffset;
|
|
geo.m_indicesTriangles.push_back(indices);
|
|
}
|
|
|
|
if(z != 0)
|
|
{
|
|
indices[2] = vertex + width + 1 + vertOffset;
|
|
indices[1] = vertex + 1 + vertOffset;
|
|
indices[0] = vertex + vertOffset;
|
|
geo.m_indicesTriangles.push_back(indices);
|
|
}
|
|
}
|
|
vertex++;
|
|
}
|
|
|
|
int middlez = zdim / 2;
|
|
|
|
for(xy = 0; xy < xydim; xy++)
|
|
{
|
|
nvmath::vec2ui indices;
|
|
indices[0] = middlez * width + xy + vertOffset;
|
|
indices[1] = middlez * width + xy + 1 + vertOffset;
|
|
geo.m_indicesOutline.push_back(indices);
|
|
}
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
int x = (xydim * i) / 4;
|
|
for(z = 0; z < zdim; z++)
|
|
{
|
|
nvmath::vec2ui indices;
|
|
indices[0] = x + width * (z) + vertOffset;
|
|
indices[1] = x + width * (z + 1) + vertOffset;
|
|
geo.m_indicesOutline.push_back(indices);
|
|
}
|
|
}
|
|
}
|
|
|
|
Sphere(int w = 16, int h = 8) { add(*this, nvmath::mat4f(1), w, h); }
|
|
};
|
|
|
|
template <class TVertex = Vertex>
|
|
class Torus : public Mesh<TVertex>
|
|
{
|
|
public:
|
|
static void add(Mesh<TVertex>& geo, const nvmath::mat4f& mat, int w, int h)
|
|
{
|
|
// Radius of inner and outer circles
|
|
float innerRadius = 0.8f;
|
|
float outerRadius = 0.2f;
|
|
|
|
unsigned int numVertices = (w + 1) * (h + 1);
|
|
|
|
float wf = (float)w;
|
|
float hf = (float)h;
|
|
|
|
float phi_step = 2.0f * nv_pi / wf;
|
|
float theta_step = 2.0f * nv_pi / hf;
|
|
|
|
// Setup vertices and normals
|
|
// Generate the Torus exactly like the sphere with rings around the origin along the latitudes.
|
|
for(unsigned int latitude = 0; latitude <= (unsigned int)w; latitude++) // theta angle
|
|
{
|
|
float theta = (float)latitude * theta_step;
|
|
float sinTheta = sinf(theta);
|
|
float cosTheta = cosf(theta);
|
|
|
|
float radius = innerRadius + outerRadius * cosTheta;
|
|
|
|
for(unsigned int longitude = 0; longitude <= (unsigned int)h; longitude++) // phi angle
|
|
{
|
|
float phi = (float)longitude * phi_step;
|
|
float sinPhi = sinf(phi);
|
|
float cosPhi = cosf(phi);
|
|
|
|
nvmath::vec3f position = nvmath::vec3f(radius * cosPhi, outerRadius * sinTheta, radius * -sinPhi);
|
|
nvmath::vec3f normal = nvmath::vec3f(cosPhi * cosTheta, sinTheta, -sinPhi * cosTheta);
|
|
nvmath::vec2f uv = nvmath::vec2f((float)longitude / wf, (float)latitude / hf);
|
|
|
|
Vertex vertex(position, normal, uv);
|
|
geo.m_vertices.push_back(TVertex(vertex));
|
|
}
|
|
}
|
|
|
|
const unsigned int columns = w + 1;
|
|
|
|
// Setup indices
|
|
for(unsigned int latitude = 0; latitude < (unsigned int)w; latitude++)
|
|
{
|
|
for(unsigned int longitude = 0; longitude < (unsigned int)h; longitude++)
|
|
{
|
|
// Indices for triangles
|
|
nvmath::vec3ui triangle1(latitude * columns + longitude, latitude * columns + longitude + 1,
|
|
(latitude + 1) * columns + longitude);
|
|
nvmath::vec3ui triangle2((latitude + 1) * columns + longitude, latitude * columns + longitude + 1,
|
|
(latitude + 1) * columns + longitude + 1);
|
|
|
|
geo.m_indicesTriangles.push_back(triangle1);
|
|
geo.m_indicesTriangles.push_back(triangle2);
|
|
}
|
|
}
|
|
|
|
// Setup outline indices
|
|
// Outline for outer ring
|
|
for(unsigned int longitude = 0; longitude < (unsigned int)w; longitude++)
|
|
{
|
|
for(unsigned int y = 0; y < 4; y++)
|
|
{
|
|
unsigned int latitude = y * (0.25 * h);
|
|
nvmath::vec2ui line(latitude * columns + longitude, latitude * columns + longitude + 1);
|
|
geo.m_indicesOutline.push_back(line);
|
|
}
|
|
}
|
|
// Outline for inner rings
|
|
for(unsigned int x = 0; x < 4; x++)
|
|
{
|
|
for(unsigned int latitude = 0; latitude < (unsigned int)h; latitude++)
|
|
{
|
|
unsigned int longitude = x * (0.25 * w);
|
|
nvmath::vec2ui line(latitude * columns + longitude, (latitude + 1) * columns + longitude);
|
|
geo.m_indicesOutline.push_back(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
Torus(int w = 16, int h = 16) { add(*this, nvmath::mat4f(1), w, h); }
|
|
};
|
|
|
|
template <class TVertex = Vertex>
|
|
class RandomMengerSponge : public Mesh<TVertex>
|
|
{
|
|
public:
|
|
static void add(Mesh<TVertex>& geo, const nvmath::mat4f& mat, int w, int h, int d, int level = 3, float probability = -1.f)
|
|
{
|
|
struct Cube
|
|
{
|
|
nvmath::vec3f m_topLeftFront;
|
|
float m_size;
|
|
|
|
void split(std::vector<Cube>& cubes)
|
|
{
|
|
float size = m_size / 3.f;
|
|
nvmath::vec3f topLeftFront = m_topLeftFront;
|
|
for(int x = 0; x < 3; x++)
|
|
{
|
|
topLeftFront[0] = m_topLeftFront[0] + static_cast<float>(x) * size;
|
|
for(int y = 0; y < 3; y++)
|
|
{
|
|
if(x == 1 && y == 1)
|
|
continue;
|
|
topLeftFront[1] = m_topLeftFront[1] + static_cast<float>(y) * size;
|
|
for(int z = 0; z < 3; z++)
|
|
{
|
|
if(x == 1 && z == 1)
|
|
continue;
|
|
if(y == 1 && z == 1)
|
|
continue;
|
|
|
|
topLeftFront[2] = m_topLeftFront[2] + static_cast<float>(z) * size;
|
|
cubes.push_back({topLeftFront, size});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void splitProb(std::vector<Cube>& cubes, float prob)
|
|
{
|
|
|
|
float size = m_size / 3.f;
|
|
nvmath::vec3f topLeftFront = m_topLeftFront;
|
|
for(int x = 0; x < 3; x++)
|
|
{
|
|
topLeftFront[0] = m_topLeftFront[0] + static_cast<float>(x) * size;
|
|
for(int y = 0; y < 3; y++)
|
|
{
|
|
topLeftFront[1] = m_topLeftFront[1] + static_cast<float>(y) * size;
|
|
for(int z = 0; z < 3; z++)
|
|
{
|
|
float sample = rand() / static_cast<float>(RAND_MAX);
|
|
if(sample > prob)
|
|
continue;
|
|
topLeftFront[2] = m_topLeftFront[2] + static_cast<float>(z) * size;
|
|
cubes.push_back({topLeftFront, size});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Cube cube = {nvmath::vec3f(-0.25, -0.25, -0.25), 0.5f};
|
|
//Cube cube = { nvmath::vec3f(-25, -25, -25), 50.f };
|
|
//Cube cube = { nvmath::vec3f(-40, -40, -40), 10.f };
|
|
|
|
std::vector<Cube> cubes1 = {cube};
|
|
std::vector<Cube> cubes2 = {};
|
|
|
|
auto previous = &cubes1;
|
|
auto next = &cubes2;
|
|
|
|
for(int i = 0; i < level; i++)
|
|
{
|
|
size_t cubeCount = previous->size();
|
|
for(Cube& c : *previous)
|
|
{
|
|
if(probability < 0.f)
|
|
c.split(*next);
|
|
else
|
|
c.splitProb(*next, probability);
|
|
}
|
|
auto temp = previous;
|
|
previous = next;
|
|
next = temp;
|
|
next->clear();
|
|
}
|
|
for(Cube& c : *previous)
|
|
{
|
|
nvmath::mat4f matrixMove = nvmath::translation_mat4(c.m_topLeftFront);
|
|
nvmath::mat4f matrixScale = nvmath::scale_mat4(nvmath::vec3f(c.m_size));
|
|
;
|
|
Box<TVertex>::add(geo, matrixMove * matrixScale, 1, 1, 1);
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace geometry
|
|
} // namespace nvh
|
|
|
|
|
|
#endif
|
|
|