mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 07:22:43 +02:00
More shapes
This commit is contained in:
parent
cd1e910844
commit
2c703e963c
16 changed files with 895 additions and 780 deletions
|
@ -30,7 +30,7 @@ static GeometryCache::Shape MAPPING[entity::NUM_SHAPES] = {
|
|||
GeometryCache::Cube,
|
||||
GeometryCache::Sphere,
|
||||
GeometryCache::Tetrahedron,
|
||||
GeometryCache::Octahetron,
|
||||
GeometryCache::Octahedron,
|
||||
GeometryCache::Dodecahedron,
|
||||
GeometryCache::Icosahedron,
|
||||
GeometryCache::Torus,
|
||||
|
@ -93,7 +93,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
|
|||
if (!success) {
|
||||
return;
|
||||
}
|
||||
if (_shape != entity::Cube) {
|
||||
if (_shape == entity::Sphere) {
|
||||
modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
}
|
||||
batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace entity {
|
|||
"Cube",
|
||||
"Sphere",
|
||||
"Tetrahedron",
|
||||
"Octahetron",
|
||||
"Octahedron",
|
||||
"Dodecahedron",
|
||||
"Icosahedron",
|
||||
"Torus",
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace entity {
|
|||
Cube,
|
||||
Sphere,
|
||||
Tetrahedron,
|
||||
Octahetron,
|
||||
Octahedron,
|
||||
Dodecahedron,
|
||||
Icosahedron,
|
||||
Torus,
|
||||
|
|
|
@ -51,8 +51,8 @@ static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT;
|
|||
|
||||
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals
|
||||
static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);
|
||||
static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16;
|
||||
static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16);
|
||||
static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT32;
|
||||
static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint32);
|
||||
|
||||
void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VertexVector& vertices) {
|
||||
vertexBuffer->append(vertices);
|
||||
|
@ -112,102 +112,7 @@ void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count
|
|||
}
|
||||
}
|
||||
|
||||
// The golden ratio
|
||||
static const float PHI = 1.61803398874f;
|
||||
|
||||
const VertexVector& icosahedronVertices() {
|
||||
static const float a = 1;
|
||||
static const float b = PHI / 2.0f;
|
||||
|
||||
static const VertexVector vertices{ //
|
||||
vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), //
|
||||
vec3(0, b, a), vec3(b, a, 0), vec3(-b, a, 0), //
|
||||
vec3(0, b, a), vec3(-a, 0, b), vec3(0, -b, a), //
|
||||
vec3(0, b, a), vec3(0, -b, a), vec3(a, 0, b), //
|
||||
vec3(0, b, -a), vec3(a, 0, -b), vec3(0, -b, -a),//
|
||||
vec3(0, b, -a), vec3(0, -b, -a), vec3(-a, 0, -b), //
|
||||
vec3(0, -b, a), vec3(-b, -a, 0), vec3(b, -a, 0), //
|
||||
vec3(0, -b, -a), vec3(b, -a, 0), vec3(-b, -a, 0), //
|
||||
vec3(-b, a, 0), vec3(-a, 0, -b), vec3(-a, 0, b), //
|
||||
vec3(-b, -a, 0), vec3(-a, 0, b), vec3(-a, 0, -b), //
|
||||
vec3(b, a, 0), vec3(a, 0, b), vec3(a, 0, -b), //
|
||||
vec3(b, -a, 0), vec3(a, 0, -b), vec3(a, 0, b), //
|
||||
vec3(0, b, a), vec3(-b, a, 0), vec3(-a, 0, b), //
|
||||
vec3(0, b, a), vec3(a, 0, b), vec3(b, a, 0), //
|
||||
vec3(0, b, -a), vec3(-a, 0, -b), vec3(-b, a, 0), //
|
||||
vec3(0, b, -a), vec3(b, a, 0), vec3(a, 0, -b), //
|
||||
vec3(0, -b, -a), vec3(-b, -a, 0), vec3(-a, 0, -b), //
|
||||
vec3(0, -b, -a), vec3(a, 0, -b), vec3(b, -a, 0), //
|
||||
vec3(0, -b, a), vec3(-a, 0, b), vec3(-b, -a, 0), //
|
||||
vec3(0, -b, a), vec3(b, -a, 0), vec3(a, 0, b)
|
||||
}; //
|
||||
return vertices;
|
||||
}
|
||||
|
||||
const VertexVector& tetrahedronVertices() {
|
||||
static const auto A = vec3(1, 1, 1);
|
||||
static const auto B = vec3(1, -1, -1);
|
||||
static const auto C = vec3(-1, 1, -1);
|
||||
static const auto D = vec3(-1, -1, 1);
|
||||
static const VertexVector vertices{
|
||||
A, B, C,
|
||||
D, B, A,
|
||||
C, D, A,
|
||||
C, B, D,
|
||||
};
|
||||
return vertices;
|
||||
}
|
||||
|
||||
static const size_t TESSELTATION_MULTIPLIER = 4;
|
||||
static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3;
|
||||
static const size_t VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER = 2;
|
||||
|
||||
|
||||
VertexVector tesselate(const VertexVector& startingTriangles, int count) {
|
||||
VertexVector triangles = startingTriangles;
|
||||
if (0 != (triangles.size() % 3)) {
|
||||
throw std::runtime_error("Bad number of vertices for tesselation");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < triangles.size(); ++i) {
|
||||
triangles[i] = glm::normalize(triangles[i]);
|
||||
}
|
||||
|
||||
VertexVector newTriangles;
|
||||
while (count) {
|
||||
newTriangles.clear();
|
||||
// Tesselation takes one triangle and makes it into 4 triangles
|
||||
// See https://en.wikipedia.org/wiki/Space-filling_tree#/media/File:Space_Filling_Tree_Tri_iter_1_2_3.png
|
||||
newTriangles.reserve(triangles.size() * TESSELTATION_MULTIPLIER);
|
||||
for (size_t i = 0; i < triangles.size(); i += VERTICES_PER_TRIANGLE) {
|
||||
const vec3& a = triangles[i];
|
||||
const vec3& b = triangles[i + 1];
|
||||
const vec3& c = triangles[i + 2];
|
||||
vec3 ab = glm::normalize(a + b);
|
||||
vec3 bc = glm::normalize(b + c);
|
||||
vec3 ca = glm::normalize(c + a);
|
||||
|
||||
newTriangles.push_back(a);
|
||||
newTriangles.push_back(ab);
|
||||
newTriangles.push_back(ca);
|
||||
|
||||
newTriangles.push_back(b);
|
||||
newTriangles.push_back(bc);
|
||||
newTriangles.push_back(ab);
|
||||
|
||||
newTriangles.push_back(c);
|
||||
newTriangles.push_back(ca);
|
||||
newTriangles.push_back(bc);
|
||||
|
||||
newTriangles.push_back(ab);
|
||||
newTriangles.push_back(bc);
|
||||
newTriangles.push_back(ca);
|
||||
}
|
||||
triangles.swap(newTriangles);
|
||||
--count;
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
size_t GeometryCache::getShapeTriangleCount(Shape shape) {
|
||||
return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE;
|
||||
|
@ -221,6 +126,324 @@ size_t GeometryCache::getCubeTriangleCount() {
|
|||
return getShapeTriangleCount(Cube);
|
||||
}
|
||||
|
||||
using Index = uint32_t;
|
||||
using IndexPair = uint64_t;
|
||||
using IndexPairs = std::unordered_set<IndexPair>;
|
||||
|
||||
template <size_t N>
|
||||
using Face = std::array<Index, N>;
|
||||
|
||||
template <size_t N>
|
||||
using FaceVector = std::vector<Face<N>>;
|
||||
|
||||
template <size_t N>
|
||||
struct Solid {
|
||||
VertexVector vertices;
|
||||
FaceVector<N> faces;
|
||||
|
||||
Solid<N>& fitDimension(float newMaxDimension) {
|
||||
float maxDimension = 0;
|
||||
for (const auto& vertex : vertices) {
|
||||
maxDimension = std::max(maxDimension, std::max(std::max(vertex.x, vertex.y), vertex.z));
|
||||
}
|
||||
float multiplier = newMaxDimension / maxDimension;
|
||||
for (auto& vertex : vertices) {
|
||||
vertex *= multiplier;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
vec3 getFaceNormal(size_t faceIndex) const {
|
||||
vec3 result;
|
||||
const auto& face = faces[faceIndex];
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
result += vertices[face[i]];
|
||||
}
|
||||
result /= N;
|
||||
return glm::normalize(result);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
static size_t triangulatedFaceTriangleCount() {
|
||||
return N - 2;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
static size_t triangulatedFaceIndexCount() {
|
||||
return triangulatedFaceTriangleCount<N>() * VERTICES_PER_TRIANGLE;
|
||||
}
|
||||
|
||||
static IndexPair indexToken(Index a, Index b) {
|
||||
if (a > b) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
return (((IndexPair)a) << 32) | ((IndexPair)b);
|
||||
}
|
||||
|
||||
static Solid<3> tesselate(Solid<3> solid, int count) {
|
||||
float length = glm::length(solid.vertices[0]);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Solid<3> result { solid.vertices, {} };
|
||||
result.vertices.reserve(solid.vertices.size() + solid.faces.size() * 3);
|
||||
for (size_t f = 0; f < solid.faces.size(); ++f) {
|
||||
Index baseVertex = (Index)result.vertices.size();
|
||||
const Face<3>& oldFace = solid.faces[f];
|
||||
const vec3& a = solid.vertices[oldFace[0]];
|
||||
const vec3& b = solid.vertices[oldFace[1]];
|
||||
const vec3& c = solid.vertices[oldFace[2]];
|
||||
vec3 ab = glm::normalize(a + b) * length;
|
||||
vec3 bc = glm::normalize(b + c) * length;
|
||||
vec3 ca = glm::normalize(c + a) * length;
|
||||
result.vertices.push_back(ab);
|
||||
result.vertices.push_back(bc);
|
||||
result.vertices.push_back(ca);
|
||||
result.faces.push_back(Face<3>{ { oldFace[0], baseVertex, baseVertex + 2 } });
|
||||
result.faces.push_back(Face<3>{ { baseVertex, oldFace[1], baseVertex + 1 } });
|
||||
result.faces.push_back(Face<3>{ { baseVertex + 1, oldFace[2], baseVertex + 2 } });
|
||||
result.faces.push_back(Face<3>{ { baseVertex, baseVertex + 1, baseVertex + 2 } });
|
||||
}
|
||||
solid = result;
|
||||
}
|
||||
return solid;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void setupFlatShape(GeometryCache::ShapeData& shapeData, const Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||
VertexVector vertices;
|
||||
IndexVector solidIndices, wireIndices;
|
||||
IndexPairs wireSeenIndices;
|
||||
|
||||
size_t faceCount = shape.faces.size();
|
||||
size_t faceIndexCount = triangulatedFaceIndexCount<N>();
|
||||
|
||||
vertices.reserve(N * faceCount * 2);
|
||||
solidIndices.reserve(faceIndexCount * faceCount);
|
||||
|
||||
for (size_t f = 0; f < faceCount; ++f) {
|
||||
const Face<N>& face = shape.faces[f];
|
||||
// Compute the face normal
|
||||
vec3 faceNormal = shape.getFaceNormal(f);
|
||||
|
||||
// Create the vertices for the face
|
||||
for (Index i = 0; i < N; ++i) {
|
||||
Index originalIndex = face[i];
|
||||
vertices.push_back(shape.vertices[originalIndex]);
|
||||
vertices.push_back(faceNormal);
|
||||
}
|
||||
|
||||
// Create the wire indices for unseen edges
|
||||
for (Index i = 0; i < N; ++i) {
|
||||
Index a = i;
|
||||
Index b = (i + 1) % N;
|
||||
auto token = indexToken(face[a], face[b]);
|
||||
if (0 == wireSeenIndices.count(token)) {
|
||||
wireSeenIndices.insert(token);
|
||||
wireIndices.push_back(a + baseVertex);
|
||||
wireIndices.push_back(b + baseVertex);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the solid face indices
|
||||
for (Index i = 0; i < N - 2; ++i) {
|
||||
solidIndices.push_back(0 + baseVertex);
|
||||
solidIndices.push_back(i + 1 + baseVertex);
|
||||
solidIndices.push_back(i + 2 + baseVertex);
|
||||
}
|
||||
baseVertex += (Index)N;
|
||||
}
|
||||
|
||||
shapeData.setupVertices(vertexBuffer, vertices);
|
||||
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void setupSmoothShape(GeometryCache::ShapeData& shapeData, const Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||
|
||||
VertexVector vertices;
|
||||
vertices.reserve(shape.vertices.size() * 2);
|
||||
for (const auto& vertex : shape.vertices) {
|
||||
vertices.push_back(vertex);
|
||||
vertices.push_back(vertex);
|
||||
}
|
||||
|
||||
IndexVector solidIndices, wireIndices;
|
||||
IndexPairs wireSeenIndices;
|
||||
|
||||
size_t faceCount = shape.faces.size();
|
||||
size_t faceIndexCount = triangulatedFaceIndexCount<N>();
|
||||
|
||||
solidIndices.reserve(faceIndexCount * faceCount);
|
||||
|
||||
for (size_t f = 0; f < faceCount; ++f) {
|
||||
const Face<N>& face = shape.faces[f];
|
||||
// Create the wire indices for unseen edges
|
||||
for (Index i = 0; i < N; ++i) {
|
||||
Index a = face[i];
|
||||
Index b = face[(i + 1) % N];
|
||||
auto token = indexToken(a, b);
|
||||
if (0 == wireSeenIndices.count(token)) {
|
||||
wireSeenIndices.insert(token);
|
||||
wireIndices.push_back(a + baseVertex);
|
||||
wireIndices.push_back(b + baseVertex);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the solid face indices
|
||||
for (Index i = 0; i < N - 2; ++i) {
|
||||
solidIndices.push_back(face[i] + baseVertex);
|
||||
solidIndices.push_back(face[i + 1] + baseVertex);
|
||||
solidIndices.push_back(face[i + 2] + baseVertex);
|
||||
}
|
||||
}
|
||||
|
||||
shapeData.setupVertices(vertexBuffer, vertices);
|
||||
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||
}
|
||||
|
||||
// The golden ratio
|
||||
static const float PHI = 1.61803398874f;
|
||||
|
||||
static const Solid<3>& tetrahedron() {
|
||||
static const auto A = vec3(1, 1, 1);
|
||||
static const auto B = vec3(1, -1, -1);
|
||||
static const auto C = vec3(-1, 1, -1);
|
||||
static const auto D = vec3(-1, -1, 1);
|
||||
static const Solid<3> TETRAHEDRON = Solid<3>{
|
||||
{ A, B, C, D },
|
||||
FaceVector<3>{
|
||||
Face<3> { { 0, 1, 2 } },
|
||||
Face<3> { { 3, 1, 0 } },
|
||||
Face<3> { { 2, 3, 0 } },
|
||||
Face<3> { { 2, 1, 3 } },
|
||||
}
|
||||
}.fitDimension(0.5f);
|
||||
return TETRAHEDRON;
|
||||
}
|
||||
|
||||
static const Solid<4>& cube() {
|
||||
static const auto A = vec3(1, 1, 1);
|
||||
static const auto B = vec3(-1, 1, 1);
|
||||
static const auto C = vec3(-1, 1, -1);
|
||||
static const auto D = vec3(1, 1, -1);
|
||||
static const Solid<4> CUBE = Solid<4>{
|
||||
{ A, B, C, D, -A, -B, -C, -D },
|
||||
FaceVector<4>{
|
||||
Face<4> { { 3, 2, 1, 0 } },
|
||||
Face<4> { { 0, 1, 7, 6 } },
|
||||
Face<4> { { 1, 2, 4, 7 } },
|
||||
Face<4> { { 2, 3, 5, 4 } },
|
||||
Face<4> { { 3, 0, 6, 5 } },
|
||||
Face<4> { { 4, 5, 6, 7 } },
|
||||
}
|
||||
}.fitDimension(0.5f);
|
||||
return CUBE;
|
||||
}
|
||||
|
||||
static const Solid<3>& octahedron() {
|
||||
static const auto A = vec3(0, 1, 0);
|
||||
static const auto B = vec3(0, -1, 0);
|
||||
static const auto C = vec3(0, 0, 1);
|
||||
static const auto D = vec3(0, 0, -1);
|
||||
static const auto E = vec3(1, 0, 0);
|
||||
static const auto F = vec3(-1, 0, 0);
|
||||
static const Solid<3> OCTAHEDRON = Solid<3>{
|
||||
{ A, B, C, D, E, F},
|
||||
FaceVector<3> {
|
||||
Face<3> { { 0, 2, 4, } },
|
||||
Face<3> { { 0, 4, 3, } },
|
||||
Face<3> { { 0, 3, 5, } },
|
||||
Face<3> { { 0, 5, 2, } },
|
||||
Face<3> { { 1, 4, 2, } },
|
||||
Face<3> { { 1, 3, 4, } },
|
||||
Face<3> { { 1, 5, 3, } },
|
||||
Face<3> { { 1, 2, 5, } },
|
||||
}
|
||||
}.fitDimension(0.5f);
|
||||
return OCTAHEDRON;
|
||||
}
|
||||
|
||||
static const Solid<5>& dodecahedron() {
|
||||
static const float P = PHI;
|
||||
static const float IP = 1.0f / PHI;
|
||||
static const vec3 A = vec3(IP, P, 0);
|
||||
static const vec3 B = vec3(-IP, P, 0);
|
||||
static const vec3 C = vec3(-1, 1, 1);
|
||||
static const vec3 D = vec3(0, IP, P);
|
||||
static const vec3 E = vec3(1, 1, 1);
|
||||
static const vec3 F = vec3(1, 1, -1);
|
||||
static const vec3 G = vec3(-1, 1, -1);
|
||||
static const vec3 H = vec3(-P, 0, IP);
|
||||
static const vec3 I = vec3(0, -IP, P);
|
||||
static const vec3 J = vec3(P, 0, IP);
|
||||
|
||||
static const Solid<5> DODECAHEDRON = Solid<5>{
|
||||
{
|
||||
A, B, C, D, E, F, G, H, I, J,
|
||||
-A, -B, -C, -D, -E, -F, -G, -H, -I, -J,
|
||||
},
|
||||
FaceVector<5> {
|
||||
Face<5> { { 0, 1, 2, 3, 4 } },
|
||||
Face<5> { { 0, 5, 18, 6, 1 } },
|
||||
Face<5> { { 1, 6, 19, 7, 2 } },
|
||||
Face<5> { { 2, 7, 15, 8, 3 } },
|
||||
Face<5> { { 3, 8, 16, 9, 4 } },
|
||||
Face<5> { { 4, 9, 17, 5, 0 } },
|
||||
Face<5> { { 14, 13, 12, 11, 10 } },
|
||||
Face<5> { { 11, 16, 8, 15, 10 } },
|
||||
Face<5> { { 12, 17, 9, 16, 11 } },
|
||||
Face<5> { { 13, 18, 5, 17, 12 } },
|
||||
Face<5> { { 14, 19, 6, 18, 13 } },
|
||||
Face<5> { { 10, 15, 7, 19, 14 } },
|
||||
}
|
||||
}.fitDimension(0.5f);
|
||||
return DODECAHEDRON;
|
||||
}
|
||||
|
||||
static const Solid<3>& icosahedron() {
|
||||
static const float N = 1.0f / PHI;
|
||||
static const float P = 1.0f;
|
||||
static const auto A = vec3(N, P, 0);
|
||||
static const auto B = vec3(-N, P, 0);
|
||||
static const auto C = vec3(0, N, P);
|
||||
static const auto D = vec3(P, 0, N);
|
||||
static const auto E = vec3(P, 0, -N);
|
||||
static const auto F = vec3(0, N, -P);
|
||||
|
||||
static const Solid<3> ICOSAHEDRON = Solid<3> {
|
||||
{
|
||||
A, B, C, D, E, F,
|
||||
-A, -B, -C, -D, -E, -F,
|
||||
},
|
||||
FaceVector<3> {
|
||||
Face<3> { { 1, 2, 0 } },
|
||||
Face<3> { { 2, 3, 0 } },
|
||||
Face<3> { { 3, 4, 0 } },
|
||||
Face<3> { { 4, 5, 0 } },
|
||||
Face<3> { { 5, 1, 0 } },
|
||||
|
||||
Face<3> { { 1, 10, 2 } },
|
||||
Face<3> { { 11, 2, 10 } },
|
||||
Face<3> { { 2, 11, 3 } },
|
||||
Face<3> { { 7, 3, 11 } },
|
||||
Face<3> { { 3, 7, 4 } },
|
||||
Face<3> { { 8, 4, 7 } },
|
||||
Face<3> { { 4, 8, 5 } },
|
||||
Face<3> { { 9, 5, 8 } },
|
||||
Face<3> { { 5, 9, 1 } },
|
||||
Face<3> { { 10, 1, 9 } },
|
||||
|
||||
Face<3> { { 8, 7, 6 } },
|
||||
Face<3> { { 9, 8, 6 } },
|
||||
Face<3> { { 10, 9, 6 } },
|
||||
Face<3> { { 11, 10, 6 } },
|
||||
Face<3> { { 7, 11, 6 } },
|
||||
}
|
||||
}.fitDimension(0.5f);
|
||||
return ICOSAHEDRON;
|
||||
}
|
||||
|
||||
// FIXME solids need per-face vertices, but smooth shaded
|
||||
// components do not. Find a way to support using draw elements
|
||||
|
@ -230,229 +453,28 @@ size_t GeometryCache::getCubeTriangleCount() {
|
|||
void GeometryCache::buildShapes() {
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>();
|
||||
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
||||
size_t startingIndex = 0;
|
||||
|
||||
// Cube
|
||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
||||
{
|
||||
ShapeData& shapeData = _shapes[Cube];
|
||||
VertexVector vertices;
|
||||
// front
|
||||
vertices.push_back(vec3(1, 1, 1));
|
||||
vertices.push_back(vec3(0, 0, 1));
|
||||
vertices.push_back(vec3(-1, 1, 1));
|
||||
vertices.push_back(vec3(0, 0, 1));
|
||||
vertices.push_back(vec3(-1, -1, 1));
|
||||
vertices.push_back(vec3(0, 0, 1));
|
||||
vertices.push_back(vec3(1, -1, 1));
|
||||
vertices.push_back(vec3(0, 0, 1));
|
||||
|
||||
// right
|
||||
vertices.push_back(vec3(1, 1, 1));
|
||||
vertices.push_back(vec3(1, 0, 0));
|
||||
vertices.push_back(vec3(1, -1, 1));
|
||||
vertices.push_back(vec3(1, 0, 0));
|
||||
vertices.push_back(vec3(1, -1, -1));
|
||||
vertices.push_back(vec3(1, 0, 0));
|
||||
vertices.push_back(vec3(1, 1, -1));
|
||||
vertices.push_back(vec3(1, 0, 0));
|
||||
|
||||
// top
|
||||
vertices.push_back(vec3(1, 1, 1));
|
||||
vertices.push_back(vec3(0, 1, 0));
|
||||
vertices.push_back(vec3(1, 1, -1));
|
||||
vertices.push_back(vec3(0, 1, 0));
|
||||
vertices.push_back(vec3(-1, 1, -1));
|
||||
vertices.push_back(vec3(0, 1, 0));
|
||||
vertices.push_back(vec3(-1, 1, 1));
|
||||
vertices.push_back(vec3(0, 1, 0));
|
||||
|
||||
// left
|
||||
vertices.push_back(vec3(-1, 1, 1));
|
||||
vertices.push_back(vec3(-1, 0, 0));
|
||||
vertices.push_back(vec3(-1, 1, -1));
|
||||
vertices.push_back(vec3(-1, 0, 0));
|
||||
vertices.push_back(vec3(-1, -1, -1));
|
||||
vertices.push_back(vec3(-1, 0, 0));
|
||||
vertices.push_back(vec3(-1, -1, 1));
|
||||
vertices.push_back(vec3(-1, 0, 0));
|
||||
|
||||
// bottom
|
||||
vertices.push_back(vec3(-1, -1, -1));
|
||||
vertices.push_back(vec3(0, -1, 0));
|
||||
vertices.push_back(vec3(1, -1, -1));
|
||||
vertices.push_back(vec3(0, -1, 0));
|
||||
vertices.push_back(vec3(1, -1, 1));
|
||||
vertices.push_back(vec3(0, -1, 0));
|
||||
vertices.push_back(vec3(-1, -1, 1));
|
||||
vertices.push_back(vec3(0, -1, 0));
|
||||
|
||||
// back
|
||||
vertices.push_back(vec3(1, -1, -1));
|
||||
vertices.push_back(vec3(0, 0, -1));
|
||||
vertices.push_back(vec3(-1, -1, -1));
|
||||
vertices.push_back(vec3(0, 0, -1));
|
||||
vertices.push_back(vec3(-1, 1, -1));
|
||||
vertices.push_back(vec3(0, 0, -1));
|
||||
vertices.push_back(vec3(1, 1, -1));
|
||||
vertices.push_back(vec3(0, 0, -1));
|
||||
|
||||
static const size_t VERTEX_FORMAT_SIZE = 2;
|
||||
static const size_t VERTEX_OFFSET = 0;
|
||||
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
auto vertexIndex = i;
|
||||
// Make a unit cube by having the vertices (at index N)
|
||||
// while leaving the normals (at index N + 1) alone
|
||||
if (VERTEX_OFFSET == vertexIndex % VERTEX_FORMAT_SIZE) {
|
||||
vertices[vertexIndex] *= 0.5f;
|
||||
}
|
||||
}
|
||||
shapeData.setupVertices(_shapeVertices, vertices);
|
||||
|
||||
IndexVector indices{
|
||||
0, 1, 2, 2, 3, 0, // front
|
||||
4, 5, 6, 6, 7, 4, // right
|
||||
8, 9, 10, 10, 11, 8, // top
|
||||
12, 13, 14, 14, 15, 12, // left
|
||||
16, 17, 18, 18, 19, 16, // bottom
|
||||
20, 21, 22, 22, 23, 20 // back
|
||||
};
|
||||
for (auto& index : indices) {
|
||||
index += (uint16_t)startingIndex;
|
||||
}
|
||||
|
||||
IndexVector wireIndices{
|
||||
0, 1, 1, 2, 2, 3, 3, 0, // front
|
||||
20, 21, 21, 22, 22, 23, 23, 20, // back
|
||||
0, 23, 1, 22, 2, 21, 3, 20 // sides
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < wireIndices.size(); ++i) {
|
||||
indices[i] += (uint16_t)startingIndex;
|
||||
}
|
||||
|
||||
shapeData.setupIndices(_shapeIndices, indices, wireIndices);
|
||||
}
|
||||
|
||||
setupFlatShape(_shapes[Cube], cube(), _shapeVertices, _shapeIndices);
|
||||
// Tetrahedron
|
||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
||||
{
|
||||
ShapeData& shapeData = _shapes[Tetrahedron];
|
||||
size_t vertexCount = 4;
|
||||
VertexVector vertices;
|
||||
{
|
||||
VertexVector originalVertices = tetrahedronVertices();
|
||||
vertexCount = originalVertices.size();
|
||||
vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER);
|
||||
for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) {
|
||||
auto triangleStartIndex = i;
|
||||
vec3 faceNormal;
|
||||
for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
||||
auto triangleVertexIndex = j;
|
||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
||||
faceNormal += originalVertices[vertexIndex];
|
||||
}
|
||||
faceNormal = glm::normalize(faceNormal);
|
||||
for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
||||
auto triangleVertexIndex = j;
|
||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
||||
vertices.push_back(originalVertices[vertexIndex]);
|
||||
vertices.push_back(faceNormal);
|
||||
}
|
||||
}
|
||||
}
|
||||
shapeData.setupVertices(_shapeVertices, vertices);
|
||||
|
||||
IndexVector indices;
|
||||
for (size_t i = 0; i < vertexCount; i += VERTICES_PER_TRIANGLE) {
|
||||
auto triangleStartIndex = i;
|
||||
for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
||||
auto triangleVertexIndex = j;
|
||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
||||
indices.push_back((uint16_t)(vertexIndex + startingIndex));
|
||||
}
|
||||
}
|
||||
|
||||
IndexVector wireIndices{
|
||||
0, 1, 1, 2, 2, 0,
|
||||
0, 3, 1, 3, 2, 3,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < wireIndices.size(); ++i) {
|
||||
wireIndices[i] += (uint16_t)startingIndex;
|
||||
}
|
||||
|
||||
shapeData.setupIndices(_shapeIndices, indices, wireIndices);
|
||||
}
|
||||
|
||||
setupFlatShape(_shapes[Tetrahedron], tetrahedron(), _shapeVertices, _shapeIndices);
|
||||
// Icosahedron
|
||||
setupFlatShape(_shapes[Icosahedron], icosahedron(), _shapeVertices, _shapeIndices);
|
||||
// Octahedron
|
||||
setupFlatShape(_shapes[Octahedron], octahedron(), _shapeVertices, _shapeIndices);
|
||||
// Dodecahedron
|
||||
setupFlatShape(_shapes[Dodecahedron], dodecahedron(), _shapeVertices, _shapeIndices);
|
||||
|
||||
// Sphere
|
||||
// FIXME this uses way more vertices than required. Should find a way to calculate the indices
|
||||
// using shared vertices for better vertex caching
|
||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
||||
{
|
||||
ShapeData& shapeData = _shapes[Sphere];
|
||||
VertexVector vertices;
|
||||
IndexVector indices;
|
||||
{
|
||||
VertexVector originalVertices = tesselate(icosahedronVertices(), ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT);
|
||||
vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER);
|
||||
for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) {
|
||||
auto triangleStartIndex = i;
|
||||
for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
||||
auto triangleVertexIndex = j;
|
||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
||||
const auto& vertex = originalVertices[i + j];
|
||||
// Spheres use the same values for vertices and normals
|
||||
vertices.push_back(vertex);
|
||||
vertices.push_back(vertex);
|
||||
indices.push_back((uint16_t)(vertexIndex + startingIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeData.setupVertices(_shapeVertices, vertices);
|
||||
// FIXME don't use solid indices for wire drawing.
|
||||
shapeData.setupIndices(_shapeIndices, indices, indices);
|
||||
}
|
||||
|
||||
// Icosahedron
|
||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
||||
{
|
||||
ShapeData& shapeData = _shapes[Icosahedron];
|
||||
|
||||
VertexVector vertices;
|
||||
IndexVector indices;
|
||||
{
|
||||
const VertexVector& originalVertices = icosahedronVertices();
|
||||
vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER);
|
||||
for (size_t i = 0; i < originalVertices.size(); i += 3) {
|
||||
auto triangleStartIndex = i;
|
||||
vec3 faceNormal;
|
||||
for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
||||
auto triangleVertexIndex = j;
|
||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
||||
faceNormal += originalVertices[vertexIndex];
|
||||
}
|
||||
faceNormal = glm::normalize(faceNormal);
|
||||
for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
||||
auto triangleVertexIndex = j;
|
||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
||||
vertices.push_back(originalVertices[vertexIndex]);
|
||||
vertices.push_back(faceNormal);
|
||||
indices.push_back((uint16_t)(vertexIndex + startingIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeData.setupVertices(_shapeVertices, vertices);
|
||||
// FIXME don't use solid indices for wire drawing.
|
||||
shapeData.setupIndices(_shapeIndices, indices, indices);
|
||||
}
|
||||
Solid<3> sphere = icosahedron();
|
||||
sphere = tesselate(sphere, ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT);
|
||||
sphere.fitDimension(1.0f);
|
||||
setupSmoothShape(_shapes[Sphere], sphere, _shapeVertices, _shapeIndices);
|
||||
|
||||
// Line
|
||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
||||
{
|
||||
Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE);
|
||||
ShapeData& shapeData = _shapes[Line];
|
||||
shapeData.setupVertices(_shapeVertices, VertexVector{
|
||||
vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0),
|
||||
|
@ -460,9 +482,8 @@ void GeometryCache::buildShapes() {
|
|||
});
|
||||
IndexVector wireIndices;
|
||||
// Only two indices
|
||||
wireIndices.push_back(0 + (uint16_t)startingIndex);
|
||||
wireIndices.push_back(1 + (uint16_t)startingIndex);
|
||||
|
||||
wireIndices.push_back(0 + baseVertex);
|
||||
wireIndices.push_back(1 + baseVertex);
|
||||
shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);
|
||||
}
|
||||
|
||||
|
|
|
@ -121,8 +121,8 @@ inline uint qHash(const Vec4PairVec4Pair& v, uint seed) {
|
|||
seed);
|
||||
}
|
||||
|
||||
using IndexVector = std::vector<uint32_t>;
|
||||
using VertexVector = std::vector<glm::vec3>;
|
||||
using IndexVector = std::vector<uint16_t>;
|
||||
|
||||
/// Stores cached geometry.
|
||||
class GeometryCache : public Dependency {
|
||||
|
@ -137,7 +137,7 @@ public:
|
|||
Cube,
|
||||
Sphere,
|
||||
Tetrahedron,
|
||||
Octahetron,
|
||||
Octahedron,
|
||||
Dodecahedron,
|
||||
Icosahedron,
|
||||
Torus,
|
||||
|
|
|
@ -1368,10 +1368,12 @@
|
|||
<div class="shape-group shape-section property dropdown">
|
||||
<label for="property-shape">Shape</label>
|
||||
<select name="SelectShape" id="property-shape">
|
||||
<option value="Cube">Cube</option>
|
||||
<option value="Cube">Box</option>
|
||||
<option value="Sphere">Sphere</option>
|
||||
<option value="Icosahedron">Icosahedron</option>
|
||||
<option value="Tetrahedron">Tetrahedron</option>
|
||||
<option value="Octahedron">Octahedron</option>
|
||||
<option value="Icosahedron">Icosahedron</option>
|
||||
<option value="Dodecahedron">Dodecahedron</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="shape-group shape-section property text">
|
||||
|
|
|
@ -4,4 +4,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils)
|
|||
setup_hifi_project(Quick Gui OpenGL Script Widgets)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||
link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils )
|
||||
package_libraries_for_deployment()
|
||||
package_libraries_for_deployment()
|
||||
|
||||
target_nsight()
|
||||
|
|
20
tests/gpu-test/src/TestHelpers.cpp
Normal file
20
tests/gpu-test/src/TestHelpers.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TestHelpers.h"
|
||||
|
||||
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
|
||||
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
|
||||
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
|
||||
auto shader = gpu::Shader::createProgram(vs, fs);
|
||||
if (!gpu::Shader::makeProgram(*shader, bindings)) {
|
||||
printf("Could not compile shader\n");
|
||||
exit(-1);
|
||||
}
|
||||
return shader;
|
||||
}
|
33
tests/gpu-test/src/TestHelpers.h
Normal file
33
tests/gpu-test/src/TestHelpers.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <Transform.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include <gpu/Resource.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include <gpu/Shader.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
class GpuTestBase {
|
||||
public:
|
||||
virtual ~GpuTestBase() {}
|
||||
virtual bool isReady() const { return true; }
|
||||
virtual size_t getTestCount() const { return 1; }
|
||||
virtual void renderTest(size_t test, RenderArgs* args) = 0;
|
||||
};
|
||||
|
||||
uint32_t toCompactColor(const glm::vec4& color);
|
||||
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings);
|
||||
|
84
tests/gpu-test/src/TestInstancedShapes.cpp
Normal file
84
tests/gpu-test/src/TestInstancedShapes.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TestInstancedShapes.h"
|
||||
|
||||
gpu::Stream::FormatPointer& getInstancedSolidStreamFormat();
|
||||
|
||||
static const size_t TYPE_COUNT = 4;
|
||||
static const size_t ITEM_COUNT = 50;
|
||||
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
|
||||
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
|
||||
|
||||
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
|
||||
GeometryCache::Icosahedron,
|
||||
GeometryCache::Cube,
|
||||
GeometryCache::Sphere,
|
||||
GeometryCache::Tetrahedron,
|
||||
};
|
||||
|
||||
const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
||||
|
||||
|
||||
TestInstancedShapes::TestInstancedShapes() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
static const float ITEM_RADIUS = 20;
|
||||
static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS };
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
//indirectCommand._count
|
||||
float startingInterval = ITEM_INTERVAL * i;
|
||||
std::vector<mat4> typeTransforms;
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
float theta = j * SHAPE_INTERVAL + startingInterval;
|
||||
auto transform = glm::rotate(mat4(), theta, Vectors::UP);
|
||||
transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X);
|
||||
transform = glm::translate(transform, ITEM_TRANSLATION);
|
||||
transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f));
|
||||
typeTransforms.push_back(transform);
|
||||
auto color = vec4 { randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 };
|
||||
color /= 255.0f;
|
||||
colors.push_back(color);
|
||||
colorBuffer->append(toCompactColor(color));
|
||||
}
|
||||
transforms.push_back(typeTransforms);
|
||||
}
|
||||
}
|
||||
|
||||
void TestInstancedShapes::renderTest(size_t testId, RenderArgs* args) {
|
||||
gpu::Batch& batch = *(args->_batch);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->bindSimpleProgram(batch);
|
||||
batch.setInputFormat(getInstancedSolidStreamFormat());
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
|
||||
std::string namedCall = __FUNCTION__ + std::to_string(i);
|
||||
|
||||
//batch.addInstanceModelTransforms(transforms[i]);
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
batch.setModelTransform(transforms[i][j]);
|
||||
batch.setupNamedCalls(namedCall, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData&) {
|
||||
batch.setInputBuffer(gpu::Stream::COLOR, gpu::BufferView(colorBuffer, i * ITEM_COUNT * 4, colorBuffer->getSize(), COLOR_ELEMENT));
|
||||
shapeData.drawInstances(batch, ITEM_COUNT);
|
||||
});
|
||||
}
|
||||
|
||||
//for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
// batch.setModelTransform(transforms[j + i * ITEM_COUNT]);
|
||||
// shapeData.draw(batch);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
23
tests/gpu-test/src/TestInstancedShapes.h
Normal file
23
tests/gpu-test/src/TestInstancedShapes.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TestHelpers.h"
|
||||
|
||||
class TestInstancedShapes : public GpuTestBase {
|
||||
|
||||
std::vector<std::vector<mat4>> transforms;
|
||||
std::vector<vec4> colors;
|
||||
gpu::BufferPointer colorBuffer;
|
||||
gpu::BufferView instanceXfmView;
|
||||
public:
|
||||
TestInstancedShapes();
|
||||
void renderTest(size_t testId, RenderArgs* args) override;
|
||||
};
|
||||
|
48
tests/gpu-test/src/TestShapes.cpp
Normal file
48
tests/gpu-test/src/TestShapes.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TestShapes.h"
|
||||
|
||||
static const size_t TYPE_COUNT = 6;
|
||||
|
||||
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
|
||||
GeometryCache::Cube,
|
||||
GeometryCache::Tetrahedron,
|
||||
GeometryCache::Octahedron,
|
||||
GeometryCache::Dodecahedron,
|
||||
GeometryCache::Icosahedron,
|
||||
GeometryCache::Sphere,
|
||||
};
|
||||
|
||||
void TestShapes::renderTest(size_t testId, RenderArgs* args) {
|
||||
gpu::Batch& batch = *(args->_batch);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->bindSimpleProgram(batch);
|
||||
|
||||
// Render unlit cube + sphere
|
||||
static auto startSecs = secTimestampNow();
|
||||
float seconds = secTimestampNow() - startSecs;
|
||||
seconds /= 4.0f;
|
||||
batch.setModelTransform(Transform());
|
||||
batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f);
|
||||
|
||||
bool wire = (seconds - floorf(seconds) > 0.5f);
|
||||
int shapeIndex = ((int)seconds) % TYPE_COUNT;
|
||||
if (wire) {
|
||||
geometryCache->renderWireShape(batch, SHAPE[shapeIndex]);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, SHAPE[shapeIndex]);
|
||||
}
|
||||
|
||||
batch.setModelTransform(Transform().setScale(1.01f));
|
||||
batch._glColor4f(1, 1, 1, 1);
|
||||
geometryCache->renderWireCube(batch);
|
||||
}
|
||||
|
||||
|
||||
|
22
tests/gpu-test/src/TestShapes.h
Normal file
22
tests/gpu-test/src/TestShapes.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TestHelpers.h"
|
||||
|
||||
class TestShapes : public GpuTestBase {
|
||||
|
||||
std::vector<std::vector<mat4>> transforms;
|
||||
std::vector<vec4> colors;
|
||||
gpu::BufferPointer colorBuffer;
|
||||
gpu::BufferView instanceXfmView;
|
||||
public:
|
||||
void renderTest(size_t testId, RenderArgs* args) override;
|
||||
};
|
||||
|
180
tests/gpu-test/src/TestWindow.cpp
Normal file
180
tests/gpu-test/src/TestWindow.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TestWindow.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QResizeEvent>
|
||||
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gl/QOpenGLDebugLoggerWrapper.h>
|
||||
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
|
||||
#include <GeometryCache.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <FramebufferCache.h>
|
||||
#include <TextureCache.h>
|
||||
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
extern void initDeferredPipelines(render::ShapePlumber& plumber);
|
||||
extern void initStencilPipeline(gpu::PipelinePointer& pipeline);
|
||||
#endif
|
||||
|
||||
TestWindow::TestWindow() {
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
|
||||
auto timer = new QTimer(this);
|
||||
timer->setInterval(5);
|
||||
connect(timer, &QTimer::timeout, [&] { draw(); });
|
||||
timer->start();
|
||||
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] {
|
||||
timer->stop();
|
||||
_aboutToQuit = true;
|
||||
});
|
||||
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
_light->setType(model::Light::SUN);
|
||||
_light->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset::OLD_TOWN_SQUARE);
|
||||
_light->setIntensity(1.0f);
|
||||
_light->setAmbientIntensity(0.5f);
|
||||
_light->setColor(vec3(1.0f));
|
||||
_light->setPosition(vec3(1, 1, 1));
|
||||
_renderContext->args = _renderArgs;
|
||||
#endif
|
||||
|
||||
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
//format.setSwapInterval(0);
|
||||
setFormat(format);
|
||||
_glContext.setFormat(format);
|
||||
_glContext.create();
|
||||
_glContext.makeCurrent(this);
|
||||
show();
|
||||
}
|
||||
|
||||
void TestWindow::initGl() {
|
||||
_glContext.makeCurrent(this);
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_renderArgs->_context = std::make_shared<gpu::Context>();
|
||||
_glContext.makeCurrent(this);
|
||||
DependencyManager::set<GeometryCache>();
|
||||
DependencyManager::set<TextureCache>();
|
||||
DependencyManager::set<FramebufferCache>();
|
||||
DependencyManager::set<DeferredLightingEffect>();
|
||||
resize(QSize(800, 600));
|
||||
|
||||
setupDebugLogger(this);
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||
deferredLightingEffect->init();
|
||||
deferredLightingEffect->setGlobalLight(_light);
|
||||
initDeferredPipelines(*_shapePlumber);
|
||||
initStencilPipeline(_opaquePipeline);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestWindow::resizeWindow(const QSize& size) {
|
||||
_size = size;
|
||||
_renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height());
|
||||
auto fboCache = DependencyManager::get<FramebufferCache>();
|
||||
if (fboCache) {
|
||||
fboCache->setFrameBufferSize(_size);
|
||||
}
|
||||
}
|
||||
|
||||
void TestWindow::beginFrame() {
|
||||
_renderArgs->_context->syncCache();
|
||||
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||
deferredLightingEffect->prepare(_renderArgs);
|
||||
#else
|
||||
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
|
||||
batch.clearDepthFramebuffer(1e4);
|
||||
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
|
||||
});
|
||||
#endif
|
||||
|
||||
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
batch.setViewportTransform(_renderArgs->_viewport);
|
||||
batch.setStateScissorRect(_renderArgs->_viewport);
|
||||
batch.setProjectionTransform(_projectionMatrix);
|
||||
});
|
||||
}
|
||||
|
||||
void TestWindow::endFrame() {
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
RenderArgs* args = _renderContext->args;
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
auto deferredFboColorDepthStencil = DependencyManager::get<FramebufferCache>()->getDeferredFramebufferDepthColor();
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
batch.setFramebuffer(deferredFboColorDepthStencil);
|
||||
batch.setPipeline(_opaquePipeline);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
});
|
||||
|
||||
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||
deferredLightingEffect->render(_renderContext);
|
||||
|
||||
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
PROFILE_RANGE_BATCH(batch, "blit");
|
||||
// Blit to screen
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
auto framebuffer = framebufferCache->getLightingFramebuffer();
|
||||
batch.blit(framebuffer, _renderArgs->_viewport, nullptr, _renderArgs->_viewport);
|
||||
});
|
||||
#endif
|
||||
|
||||
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
batch.resetStages();
|
||||
});
|
||||
_glContext.swapBuffers(this);
|
||||
}
|
||||
|
||||
void TestWindow::draw() {
|
||||
if (_aboutToQuit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempting to draw before we're visible and have a valid size will
|
||||
// produce GL errors.
|
||||
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_glContext.makeCurrent(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] { initGl(); });
|
||||
beginFrame();
|
||||
|
||||
renderFrame();
|
||||
|
||||
endFrame();
|
||||
}
|
||||
|
||||
void TestWindow::resizeEvent(QResizeEvent* ev) {
|
||||
resizeWindow(ev->size());
|
||||
float fov_degrees = 60.0f;
|
||||
float aspect_ratio = (float)_size.width() / _size.height();
|
||||
float near_clip = 0.1f;
|
||||
float far_clip = 1000.0f;
|
||||
_projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
|
||||
}
|
53
tests/gpu-test/src/TestWindow.h
Normal file
53
tests/gpu-test/src/TestWindow.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/05/16
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtCore/QTime>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <gpu/Forward.h>
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <render/ShapePipeline.h>
|
||||
#include <render/Context.h>
|
||||
|
||||
#define DEFERRED_LIGHTING
|
||||
|
||||
class TestWindow : public QWindow {
|
||||
protected:
|
||||
QOpenGLContextWrapper _glContext;
|
||||
QSize _size;
|
||||
glm::mat4 _projectionMatrix;
|
||||
bool _aboutToQuit { false };
|
||||
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
// Prepare the ShapePipelines
|
||||
render::ShapePlumberPointer _shapePlumber { std::make_shared<render::ShapePlumber>() };
|
||||
render::RenderContextPointer _renderContext { std::make_shared<render::RenderContext>() };
|
||||
gpu::PipelinePointer _opaquePipeline;
|
||||
model::LightPointer _light { std::make_shared<model::Light>() };
|
||||
#endif
|
||||
|
||||
RenderArgs* _renderArgs { new RenderArgs() };
|
||||
|
||||
TestWindow();
|
||||
virtual void initGl();
|
||||
virtual void renderFrame() = 0;
|
||||
|
||||
private:
|
||||
void resizeWindow(const QSize& size);
|
||||
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
void draw();
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
};
|
||||
|
|
@ -42,238 +42,64 @@
|
|||
|
||||
#include <GeometryCache.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <FramebufferCache.h>
|
||||
#include <TextureCache.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PathUtils.h>
|
||||
#include <RenderArgs.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "unlit_frag.h"
|
||||
#include "unlit_vert.h"
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <gpu/Context.h>
|
||||
|
||||
class RateCounter {
|
||||
std::vector<float> times;
|
||||
QElapsedTimer timer;
|
||||
public:
|
||||
RateCounter() {
|
||||
timer.start();
|
||||
}
|
||||
#include <render/Engine.h>
|
||||
#include <render/Scene.h>
|
||||
#include <render/CullTask.h>
|
||||
#include <render/SortTask.h>
|
||||
#include <render/DrawTask.h>
|
||||
#include <render/DrawStatus.h>
|
||||
#include <render/DrawSceneOctree.h>
|
||||
#include <render/CullTask.h>
|
||||
|
||||
void reset() {
|
||||
times.clear();
|
||||
}
|
||||
#include "TestWindow.h"
|
||||
#include "TestInstancedShapes.h"
|
||||
#include "TestShapes.h"
|
||||
|
||||
unsigned int count() const {
|
||||
return (unsigned int)times.size() - 1;
|
||||
}
|
||||
|
||||
float elapsed() const {
|
||||
if (times.size() < 1) {
|
||||
return 0.0f;
|
||||
}
|
||||
float elapsed = *times.rbegin() - *times.begin();
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
void increment() {
|
||||
times.push_back(timer.elapsed() / 1000.0f);
|
||||
}
|
||||
|
||||
float rate() const {
|
||||
if (elapsed() == 0.0f) {
|
||||
return NAN;
|
||||
}
|
||||
return (float) count() / elapsed();
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t toCompactColor(const glm::vec4& color);
|
||||
using namespace render;
|
||||
|
||||
|
||||
const char* VERTEX_SHADER = R"SHADER(
|
||||
|
||||
layout(location = 0) in vec4 inPosition;
|
||||
layout(location = 3) in vec2 inTexCoord0;
|
||||
|
||||
struct TransformObject {
|
||||
mat4 _model;
|
||||
mat4 _modelInverse;
|
||||
};
|
||||
|
||||
layout(location=15) in ivec2 _drawCallInfo;
|
||||
|
||||
uniform samplerBuffer transformObjectBuffer;
|
||||
|
||||
TransformObject getTransformObject() {
|
||||
int offset = 8 * _drawCallInfo.x;
|
||||
TransformObject object;
|
||||
object._model[0] = texelFetch(transformObjectBuffer, offset);
|
||||
object._model[1] = texelFetch(transformObjectBuffer, offset + 1);
|
||||
object._model[2] = texelFetch(transformObjectBuffer, offset + 2);
|
||||
object._model[3] = texelFetch(transformObjectBuffer, offset + 3);
|
||||
|
||||
object._modelInverse[0] = texelFetch(transformObjectBuffer, offset + 4);
|
||||
object._modelInverse[1] = texelFetch(transformObjectBuffer, offset + 5);
|
||||
object._modelInverse[2] = texelFetch(transformObjectBuffer, offset + 6);
|
||||
object._modelInverse[3] = texelFetch(transformObjectBuffer, offset + 7);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
struct TransformCamera {
|
||||
mat4 _view;
|
||||
mat4 _viewInverse;
|
||||
mat4 _projectionViewUntranslated;
|
||||
mat4 _projection;
|
||||
mat4 _projectionInverse;
|
||||
vec4 _viewport;
|
||||
};
|
||||
|
||||
layout(std140) uniform transformCameraBuffer {
|
||||
TransformCamera _camera;
|
||||
};
|
||||
|
||||
TransformCamera getTransformCamera() {
|
||||
return _camera;
|
||||
}
|
||||
|
||||
// the interpolated normal
|
||||
out vec2 _texCoord0;
|
||||
|
||||
void main(void) {
|
||||
_texCoord0 = inTexCoord0.st;
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
{ // transformModelToClipPos
|
||||
vec4 eyeWAPos;
|
||||
{ // _transformModelToEyeWorldAlignedPos
|
||||
highp mat4 _mv = obj._model;
|
||||
_mv[3].xyz -= cam._viewInverse[3].xyz;
|
||||
highp vec4 _eyeWApos = (_mv * inPosition);
|
||||
eyeWAPos = _eyeWApos;
|
||||
}
|
||||
gl_Position = cam._projectionViewUntranslated * eyeWAPos;
|
||||
}
|
||||
|
||||
})SHADER";
|
||||
|
||||
const char* FRAGMENT_SHADER = R"SHADER(
|
||||
|
||||
uniform sampler2D originalTexture;
|
||||
|
||||
in vec2 _texCoord0;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
void main(void) {
|
||||
//_fragColor0 = vec4(_texCoord0, 0.0, 1.0);
|
||||
_fragColor0 = texture(originalTexture, _texCoord0);
|
||||
}
|
||||
)SHADER";
|
||||
using TestBuilder = std::function<GpuTestBase*()>;
|
||||
using TestBuilders = std::list<TestBuilder>;
|
||||
|
||||
|
||||
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
|
||||
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
|
||||
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
|
||||
auto shader = gpu::Shader::createProgram(vs, fs);
|
||||
if (!gpu::Shader::makeProgram(*shader, bindings)) {
|
||||
printf("Could not compile shader\n");
|
||||
exit(-1);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
#define INTERACTIVE
|
||||
|
||||
float getSeconds(quint64 start = 0) {
|
||||
auto usecs = usecTimestampNow() - start;
|
||||
auto msecs = usecs / USECS_PER_MSEC;
|
||||
float seconds = (float)msecs / MSECS_PER_SECOND;
|
||||
return seconds;
|
||||
}
|
||||
|
||||
static const size_t TYPE_COUNT = 4;
|
||||
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
|
||||
GeometryCache::Icosahedron,
|
||||
GeometryCache::Cube,
|
||||
GeometryCache::Sphere,
|
||||
GeometryCache::Tetrahedron,
|
||||
//GeometryCache::Line,
|
||||
};
|
||||
|
||||
gpu::Stream::FormatPointer& getInstancedSolidStreamFormat();
|
||||
|
||||
// Creates an OpenGL window that renders a simple unlit scene using the gpu library and GeometryCache
|
||||
// Should eventually get refactored into something that supports multiple gpu backends.
|
||||
class QTestWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
|
||||
QOpenGLContextWrapper _qGlContext;
|
||||
QSize _size;
|
||||
|
||||
gpu::ContextPointer _context;
|
||||
gpu::PipelinePointer _pipeline;
|
||||
glm::mat4 _projectionMatrix;
|
||||
RateCounter fps;
|
||||
QTime _time;
|
||||
class MyTestWindow : public TestWindow {
|
||||
using Parent = TestWindow;
|
||||
TestBuilders _testBuilders;
|
||||
GpuTestBase* _currentTest { nullptr };
|
||||
size_t _currentTestId { 0 };
|
||||
size_t _currentMaxTests { 0 };
|
||||
glm::mat4 _camera;
|
||||
QTime _time;
|
||||
|
||||
protected:
|
||||
void renderText();
|
||||
|
||||
private:
|
||||
void resizeWindow(const QSize& size) {
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public:
|
||||
QTestWindow() {
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
QSurfaceFormat format;
|
||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||
format.setDepthBufferSize(16);
|
||||
format.setStencilBufferSize(8);
|
||||
setGLFormatVersion(format);
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
//format.setSwapInterval(0);
|
||||
|
||||
setFormat(format);
|
||||
|
||||
_qGlContext.setFormat(format);
|
||||
_qGlContext.create();
|
||||
|
||||
show();
|
||||
makeCurrent();
|
||||
setupDebugLogger(this);
|
||||
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_context = std::make_shared<gpu::Context>();
|
||||
makeCurrent();
|
||||
auto shader = makeShader(unlit_vert, unlit_frag, gpu::Shader::BindingSet{});
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setMultisampleEnable(true);
|
||||
state->setDepthTest(gpu::State::DepthTest { true });
|
||||
_pipeline = gpu::Pipeline::create(shader, state);
|
||||
|
||||
|
||||
|
||||
// Clear screen
|
||||
gpu::Batch batch;
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 1.0, 0.0, 0.5, 1.0 });
|
||||
_context->render(batch);
|
||||
|
||||
DependencyManager::set<GeometryCache>();
|
||||
DependencyManager::set<TextureCache>();
|
||||
DependencyManager::set<DeferredLightingEffect>();
|
||||
|
||||
resize(QSize(800, 600));
|
||||
|
||||
void initGl() override {
|
||||
Parent::initGl();
|
||||
#ifdef INTERACTIVE
|
||||
_time.start();
|
||||
}
|
||||
|
||||
virtual ~QTestWindow() {
|
||||
#endif
|
||||
updateCamera();
|
||||
_testBuilders = TestBuilders({
|
||||
//[this] { return new TestFbx(_shapePlumber); },
|
||||
[] { return new TestShapes(); },
|
||||
});
|
||||
}
|
||||
|
||||
void updateCamera() {
|
||||
float t = _time.elapsed() * 1e-4f;
|
||||
float t = 0;
|
||||
#ifdef INTERACTIVE
|
||||
t = _time.elapsed() * 1e-3f;
|
||||
#endif
|
||||
glm::vec3 unitscale { 1.0f };
|
||||
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
||||
|
||||
|
@ -283,263 +109,64 @@ public:
|
|||
static const vec3 camera_focus(0);
|
||||
static const vec3 camera_up(0, 1, 0);
|
||||
_camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
|
||||
|
||||
ViewFrustum frustum;
|
||||
frustum.setPosition(camera_position);
|
||||
frustum.setOrientation(glm::quat_cast(_camera));
|
||||
frustum.setProjection(_projectionMatrix);
|
||||
_renderArgs->setViewFrustum(frustum);
|
||||
}
|
||||
|
||||
void renderFrame() override {
|
||||
updateCamera();
|
||||
|
||||
void drawFloorGrid(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
// Render grid on xz plane (not the optimal way to do things, but w/e)
|
||||
// Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only
|
||||
static const std::string GRID_INSTANCE = "Grid";
|
||||
static auto compactColor1 = toCompactColor(vec4 { 0.35f, 0.25f, 0.15f, 1.0f });
|
||||
static auto compactColor2 = toCompactColor(vec4 { 0.15f, 0.25f, 0.35f, 1.0f });
|
||||
static std::vector<glm::mat4> transforms;
|
||||
static gpu::BufferPointer colorBuffer;
|
||||
if (!transforms.empty()) {
|
||||
transforms.reserve(200);
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
{
|
||||
glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i));
|
||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
||||
transforms.push_back(transform);
|
||||
colorBuffer->append(compactColor1);
|
||||
}
|
||||
while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) {
|
||||
if (_currentTest) {
|
||||
delete _currentTest;
|
||||
_currentTest = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0)));
|
||||
transform = glm::translate(transform, vec3(0, -1, -50 + i));
|
||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
||||
transforms.push_back(transform);
|
||||
colorBuffer->append(compactColor2);
|
||||
}
|
||||
_currentTest = _testBuilders.front()();
|
||||
_testBuilders.pop_front();
|
||||
|
||||
if (_currentTest) {
|
||||
_currentMaxTests = _currentTest->getTestCount();
|
||||
_currentTestId = 0;
|
||||
}
|
||||
}
|
||||
auto pipeline = geometryCache->getSimplePipeline();
|
||||
for (auto& transform : transforms) {
|
||||
batch.setModelTransform(transform);
|
||||
batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
||||
batch.setViewTransform(_camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void drawSimpleShapes(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
static const size_t ITEM_COUNT = 1000;
|
||||
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
|
||||
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
|
||||
|
||||
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
||||
|
||||
static std::vector<Transform> transforms;
|
||||
static std::vector<vec4> colors;
|
||||
static gpu::BufferPointer colorBuffer;
|
||||
static gpu::BufferView colorView;
|
||||
static gpu::BufferView instanceXfmView;
|
||||
if (!colorBuffer) {
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
static const float ITEM_RADIUS = 20;
|
||||
static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS };
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
//indirectCommand._count
|
||||
float startingInterval = ITEM_INTERVAL * i;
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
float theta = j * SHAPE_INTERVAL + startingInterval;
|
||||
auto transform = glm::rotate(mat4(), theta, Vectors::UP);
|
||||
transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X);
|
||||
transform = glm::translate(transform, ITEM_TRANSLATION);
|
||||
transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f));
|
||||
transforms.push_back(transform);
|
||||
auto color = vec4 { randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 };
|
||||
color /= 255.0f;
|
||||
colors.push_back(color);
|
||||
colorBuffer->append(toCompactColor(color));
|
||||
}
|
||||
}
|
||||
colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT);
|
||||
}
|
||||
|
||||
batch.setViewTransform(_camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
batch.setInputFormat(getInstancedSolidStreamFormat());
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
batch.setModelTransform(transforms[j]);
|
||||
shapeData.draw(batch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawCenterShape(gpu::Batch& batch) {
|
||||
// Render unlit cube + sphere
|
||||
static auto startUsecs = usecTimestampNow();
|
||||
float seconds = getSeconds(startUsecs);
|
||||
seconds /= 4.0f;
|
||||
batch.setModelTransform(Transform());
|
||||
batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f);
|
||||
|
||||
bool wire = (seconds - floorf(seconds) > 0.5f);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
int shapeIndex = ((int)seconds) % TYPE_COUNT;
|
||||
if (wire) {
|
||||
geometryCache->renderWireShape(batch, SHAPE[shapeIndex]);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, SHAPE[shapeIndex]);
|
||||
}
|
||||
|
||||
batch.setModelTransform(Transform().setScale(2.05f));
|
||||
batch._glColor4f(1, 1, 1, 1);
|
||||
geometryCache->renderWireCube(batch);
|
||||
}
|
||||
|
||||
void drawTerrain(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
static std::once_flag once;
|
||||
static gpu::BufferPointer vertexBuffer { std::make_shared<gpu::Buffer>() };
|
||||
static gpu::BufferPointer indexBuffer { std::make_shared<gpu::Buffer>() };
|
||||
|
||||
static gpu::BufferView positionView;
|
||||
static gpu::BufferView textureView;
|
||||
static gpu::Stream::FormatPointer vertexFormat { std::make_shared<gpu::Stream::Format>() };
|
||||
|
||||
static gpu::TexturePointer texture;
|
||||
static gpu::PipelinePointer pipeline;
|
||||
std::call_once(once, [&] {
|
||||
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec4) * 2; // position, normals, textures
|
||||
static const uint SHAPE_TEXTURES_OFFSET = sizeof(glm::vec4);
|
||||
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element TEXTURE_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV };
|
||||
std::vector<vec4> vertices;
|
||||
const int MINX = -1000;
|
||||
const int MAXX = 1000;
|
||||
|
||||
// top
|
||||
vertices.push_back(vec4(MAXX, 0, MAXX, 1));
|
||||
vertices.push_back(vec4(MAXX, MAXX, 0, 0));
|
||||
|
||||
vertices.push_back(vec4(MAXX, 0, MINX, 1));
|
||||
vertices.push_back(vec4(MAXX, 0, 0, 0));
|
||||
|
||||
vertices.push_back(vec4(MINX, 0, MINX, 1));
|
||||
vertices.push_back(vec4(0, 0, 0, 0));
|
||||
|
||||
vertices.push_back(vec4(MINX, 0, MAXX, 1));
|
||||
vertices.push_back(vec4(0, MAXX, 0, 0));
|
||||
|
||||
vertexBuffer->append(vertices);
|
||||
indexBuffer->append(std::vector<uint16_t>({ 0, 1, 2, 2, 3, 0 }));
|
||||
|
||||
positionView = gpu::BufferView(vertexBuffer, 0, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, POSITION_ELEMENT);
|
||||
textureView = gpu::BufferView(vertexBuffer, SHAPE_TEXTURES_OFFSET, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, TEXTURE_ELEMENT);
|
||||
texture = DependencyManager::get<TextureCache>()->getImageTexture("C:/Users/bdavis/Git/openvr/samples/bin/cube_texture.png");
|
||||
// texture = DependencyManager::get<TextureCache>()->getImageTexture("H:/test.png");
|
||||
//texture = DependencyManager::get<TextureCache>()->getImageTexture("H:/crate_blue.fbm/lambert8SG_Normal_OpenGL.png");
|
||||
|
||||
auto shader = makeShader(VERTEX_SHADER, FRAGMENT_SHADER, gpu::Shader::BindingSet {});
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setMultisampleEnable(false);
|
||||
state->setDepthTest(gpu::State::DepthTest { true });
|
||||
pipeline = gpu::Pipeline::create(shader, state);
|
||||
vertexFormat->setAttribute(gpu::Stream::POSITION);
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD);
|
||||
});
|
||||
|
||||
static auto start = usecTimestampNow();
|
||||
auto now = usecTimestampNow();
|
||||
if ((now - start) > USECS_PER_SECOND * 1) {
|
||||
start = now;
|
||||
texture->incremementMinMip();
|
||||
}
|
||||
|
||||
batch.setPipeline(pipeline);
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, positionView);
|
||||
batch.setInputBuffer(gpu::Stream::TEXCOORD, textureView);
|
||||
batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0);
|
||||
batch.setInputFormat(vertexFormat);
|
||||
|
||||
batch.setResourceTexture(0, texture);
|
||||
batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.1, 0)));
|
||||
batch.drawIndexed(gpu::TRIANGLES, 6, 0);
|
||||
|
||||
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getBlueTexture());
|
||||
batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.2, 0)));
|
||||
batch.drawIndexed(gpu::TRIANGLES, 6, 0);
|
||||
}
|
||||
|
||||
void draw() {
|
||||
// Attempting to draw before we're visible and have a valid size will
|
||||
// produce GL errors.
|
||||
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
||||
if (!_currentTest && _testBuilders.empty()) {
|
||||
qApp->quit();
|
||||
return;
|
||||
}
|
||||
updateCamera();
|
||||
makeCurrent();
|
||||
|
||||
gpu::Batch batch;
|
||||
batch.resetStages();
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
|
||||
batch.clearDepthFramebuffer(1e4);
|
||||
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
|
||||
batch.setProjectionTransform(_projectionMatrix);
|
||||
|
||||
batch.setViewTransform(_camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
//drawFloorGrid(batch);
|
||||
//drawSimpleShapes(batch);
|
||||
//drawCenterShape(batch);
|
||||
drawTerrain(batch);
|
||||
|
||||
_context->render(batch);
|
||||
_qGlContext.swapBuffers(this);
|
||||
|
||||
fps.increment();
|
||||
if (fps.elapsed() >= 0.5f) {
|
||||
qDebug() << "FPS: " << fps.rate();
|
||||
fps.reset();
|
||||
// Tests might need to wait for resources to download
|
||||
if (!_currentTest->isReady()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void makeCurrent() {
|
||||
_qGlContext.makeCurrent(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* ev) override {
|
||||
resizeWindow(ev->size());
|
||||
|
||||
float fov_degrees = 60.0f;
|
||||
float aspect_ratio = (float)_size.width() / _size.height();
|
||||
float near_clip = 0.1f;
|
||||
float far_clip = 1000.0f;
|
||||
_projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
|
||||
}
|
||||
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
batch.setViewTransform(_camera);
|
||||
_renderArgs->_batch = &batch;
|
||||
_currentTest->renderTest(_currentTestId, _renderArgs);
|
||||
_renderArgs->_batch = nullptr;
|
||||
});
|
||||
|
||||
#ifdef INTERACTIVE
|
||||
|
||||
#else
|
||||
// TODO Capture the current rendered framebuffer and save
|
||||
// Increment the test ID
|
||||
++_currentTestId;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QGuiApplication app(argc, argv);
|
||||
QTestWindow window;
|
||||
auto timer = new QTimer(&app);
|
||||
timer->setInterval(0);
|
||||
app.connect(timer, &QTimer::timeout, &app, [&] {
|
||||
window.draw();
|
||||
});
|
||||
timer->start();
|
||||
MyTestWindow window;
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "main.moc"
|
||||
|
||||
|
|
Loading…
Reference in a new issue