// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lights.h" #include "embree4/rtcore.h" RTC_NAMESPACE_USE namespace embree { struct Material; namespace SceneGraph { struct Node; struct MaterialNode; struct TriangleMeshNode; struct Statistics { Statistics() : numTriangleMeshes(0), numTriangles(0), numTriangleBytes(0), numQuadMeshes(0), numQuads(0), numQuadBytes(0), numSubdivMeshes(0), numPatches(0), numSubdivBytes(0), numCurveSets(0), numCurves(0), numCurveBytes(0), numGridMeshNodes(0), numGrids(0), numGridBytes(0), numPointSets(0), numPoints(0), numPointBytes(0), numTransformNodes(0), numTransformedObjects(0), numLights(0), numCameras(0), numMaterials(0) { } void print(); size_t numTriangleMeshes; size_t numTriangles; size_t numTriangleBytes; size_t numQuadMeshes; size_t numQuads; size_t numQuadBytes; size_t numSubdivMeshes; size_t numPatches; size_t numSubdivBytes; size_t numCurveSets; size_t numCurves; size_t numCurveBytes; size_t numGridMeshNodes; size_t numGrids; size_t numGridBytes; size_t numPointSets; size_t numPoints; size_t numPointBytes; size_t numTransformNodes; size_t numTransformedObjects; size_t numLights; size_t numCameras; size_t numMaterials; }; extern void (*opaque_geometry_destruction)(void*); Ref load(const FileName& fname, bool singleObject = false); struct Node : public RefCount { Node(bool closed = false) : indegree(0), closed(closed), hasLightOrCamera(false), id(-1), geometry(nullptr) { } Node(const std::string& name) : name(name), indegree(0), closed(false), id(-1), geometry(nullptr) { } ~Node() { if (opaque_geometry_destruction) opaque_geometry_destruction(geometry); } /* prints scenegraph */ virtual void print(std::ostream& cout, int depth = 0) = 0; /* sets material */ virtual void setMaterial(Ref material) { }; /* calculates the number of parent nodes pointing to this node */ virtual void calculateInDegree(); /* calculates for each node if its subtree is closed, indegrees have to be calculated first */ virtual bool calculateClosed(bool group_instancing); /* resets the number of parent nodes pointing to this node */ virtual void resetInDegree(); /* checks if the node is closed */ __forceinline bool isClosed() const { return closed; } /* calculates bounding box of node */ virtual BBox3fa bounds() const { return empty; } virtual BBox3fa bounds(size_t i) const { return empty; } /* calculates linear bounding box of node */ virtual LBBox3fa lbounds() const { return empty; } virtual LBBox3fa lbounds(size_t i) const { return empty; } /* calculates number of primitives */ virtual size_t numPrimitives() const { return 0; } public: std::string fileName; // when set to some filename the exporter references this file std::string name; // name of this node size_t indegree; // number of nodes pointing to us bool closed; // determines if the subtree may represent an instance bool hasLightOrCamera; unsigned int id; void* geometry; }; struct Transformations { __forceinline Transformations() { } __forceinline Transformations(OneTy) : time_range(0.0f, 1.0f) { spaces.push_back(one); } __forceinline Transformations(const BBox1f& time_range, size_t N) : time_range(time_range), spaces(N) { } __forceinline Transformations(const AffineSpace3fa& space) : time_range(0.0f, 1.0f) { spaces.push_back(space); } __forceinline Transformations(const AffineSpace3fa& space0, const AffineSpace3fa& space1) : time_range(0.0f, 1.0f) { spaces.push_back(space0); spaces.push_back(space1); } __forceinline Transformations(const avector& spaces) : time_range(0.0f, 1.0f), spaces(spaces) { assert(spaces.size()); } __forceinline size_t size() const { return spaces.size(); } __forceinline AffineSpace3ff& operator[](const size_t i) { return spaces[i]; } __forceinline const AffineSpace3ff& operator[](const size_t i) const { return spaces[i]; } BBox3fa bounds(const BBox3fa& cbounds) const { BBox3fa r = empty; for (size_t i = 0; i < spaces.size(); i++) r.extend(xfmBounds(spaces[i], cbounds)); return r; } LBBox3fa lbounds(const LBBox3fa& cbounds) const { assert(spaces.size()); if (spaces.size() == 1) { return LBBox3fa(xfmBounds(spaces[0], cbounds.bounds0), xfmBounds(spaces[0], cbounds.bounds1)); } else { avector bounds(spaces.size()); for (size_t i = 0; i < spaces.size(); i++) { const float f = float(i) / float(spaces.size() - 1); bounds[i] = xfmBounds(spaces[i], cbounds.interpolate(f)); } return LBBox3fa(bounds); } } void add(const Transformations& other) { for (size_t i = 0; i < other.size(); i++) spaces.push_back(other[i]); } static __forceinline bool isIdentity(AffineSpace3ff const& M, bool q) { if (M.l.vx.x != 1.f) return false; if (M.l.vx.y != 0.f) return false; if (M.l.vx.z != 0.f) return false; if (q && M.l.vx.w != 0.f) return false; if (M.l.vy.x != 0.f) return false; if (M.l.vy.y != 1.f) return false; if (M.l.vy.z != 0.f) return false; if (q && M.l.vy.w != 0.f) return false; if (M.l.vz.x != 0.f) return false; if (M.l.vz.y != 0.f) return false; if (M.l.vz.z != 1.f) return false; if (q && M.l.vz.w != 0.f) return false; if (M.p.x != 0.f) return false; if (M.p.y != 0.f) return false; if (M.p.z != 0.f) return false; if (q && M.p.w != 1.f) return false; return true; } static __forceinline AffineSpace3ff mul(AffineSpace3ff const& M0, AffineSpace3ff const& M1, bool q0, bool q1, bool& q) { q = false; if (isIdentity(M0, q0)) { q = q1; return M1; } if (isIdentity(M1, q1)) { q = q0; return M0; } // simple case non of the transformations is a quaternion if (q0 == false && q1 == false) { return M0 * M1; } else if (q0 == true && q1 == true) { std::cout << "warning: cannot multiply two quaternion decompositions. will convert to regular transforms and multiply" << std::endl; return quaternionDecompositionToAffineSpace(M0) * quaternionDecompositionToAffineSpace(M1); } else if (q0 == true && q1 == false) { AffineSpace3fa S; Quaternion3f Q; Vec3fa T; quaternionDecomposition(M0, T, Q, S); S = S * AffineSpace3fa(M1); if (S.l.vx.y != 0.f || S.l.vx.z != 0 || S.l.vy.z != 0) std::cout << "warning: cannot multiply quaternion and general transformation matrix. will ignore lower diagonal" << std::endl; q = true; return quaternionDecomposition(T, Q, S); } else { if (M0.l.vx.y != 0.f || M0.l.vx.z != 0 || M0.l.vy.z != 0 || M0.l.vy.x != 0.f || M0.l.vz.x != 0 || M0 .l.vz.y != 0) std::cout << "warning: cannot multiply general transformation matrix and quaternion. will only consider translation and diagonal as scale factors" << std::endl; AffineSpace3ff M = M1; M.l.vx.y += M0.p.x; M.l.vx.z += M0.p.y; M.l.vy.z += M0.p.z; M.l.vx.x *= M0.l.vx.x; M.l.vy.y *= M0.l.vy.y; M.l.vz.z *= M0.l.vz.z; q = true; return M; } } friend __forceinline Transformations operator*(const Transformations& a, const Transformations& b) { if (a.size() == 1) { Transformations c(intersect(a.time_range, b.time_range), b.size()); for (size_t i = 0; i < b.size(); i++) c[i] = mul(a[0], b[i], a.quaternion, b.quaternion, c.quaternion); return c; } else if (b.size() == 1) { Transformations c(intersect(a.time_range, b.time_range), a.size()); c.quaternion = a.quaternion || b.quaternion; for (size_t i = 0; i < a.size(); i++) c[i] = mul(a[i], b[0], a.quaternion, b.quaternion, c.quaternion); return c; } else if (a.size() == b.size()) { Transformations c(intersect(a.time_range, b.time_range), a.size()); c.quaternion = a.quaternion || b.quaternion; for (size_t i = 0; i < a.size(); i++) c[i] = mul(a[i], b[i], a.quaternion, b.quaternion, c.quaternion); return c; } else THROW_RUNTIME_ERROR("number of transformations does not match"); } friend __forceinline std::vector operator*(const std::vector& a, const Transformations& b) { std::vector result; for (size_t i = 0; i < a.size(); ++i) { result.push_back(a[i] * b); } return result; } friend __forceinline std::vector operator*(const Transformations& a, const std::vector& b) { return b * a; } friend __forceinline std::vector operator*(const std::vector& a, const std::vector& b) { if (a.size() != b.size()) THROW_RUNTIME_ERROR("number of transformations does not match"); std::vector result; for (size_t i = 0; i < a.size(); ++i) { result.push_back(a[i] * b[i]); } return result; } AffineSpace3ff interpolate(const float gtime) const { assert(time_range.lower == 0.0f && time_range.upper == 1.0f); if (spaces.size() == 1) return spaces[0]; /* calculate time segment itime and fractional time ftime */ const int time_segments = int(spaces.size() - 1); const float time = gtime * float(time_segments); const int itime = clamp(int(floor(time)), 0, time_segments - 1); const float ftime = time - float(itime); return lerp(spaces[itime + 0], spaces[itime + 1], ftime); } public: BBox1f time_range; avector spaces; bool quaternion = false; }; inline std::vector > transformMSMBlurVec3faBuffer( const std::vector >& positions_in, const Transformations& spaces) { std::vector > positions_out; const size_t num_time_steps = positions_in.size(); assert(num_time_steps); const size_t num_vertices = positions_in[0].size(); /* if we have only one set of vertices, use transformation to generate more vertex sets */ if (num_time_steps == 1) { for (size_t i = 0; i < spaces.size(); i++) { avector verts(num_vertices); for (size_t j = 0; j < num_vertices; j++) { verts[j] = xfmPoint((AffineSpace3fa) spaces[i], positions_in[0][j]); } positions_out.push_back(std::move(verts)); } } /* otherwise transform all vertex sets with interpolated transformation */ else { for (size_t t = 0; t < num_time_steps; t++) { float time = num_time_steps > 1 ? float(t) / float(num_time_steps - 1) : 0.0f; const AffineSpace3ff space = spaces.interpolate(time); avector verts(num_vertices); for (size_t i = 0; i < num_vertices; i++) { verts[i] = xfmPoint((AffineSpace3fa) space, positions_in[t][i]); } positions_out.push_back(std::move(verts)); } } return positions_out; } template std::vector > transformMSMBlurNormalBuffer(const std::vector >& normals_in, const Transformations& spaces) { if (normals_in.size() == 0) return normals_in; std::vector > normals_out; const size_t num_time_steps = normals_in.size(); const size_t num_vertices = normals_in[0].size(); /* if we have only one set of vertices, use transformation to generate more vertex sets */ if (num_time_steps == 1) { for (size_t i = 0; i < spaces.size(); i++) { avector norms(num_vertices); for (size_t j = 0; j < num_vertices; j++) { norms[j] = xfmNormal((AffineSpace3fa) spaces[i], normals_in[0][j]); } normals_out.push_back(std::move(norms)); } } /* otherwise transform all vertex sets with interpolated transformation */ else { for (size_t t = 0; t < num_time_steps; t++) { float time = num_time_steps > 1 ? float(t) / float(num_time_steps - 1) : 0.0f; const AffineSpace3ff space = spaces.interpolate(time); avector norms(num_vertices); for (size_t i = 0; i < num_vertices; i++) { norms[i] = xfmNormal((AffineSpace3fa) space, normals_in[t][i]); } normals_out.push_back(std::move(norms)); } } return normals_out; } struct GroupNode : public Node { GroupNode(const size_t N = 0) { children.resize(N); } GroupNode(std::vector >& children) : children(children) { } size_t size() const { return children.size(); } void add(const Ref& node) { if (node) children.push_back(node); } void set(const size_t i, const Ref& node) { children[i] = node; } Ref child(size_t i) const { return children[i]; } virtual BBox3fa bounds() const { BBox3fa b = empty; for (auto& c: children) b.extend(c->bounds()); return b; } virtual LBBox3fa lbounds() const { LBBox3fa b = empty; for (auto& c: children) b.extend(c->lbounds()); return b; } virtual size_t numPrimitives() const { size_t n = 0; for (auto& child: children) n += child->numPrimitives(); return n; } virtual void setMaterial(Ref material) { for (auto& child: children) child->setMaterial(material); } virtual void print(std::ostream& cout, int depth); virtual void calculateInDegree(); virtual bool calculateClosed(bool group_instancing); virtual void resetInDegree(); public: std::vector > children; }; struct MaterialNode : public Node { ALIGNED_STRUCT_USM_(16) MaterialNode(const std::string& name = "") : Node(name) { } virtual Material* material() = 0; virtual void print(std::ostream& cout, int depth); }; /*! Mesh. */ struct TriangleMeshNode : public Node { typedef Vec3fa Vertex; struct Triangle { public: Triangle() { } Triangle(int v0, int v1, int v2) : v0(v0), v1(v1), v2(v2) { } public: int v0, v1, v2; }; public: TriangleMeshNode(const avector& positions_in, const avector& normals_in, const std::vector& texcoords, const std::vector& triangles, Ref material) : Node(true), time_range(0.0f, 1.0f), texcoords(texcoords), triangles(triangles), material(material) { positions.push_back(positions_in); normals.push_back(normals_in); } TriangleMeshNode(Ref material, const BBox1f time_range = BBox1f(0, 1), size_t numTimeSteps = 0) : Node(true), time_range(time_range), material(material) { for (size_t i = 0; i < numTimeSteps; i++) positions.push_back(avector()); } TriangleMeshNode(Ref imesh, const Transformations& spaces) : Node(true), time_range(imesh->time_range), positions(transformMSMBlurVec3faBuffer(imesh->positions, spaces)), normals(transformMSMBlurNormalBuffer(imesh->normals, spaces)), texcoords(imesh->texcoords), triangles(imesh->triangles), material(imesh->material) { } virtual void setMaterial(Ref material) { this->material = material; } virtual BBox3fa bounds() const { BBox3fa b = empty; for (const auto& p: positions) for (auto& x: p) b.extend(x); return b; } virtual LBBox3fa lbounds() const { avector bboxes(positions.size()); for (size_t t = 0; t < positions.size(); t++) { BBox3fa b = empty; for (auto& x: positions[t]) b.extend(x); bboxes[t] = b; } return LBBox3fa(bboxes); } virtual size_t numPrimitives() const { return triangles.size(); } size_t numVertices() const { assert(positions.size()); return positions[0].size(); } size_t numTimeSteps() const { return positions.size(); } size_t numBytes() const { return numPrimitives() * sizeof(Triangle) + numVertices() * numTimeSteps() * sizeof(Vertex); } void verify() const; virtual void print(std::ostream& cout, int depth); virtual void calculateInDegree(); virtual void resetInDegree(); public: BBox1f time_range; std::vector > positions; std::vector > normals; std::vector texcoords; std::vector triangles; Ref material; }; struct LightNode : public Node { virtual void print(std::ostream& cout, int depth); virtual void calculateStatistics(Statistics& stat); virtual bool calculateClosed(bool group_instancing); virtual LightType getType() const = 0; virtual Ref transform(const AffineSpace3fa& space) const = 0; virtual Ref lerp(const Ref& light1_in, float f) const = 0; virtual Ref get(float time) const = 0; }; template struct LightNodeImpl : public LightNode { ALIGNED_STRUCT_(16); LightNodeImpl(const Light& light) : light(light) { } virtual LightType getType() const { return light.getType(); } virtual Ref transform(const AffineSpace3fa& space) const { return new LightNodeImpl(light.transform(space)); } virtual Ref get(float time) const { return (LightNode *) this; } virtual Ref lerp(const Ref& light1_in, float f) const { const Ref > light1 = light1_in.dynamicCast >(); assert(light1); return new LightNodeImpl(Light::lerp(light, light1->light, f)); } Light light; }; enum InstancingMode { INSTANCING_NONE, INSTANCING_GEOMETRY, INSTANCING_GROUP, INSTANCING_FLATTENED, INSTANCING_MULTI_LEVEL }; Ref flatten(Ref node, InstancingMode mode); Ref flatten(Ref node, InstancingMode mode); } } #include "materials.h"