diff options
23 files changed, 2015 insertions, 123 deletions
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 29eeebcf67..2f25a4359d 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -45,13 +45,13 @@ #include "llmatrix3a.h" #include "lloctree.h" #include "llvolume.h" -#include "llvolumeoctree.h" #include "llstl.h" #include "llsdserialize.h" #include "llvector4a.h" #include "llmatrix4a.h" #include "llmeshoptimizer.h" #include "lltimer.h" +#include "llvolumeoctree.h" #include "mikktspace/mikktspace.h" #include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file @@ -377,77 +377,6 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons } } -class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*> -{ -public: - const LLVolumeFace* mFace; - - LLVolumeOctreeRebound(const LLVolumeFace* face) - { - mFace = face; - } - - virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) - { //this is a depth first traversal, so it's safe to assum all children have complete - //bounding data - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); - - LLVector4a& min = node->mExtents[0]; - LLVector4a& max = node->mExtents[1]; - - if (!branch->isEmpty()) - { //node has data, find AABB that binds data set - const LLVolumeTriangle* tri = *(branch->getDataBegin()); - - //initialize min/max to first available vertex - min = *(tri->mV[0]); - max = *(tri->mV[0]); - - for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) - { //for each triangle in node - - //stretch by triangles in node - tri = *iter; - - min.setMin(min, *tri->mV[0]); - min.setMin(min, *tri->mV[1]); - min.setMin(min, *tri->mV[2]); - - max.setMax(max, *tri->mV[0]); - max.setMax(max, *tri->mV[1]); - max.setMax(max, *tri->mV[2]); - } - } - else if (branch->getChildCount() > 0) - { //no data, but child nodes exist - LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0); - - //initialize min/max to extents of first child - min = child->mExtents[0]; - max = child->mExtents[1]; - } - else - { - llassert(!branch->isLeaf()); // Empty leaf - } - - for (S32 i = 0; i < branch->getChildCount(); ++i) - { //stretch by child extents - LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); - min.setMin(min, child->mExtents[0]); - max.setMax(max, child->mExtents[1]); - } - - node->mBounds[0].setAdd(min, max); - node->mBounds[0].mul(0.5f); - - node->mBounds[1].setSub(max,min); - node->mBounds[1].mul(0.5f); - } -}; - //------------------------------------------------------------------- // statics //------------------------------------------------------------------- @@ -5509,7 +5438,6 @@ struct MikktData } }; - bool LLVolumeFace::cacheOptimize(bool gen_tangents) { //optimize for vertex cache according to Forsyth method: LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; @@ -5687,8 +5615,7 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe llassert(mNumIndices % 3 == 0); - mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL); - new LLVolumeOctreeListener(mOctree); + mOctree = new LLVolumeOctree(center, size); const U32 num_triangles = mNumIndices / 3; // Initialize all the triangles we need mOctreeTriangles = new LLVolumeTriangle[num_triangles]; @@ -5743,7 +5670,7 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe while (!mOctree->balance()) { } //calculate AABB for each node - LLVolumeOctreeRebound rebound(this); + LLVolumeOctreeRebound rebound; rebound.traverse(mOctree); if (gDebugGL) @@ -5756,12 +5683,12 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe void LLVolumeFace::destroyOctree() { delete mOctree; - mOctree = NULL; + mOctree = nullptr; delete[] mOctreeTriangles; - mOctreeTriangles = NULL; + mOctreeTriangles = nullptr; } -const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const +const LLVolumeOctree* LLVolumeFace::getOctree() const { return mOctree; } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index c2586601ae..cca284d9bc 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -41,6 +41,7 @@ template <class T, typename T_PTR> class LLOctreeNode; class LLVolumeFace; class LLVolume; class LLVolumeTriangle; +class LLVolumeOctree; #include "lluuid.h" #include "v4color.h" @@ -913,7 +914,7 @@ public: void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); void destroyOctree(); // Get a reference to the octree, which may be null - const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const; + const LLVolumeOctree* getOctree() const; enum { @@ -987,7 +988,7 @@ public: LLVector3 mNormalizedScale = LLVector3(1,1,1); private: - LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree; + LLVolumeOctree* mOctree; LLVolumeTriangle* mOctreeTriangles; BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE); diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp index 6894d04d3c..95c7cb0b5c 100644 --- a/indra/llmath/llvolumeoctree.cpp +++ b/indra/llmath/llvolumeoctree.cpp @@ -92,15 +92,15 @@ void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTria } LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, - const LLVolumeFace* face, F32* closest_t, + LLVolumeFace* face, F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) - : mFace(face), - mStart(start), + : mStart(start), mDir(dir), mIntersection(intersection), mTexCoord(tex_coord), mNormal(normal), mTangent(tangent), + mFace(face), mClosestT(closest_t), mHitFace(false) { @@ -139,7 +139,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL { *mClosestT = t; mHitFace = true; - + mHitTriangle = tri; if (mIntersection != NULL) { LLVector4a intersect = mDir; diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index d65bca5e52..d6f536b9ca 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -112,7 +112,6 @@ public: class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*> { public: - const LLVolumeFace* mFace; LLVector4a mStart; LLVector4a mDir; LLVector4a mEnd; @@ -121,10 +120,13 @@ public: LLVector4a* mNormal; LLVector4a* mTangent; F32* mClosestT; + LLVolumeFace* mFace; bool mHitFace; + const LLVolumeTriangle* mHitTriangle = nullptr; - LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, - const LLVolumeFace* face, F32* closest_t, + LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, + LLVolumeFace* face, + F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node); @@ -137,4 +139,91 @@ class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle, LLVolum virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch); }; +class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*> +{ +public: + LLVolumeOctreeRebound() + { + } + + virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) + { //this is a depth first traversal, so it's safe to assum all children have complete + //bounding data + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + LLVolumeOctreeListener* node = (LLVolumeOctreeListener*)branch->getListener(0); + + LLVector4a& min = node->mExtents[0]; + LLVector4a& max = node->mExtents[1]; + + if (!branch->isEmpty()) + { //node has data, find AABB that binds data set + const LLVolumeTriangle* tri = *(branch->getDataBegin()); + + //initialize min/max to first available vertex + min = *(tri->mV[0]); + max = *(tri->mV[0]); + + for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) + { //for each triangle in node + + //stretch by triangles in node + tri = *iter; + + min.setMin(min, *tri->mV[0]); + min.setMin(min, *tri->mV[1]); + min.setMin(min, *tri->mV[2]); + + max.setMax(max, *tri->mV[0]); + max.setMax(max, *tri->mV[1]); + max.setMax(max, *tri->mV[2]); + } + } + else if (branch->getChildCount() > 0) + { //no data, but child nodes exist + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*)branch->getChild(0)->getListener(0); + + //initialize min/max to extents of first child + min = child->mExtents[0]; + max = child->mExtents[1]; + } + else + { + llassert(!branch->isLeaf()); // Empty leaf + } + + for (S32 i = 0; i < branch->getChildCount(); ++i) + { //stretch by child extents + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*)branch->getChild(i)->getListener(0); + min.setMin(min, child->mExtents[0]); + max.setMax(max, child->mExtents[1]); + } + + node->mBounds[0].setAdd(min, max); + node->mBounds[0].mul(0.5f); + + node->mBounds[1].setSub(max, min); + node->mBounds[1].mul(0.5f); + } +}; + +class LLVolumeOctree : public LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>, public LLRefCount +{ +public: + LLVolumeOctree(const LLVector4a& center, const LLVector4a& size) + : + LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, nullptr), + LLRefCount() + { + new LLVolumeOctreeListener(this); + } + + LLVolumeOctree() + : LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(LLVector4a::getZero(), LLVector4a(1.f,1.f,1.f), nullptr), + LLRefCount() + { + new LLVolumeOctreeListener(this); + } +}; + #endif diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 4f5f30d7c2..dda3c1532d 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -657,7 +657,7 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto U16 idx = indicesp[i]; gGL.vertex3fv(pos[idx].getF32ptr()); } -} + } gGL.end(); gGL.flush(); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8494ba5b49..125f5b28ba 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -74,6 +74,9 @@ if (NOT HAVOK_TPV) endif (NOT HAVOK_TPV) set(viewer_SOURCE_FILES + gltfscenemanager.cpp + gltf/asset.cpp + gltf/primitive.cpp groupchatlistener.cpp llaccountingcostmanager.cpp llaisapi.cpp @@ -727,7 +730,10 @@ set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake + gltfscenemanager.h groupchatlistener.h + gltf/asset.h + gltf/primitive.h llaccountingcost.h llaccountingcostmanager.h llaisapi.h diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp new file mode 100644 index 0000000000..18f814c5b6 --- /dev/null +++ b/indra/newview/gltf/asset.cpp @@ -0,0 +1,209 @@ +/** + * @file asset.cpp + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "asset.h" +#include "llvolumeoctree.h" + +using namespace LL::GLTF; + +void Scene::updateTransforms(Asset& asset) +{ + LLMatrix4a identity; + identity.setIdentity(); + for (auto& nodeIndex : mNodes) + { + Node& node = asset.mNodes[nodeIndex]; + node.updateTransforms(asset, identity); + } +} + +void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +{ + for (auto& nodeIndex : mNodes) + { + Node& node = asset.mNodes[nodeIndex]; + node.updateRenderTransforms(asset, modelview); + } +} + +void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +{ + matMul(mMatrix, modelview, mRenderMatrix); + + for (auto& childIndex : mChildren) + { + Node& child = asset.mNodes[childIndex]; + child.updateRenderTransforms(asset, mRenderMatrix); + } +} + +LLMatrix4a inverse(const LLMatrix4a& mat); + +void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix) +{ + matMul(mMatrix, parentMatrix, mAssetMatrix); + mAssetMatrixInv = inverse(mAssetMatrix); + + for (auto& childIndex : mChildren) + { + Node& child = asset.mNodes[childIndex]; + child.updateTransforms(asset, mAssetMatrix); + } +} + +void Asset::updateTransforms() +{ + for (auto& scene : mScenes) + { + scene.updateTransforms(*this); + } +} + +void Asset::updateRenderTransforms(const LLMatrix4a& modelview) +{ +#if 0 + // traverse hierarchy and update render transforms from scratch + for (auto& scene : mScenes) + { + scene.updateRenderTransforms(*this, modelview); + } +#else + // use mAssetMatrix to update render transforms from node list + for (auto& node : mNodes) + { + if (node.mMesh != INVALID_INDEX) + { + matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); + } + } + +#endif + +} + +S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector4a* normal, // return the surface normal at the intersection point + LLVector4a* tangent, // return the surface tangent at the intersection point + S32* primitive_hitp +) +{ + S32 node_hit = -1; + S32 primitive_hit = -1; + + LLVector4a local_start; + LLVector4a asset_end = end; + LLVector4a local_end; + LLVector4a p; + + + for (auto& node : mNodes) + { + if (node.mMesh != INVALID_INDEX) + { + + bool newHit = false; + + // transform start and end to this node's local space + node.mAssetMatrixInv.affineTransform(start, local_start); + node.mAssetMatrixInv.affineTransform(asset_end, local_end); + + Mesh& mesh = mMeshes[node.mMesh]; + for (auto& primitive : mesh.mPrimitives) + { + const LLVolumeTriangle* tri = primitive.lineSegmentIntersect(local_start, local_end, &p, tex_coord, normal, tangent); + if (tri) + { + newHit = true; + local_end = p; + + // pointer math to get the node index + node_hit = &node - &mNodes[0]; + llassert(&mNodes[node_hit] == &node); + + //pointer math to get the primitive index + primitive_hit = &primitive - &mesh.mPrimitives[0]; + llassert(&mesh.mPrimitives[primitive_hit] == &primitive); + } + } + + if (newHit) + { + // shorten line segment on hit + node.mAssetMatrix.affineTransform(p, asset_end); + + // transform results back to asset space + if (intersection) + { + *intersection = asset_end; + } + + if (normal || tangent) + { + LLMatrix4 normalMatrix(node.mAssetMatrixInv.getF32ptr()); + + normalMatrix.transpose(); + + LLMatrix4a norm_mat; + norm_mat.loadu((F32*)normalMatrix.mMatrix); + + if (normal) + { + LLVector4a n = *normal; + F32 w = n.getF32ptr()[3]; + n.getF32ptr()[3] = 0.0f; + + norm_mat.affineTransform(n, *normal); + normal->getF32ptr()[3] = w; + } + + if (tangent) + { + LLVector4a t = *tangent; + F32 w = t.getF32ptr()[3]; + t.getF32ptr()[3] = 0.0f; + + norm_mat.affineTransform(t, *tangent); + tangent->getF32ptr()[3] = w; + } + } + } + } + } + + if (node_hit != -1) + { + if (primitive_hitp) + { + *primitive_hitp = primitive_hit; + } + } + + return node_hit; +} + + diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h new file mode 100644 index 0000000000..0f1d4e8993 --- /dev/null +++ b/indra/newview/gltf/asset.h @@ -0,0 +1,445 @@ +#pragma once + +/** + * @file asset.h + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llvertexbuffer.h" +#include "llvolumeoctree.h" +#include "../lltinygltfhelper.h" +#include "primitive.h" + +// LL GLTF Implementation +namespace LL +{ + namespace GLTF + { + constexpr S32 INVALID_INDEX = -1; + + class Asset; + + class Buffer + { + public: + std::vector<U8> mData; + std::string mName; + std::string mUri; + + const Buffer& operator=(const tinygltf::Buffer& src) + { + mData = src.data; + mName = src.name; + mUri = src.uri; + return *this; + } + }; + + class BufferView + { + public: + S32 mBuffer = INVALID_INDEX; + S32 mByteLength; + S32 mByteOffset; + S32 mByteStride; + S32 mTarget; + S32 mComponentType; + + std::string mName; + + const BufferView& operator=(const tinygltf::BufferView& src) + { + mBuffer = src.buffer; + mByteLength = src.byteLength; + mByteOffset = src.byteOffset; + mByteStride = src.byteStride; + mTarget = src.target; + mName = src.name; + return *this; + } + }; + + class Accessor + { + public: + S32 mBufferView = INVALID_INDEX; + S32 mByteOffset; + S32 mComponentType; + S32 mCount; + std::vector<double> mMax; + std::vector<double> mMin; + S32 mType; + bool mNormalized; + std::string mName; + + const Accessor& operator=(const tinygltf::Accessor& src) + { + mBufferView = src.bufferView; + mByteOffset = src.byteOffset; + mComponentType = src.componentType; + mCount = src.count; + mType = src.type; + mNormalized = src.normalized; + mName = src.name; + mMax = src.maxValues; + mMin = src.maxValues; + + return *this; + } + }; + + class Material + { + public: + // use LLFetchedGLTFMaterial for now, but eventually we'll want to use + // a more flexible GLTF material implementation instead of the fixed packing + // version we use for sharable GLTF material assets + LLPointer<LLFetchedGLTFMaterial> mMaterial; + std::string mName; + + const Material& operator=(const tinygltf::Material& src) + { + mName = src.name; + return *this; + } + + void allocateGLResources(Asset& asset) + { + // allocate material + mMaterial = new LLFetchedGLTFMaterial(); + } + }; + + class Mesh + { + public: + std::vector<Primitive> mPrimitives; + std::vector<double> mWeights; + std::string mName; + + const Mesh& operator=(const tinygltf::Mesh& src) + { + mPrimitives.resize(src.primitives.size()); + for (U32 i = 0; i < src.primitives.size(); ++i) + { + mPrimitives[i] = src.primitives[i]; + } + + mWeights = src.weights; + mName = src.name; + + return *this; + } + + void allocateGLResources(Asset& asset) + { + for (auto& primitive : mPrimitives) + { + primitive.allocateGLResources(asset); + } + } + + }; + + class Node + { + public: + LLMatrix4a mMatrix; //local transform + LLMatrix4a mRenderMatrix; //transform for rendering + LLMatrix4a mAssetMatrix; //transform from local to asset space + LLMatrix4a mAssetMatrixInv; //transform from asset to local space + + std::vector<S32> mChildren; + S32 mMesh = INVALID_INDEX; + std::string mName; + + const Node& operator=(const tinygltf::Node& src) + { + F32* dstMatrix = mMatrix.getF32ptr(); + + if (src.matrix.size() != 16) + { + mMatrix.setIdentity(); + } + else + { + for (U32 i = 0; i < 16; ++i) + { + dstMatrix[i] = (F32)src.matrix[i]; + } + } + + mChildren = src.children; + mMesh = src.mesh; + mName = src.name; + + return *this; + } + + // Set mRenderMatrix to a transform that can be used for the current render pass + // modelview -- parent's render matrix + void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); + + // update mAssetMatrix and mAssetMatrixInv + void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix); + + }; + + class Scene + { + public: + std::vector<S32> mNodes; + std::string mName; + + const Scene& operator=(const tinygltf::Scene& src) + { + mNodes = src.nodes; + mName = src.name; + + return *this; + } + + void updateTransforms(Asset& asset); + void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); + + }; + + class Texture + { + public: + S32 mSampler = INVALID_INDEX; + S32 mSource = INVALID_INDEX; + std::string mName; + + const Texture& operator=(const tinygltf::Texture& src) + { + mSampler = src.sampler; + mSource = src.source; + mName = src.name; + + return *this; + } + }; + + class Sampler + { + public: + S32 mMagFilter; + S32 mMinFilter; + S32 mWrapS; + S32 mWrapT; + std::string mName; + + const Sampler& operator=(const tinygltf::Sampler& src) + { + mMagFilter = src.magFilter; + mMinFilter = src.minFilter; + mWrapS = src.wrapS; + mWrapT = src.wrapT; + mName = src.name; + + return *this; + } + }; + + class Image + { + public: + std::string mName; + std::string mUri; + std::string mMimeType; + std::vector<U8> mData; + S32 mWidth; + S32 mHeight; + S32 mComponent; + S32 mBits; + LLPointer<LLViewerFetchedTexture> mTexture; + + const Image& operator=(const tinygltf::Image& src) + { + mName = src.name; + mUri = src.uri; + mMimeType = src.mimeType; + mData = src.image; + mWidth = src.width; + mHeight = src.height; + mComponent = src.component; + mBits = src.bits; + + return *this; + } + + void allocateGLResources() + { + // allocate texture + + } + }; + + // C++ representation of a GLTF Asset + class Asset : public LLRefCount + { + public: + std::vector<Scene> mScenes; + std::vector<Node> mNodes; + std::vector<Mesh> mMeshes; + std::vector<Material> mMaterials; + std::vector<Buffer> mBuffers; + std::vector<BufferView> mBufferViews; + std::vector<Texture> mTextures; + std::vector<Sampler> mSamplers; + std::vector<Image> mImages; + std::vector<Accessor> mAccessors; + + void allocateGLResources(const std::string& filename, const tinygltf::Model& model) + { + for (auto& mesh : mMeshes) + { + mesh.allocateGLResources(*this); + } + + for (auto& image : mImages) + { + image.allocateGLResources(); + } + + for (U32 i = 0; i < mMaterials.size(); ++i) + { + mMaterials[i].allocateGLResources(*this); + LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); + } + } + + // update asset-to-node and node-to-asset transforms + void updateTransforms(); + + // update node render transforms + void updateRenderTransforms(const LLMatrix4a& modelview); + + void renderOpaque() + { + for (auto& node : mNodes) + { + if (node.mMesh != INVALID_INDEX) + { + Mesh& mesh = mMeshes[node.mMesh]; + for (auto& primitive : mesh.mPrimitives) + { + gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + if (primitive.mMaterial != INVALID_INDEX) + { + Material& material = mMaterials[primitive.mMaterial]; + material.mMaterial->bind(); + } + primitive.mVertexBuffer->setBuffer(); + if (primitive.mVertexBuffer->getNumIndices() > 0) + { + primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0); + } + else + { + primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts()); + } + } + } + } + } + + // return the index of the node that the line segment intersects with, or -1 if no hit + // input and output values must be in this asset's local coordinate frame + S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection = nullptr, // return the intersection point + LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point + LLVector4a* normal = nullptr, // return the surface normal at the intersection point + LLVector4a* tangent = nullptr, // return the surface tangent at the intersection point + S32* primitive_hitp = nullptr // return the index of the primitive that was hit + ); + + const Asset& operator=(const tinygltf::Model& src) + { + mScenes.resize(src.scenes.size()); + for (U32 i = 0; i < src.scenes.size(); ++i) + { + mScenes[i] = src.scenes[i]; + } + + mNodes.resize(src.nodes.size()); + for (U32 i = 0; i < src.nodes.size(); ++i) + { + mNodes[i] = src.nodes[i]; + } + + mMeshes.resize(src.meshes.size()); + for (U32 i = 0; i < src.meshes.size(); ++i) + { + mMeshes[i] = src.meshes[i]; + } + + mMaterials.resize(src.materials.size()); + for (U32 i = 0; i < src.materials.size(); ++i) + { + mMaterials[i] = src.materials[i]; + } + + mBuffers.resize(src.buffers.size()); + for (U32 i = 0; i < src.buffers.size(); ++i) + { + mBuffers[i] = src.buffers[i]; + } + + mBufferViews.resize(src.bufferViews.size()); + for (U32 i = 0; i < src.bufferViews.size(); ++i) + { + mBufferViews[i] = src.bufferViews[i]; + } + + mTextures.resize(src.textures.size()); + for (U32 i = 0; i < src.textures.size(); ++i) + { + mTextures[i] = src.textures[i]; + } + + mSamplers.resize(src.samplers.size()); + for (U32 i = 0; i < src.samplers.size(); ++i) + { + mSamplers[i] = src.samplers[i]; + } + + mImages.resize(src.images.size()); + for (U32 i = 0; i < src.images.size(); ++i) + { + mImages[i] = src.images[i]; + } + + mAccessors.resize(src.accessors.size()); + for (U32 i = 0; i < src.accessors.size(); ++i) + { + mAccessors[i] = src.accessors[i]; + } + + return *this; + } + }; + } +} diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp new file mode 100644 index 0000000000..dca3ccf706 --- /dev/null +++ b/indra/newview/gltf/primitive.cpp @@ -0,0 +1,480 @@ +/** + * @file primitive.cpp + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "asset.h" +#include "../lltinygltfhelper.h" + +using namespace LL::GLTF; + +#ifndef __PRETTY_FUNCTION__ +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + +// copy one vec3 from src to dst +template<class S, class T> +void copyVec2(S* src, T& dst) +{ + LL_ERRS() << "TODO: implement " << __PRETTY_FUNCTION__ << LL_ENDL; +} + +// copy one vec3 from src to dst +template<class S, class T> +void copyVec3(S* src, T& dst) +{ + LL_ERRS() << "TODO: implement " << __PRETTY_FUNCTION__ << LL_ENDL; +} + +// copy one vec4 from src to dst +template<class S, class T> +void copyVec4(S* src, T& dst) +{ + LL_ERRS() << "TODO: implement " << __PRETTY_FUNCTION__ << LL_ENDL; +} + +template<> +void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst) +{ + dst.set(src[0], src[1]); +} + +template<> +void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) +{ + dst.load3(src); +} + +template<> +void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst) +{ + dst.set(src[0], src[1], src[2], 255); +} + +template<> +void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst) +{ + dst.loadua(src); +} + +// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy +template<class S, class T> +void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count) +{ + for (S32 i = 0; i < count; ++i) + { + copyVec2(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } +} + +// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy +template<class S, class T> +void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count) +{ + for (S32 i = 0; i < count; ++i) + { + copyVec3(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } +} + +// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy +template<class S, class T> +void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count) +{ + for (S32 i = 0; i < count; ++i) + { + copyVec3(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } +} + +template<class S, class T> +void copyAttributeArray(Asset& asset, const Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) +{ + if (accessor.mType == TINYGLTF_TYPE_VEC2) + { + S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride; + copyVec2((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == TINYGLTF_TYPE_VEC3) + { + S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride; + copyVec3((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == TINYGLTF_TYPE_VEC4) + { + S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; + copyVec4((S*)src, dst, stride, accessor.mCount); + } + else + { + LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL; + } +} + +template <class T> +void Primitive::copyAttribute(Asset& asset, S32 accessorIdx, LLStrider<T>& dst) +{ + const Accessor& accessor = asset.mAccessors[accessorIdx]; + const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; + const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; + + if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT) + { + copyAttributeArray(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + copyAttributeArray(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + copyAttributeArray(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); + } + else + + { + LL_ERRS() << "Unsupported component type" << LL_ENDL; + } +} + +void Primitive::allocateGLResources(Asset& asset) +{ + // allocate vertex buffer + // We diverge from the intent of the GLTF format here to work with our existing render pipeline + // GLTF wants us to copy the buffer views into GPU storage as is and build render commands that source that data. + // For our engine, though, it's better to rearrange the buffers at load time into a layout that's more consistent. + // The GLTF native approach undoubtedly works well if you can count on VAOs, but VAOs perform much worse with our scenes. + + // get the number of vertices + U32 numVertices = 0; + for (auto& it : mAttributes) + { + const Accessor& accessor = asset.mAccessors[it.second]; + numVertices = accessor.mCount; + break; + } + + // get the number of indices + U32 numIndices = 0; + if (mIndices != INVALID_INDEX) + { + const Accessor& accessor = asset.mAccessors[mIndices]; + numIndices = accessor.mCount; + } + + // create vertex buffer + mVertexBuffer = new LLVertexBuffer(ATTRIBUTE_MASK); + mVertexBuffer->allocateBuffer(numVertices, numIndices); + + bool needs_color = true; + bool needs_texcoord = true; + bool needs_normal = true; + bool needs_tangent = true; + + // load vertex data + for (auto& it : mAttributes) + { + const std::string& attribName = it.first; + + // load vertex data + if (attribName == "POSITION") + { + // load position data + LLStrider<LLVector4a> dst; + mVertexBuffer->getVertexStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "NORMAL") + { + needs_normal = false; + // load normal data + LLStrider<LLVector4a> dst; + mVertexBuffer->getNormalStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "TANGENT") + { + needs_tangent = false; + // load tangent data + + LLStrider<LLVector4a> dst; + mVertexBuffer->getTangentStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "COLOR_0") + { + needs_color = false; + // load color data + + LLStrider<LLColor4U> dst; + mVertexBuffer->getColorStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "TEXCOORD_0") + { + needs_texcoord = false; + // load texcoord data + LLStrider<LLVector2> dst; + mVertexBuffer->getTexCoord0Strider(dst); + + LLStrider<LLVector2> tc = dst; + copyAttribute(asset, it.second, dst); + + // convert to OpenGL coordinate space + for (U32 i = 0; i < numVertices; ++i) + { + tc->mV[1] = 1.0f - tc->mV[1];; + tc++; + } + } + } + + // copy index buffer + if (mIndices != INVALID_INDEX) + { + const Accessor& accessor = asset.mAccessors[mIndices]; + const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; + const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + + const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; + + LLStrider<U16> dst; + mVertexBuffer->getIndexStrider(dst); + mIndexArray.resize(numIndices); + + if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + for (U32 i = 0; i < numIndices; ++i) + { + *(dst++) = (U16) * (U32*)src; + src += sizeof(U32); + } + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + for (U32 i = 0; i < numIndices; ++i) + { + *(dst++) = *(U16*)src; + src += sizeof(U16); + } + } + else + { + LL_ERRS("GLTF") << "Unsupported component type for indices" << LL_ENDL; + } + + U16* idx = (U16*)mVertexBuffer->getMappedIndices(); + for (U32 i = 0; i < numIndices; ++i) + { + mIndexArray[i] = idx[i]; + } + } + + // fill in default values for missing attributes + if (needs_color) + { // set default color + LLStrider<LLColor4U> dst; + mVertexBuffer->getColorStrider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLColor4U(255, 255, 255, 255); + } + } + + if (needs_texcoord) + { // set default texcoord + LLStrider<LLVector2> dst; + mVertexBuffer->getTexCoord0Strider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLVector2(0.0f, 0.0f); + } + } + + if (needs_normal) + { // set default normal + LLStrider<LLVector4a> dst; + mVertexBuffer->getNormalStrider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLVector4a(0.0f, 0.0f, 1.0f, 0.0f); + } + } + + if (needs_tangent) + { // TODO: generate tangents if needed + LLStrider<LLVector4a> dst; + mVertexBuffer->getTangentStrider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLVector4a(1.0f, 0.0f, 0.0f, 1.0f); + } + } + + mPositions.resize(numVertices); + mTexCoords.resize(numVertices); + mNormals.resize(numVertices); + mTangents.resize(numVertices); + + LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); + LLVector2* tc = (LLVector2*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TEXCOORD0)); + LLVector4a* norm = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_NORMAL)); + LLVector4a* tangent = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TANGENT)); + for (U32 i = 0; i < numVertices; ++i) + { + mPositions[i] = pos[i]; + mTexCoords[i] = tc[i]; + mNormals[i] = norm[i]; + mTangents[i] = tangent[i]; + } + createOctree(); + + mVertexBuffer->unmapBuffer(); +} + +void Primitive::createOctree() +{ + // create octree + mOctree = new LLVolumeOctree(); + + if (mMode == TINYGLTF_MODE_TRIANGLES) + { + F32 scaler = 0.25f; + + const U32 num_triangles = mVertexBuffer->getNumIndices() / 3; + // Initialize all the triangles we need + mOctreeTriangles.resize(num_triangles); + + LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); + U16* indices = (U16*)mVertexBuffer->getMappedIndices(); + + for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) + { //for each triangle + const U32 index = triangle_index * 3; + LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; + const LLVector4a& v0 = pos[indices[index]]; + const LLVector4a& v1 = pos[indices[index + 1]]; + const LLVector4a& v2 = pos[indices[index + 2]]; + + //store pointers to vertex data + tri->mV[0] = &v0; + tri->mV[1] = &v1; + tri->mV[2] = &v2; + + //store indices + tri->mIndex[0] = indices[index]; + tri->mIndex[1] = indices[index + 1]; + tri->mIndex[2] = indices[index + 2]; + + //get minimum point + LLVector4a min = v0; + min.setMin(min, v1); + min.setMin(min, v2); + + //get maximum point + LLVector4a max = v0; + max.setMax(max, v1); + max.setMax(max, v2); + + //compute center + LLVector4a center; + center.setAdd(min, max); + center.mul(0.5f); + + tri->mPositionGroup = center; + + //compute "radius" + LLVector4a size; + size.setSub(max, min); + + tri->mRadius = size.getLength3().getF32() * scaler; + + //insert + mOctree->insert(tri); + } + } + else + { + LL_ERRS() << "Unsupported Primitive mode" << LL_ENDL; + } + + //remove unneeded octree layers + while (!mOctree->balance()) {} + + //calculate AABB for each node + LLVolumeOctreeRebound rebound; + rebound.traverse(mOctree); +} + +const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out) +{ + if (mOctree.isNull()) + { + return nullptr; + } + + LLVector4a dir; + dir.setSub(end, start); + + F32 closest_t = 2.f; // must be larger than 1 + + //create a proxy LLVolumeFace for the raycast + LLVolumeFace face; + face.mPositions = mPositions.data(); + face.mTexCoords = mTexCoords.data(); + face.mNormals = mNormals.data(); + face.mTangents = mTangents.data(); + face.mIndices = mIndexArray.data(); + + face.mNumIndices = mIndexArray.size(); + face.mNumVertices = mPositions.size(); + + LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out); + intersect.traverse(mOctree); + + // null out proxy data so it doesn't get freed + face.mPositions = face.mNormals = face.mTangents = nullptr; + face.mIndices = nullptr; + face.mTexCoords = nullptr; + + return intersect.mHitTriangle; +} + +Primitive::~Primitive() +{ + mOctree = nullptr; +} + diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h new file mode 100644 index 0000000000..7c47d9dac5 --- /dev/null +++ b/indra/newview/gltf/primitive.h @@ -0,0 +1,140 @@ +#pragma once + +/** + * @file primitive.h + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llvertexbuffer.h" +#include "llvolumeoctree.h" + +// LL GLTF Implementation +namespace LL +{ + namespace GLTF + { + class Asset; + + constexpr U32 ATTRIBUTE_MASK = + LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_TANGENT | + LLVertexBuffer::MAP_COLOR; + + class Primitive + { + public: + ~Primitive(); + + // GPU copy of mesh data + LLPointer<LLVertexBuffer> mVertexBuffer; + + // CPU copy of mesh data + std::vector<LLVector2> mTexCoords; + std::vector<LLVector4a> mNormals; + std::vector<LLVector4a> mTangents; + std::vector<LLVector4a> mPositions; + std::vector<U16> mIndexArray; + + // raycast acceleration structure + LLPointer<LLVolumeOctree> mOctree; + std::vector<LLVolumeTriangle> mOctreeTriangles; + + S32 mMaterial = -1; + U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles + U32 mGLMode = LLRender::TRIANGLES; + S32 mIndices = -1; + std::unordered_map<std::string, int> mAttributes; + + // copy the attribute in the given BufferView to the given destination + // assumes destination has enough storage for the attribute + template<class T> + void copyAttribute(Asset& asset, S32 bufferViewIdx, LLStrider<T>& dst); + + // create octree based on vertex buffer + // must be called before buffer is unmapped and after buffer is populated with good data + void createOctree(); + + //get the LLVolumeTriangle that intersects with the given line segment at the point + //closest to start. Moves end to the point of intersection. Returns nullptr if no intersection. + //Line segment must be in the same coordinate frame as this Primitive + const LLVolumeTriangle* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection = NULL, // return the intersection point + LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point + LLVector4a* normal = NULL, // return the surface normal at the intersection point + LLVector4a* tangent = NULL // return the surface tangent at the intersection point + ); + + const Primitive& operator=(const tinygltf::Primitive& src) + { + // load material + mMaterial = src.material; + + // load mode + mMode = src.mode; + + // load indices + mIndices = src.indices; + + // load attributes + for (auto& it : src.attributes) + { + mAttributes[it.first] = it.second; + } + + switch (mMode) + { + case TINYGLTF_MODE_POINTS: + mGLMode = LLRender::POINTS; + break; + case TINYGLTF_MODE_LINE: + mGLMode = LLRender::LINES; + break; + case TINYGLTF_MODE_LINE_LOOP: + mGLMode = LLRender::LINE_LOOP; + break; + case TINYGLTF_MODE_LINE_STRIP: + mGLMode = LLRender::LINE_STRIP; + break; + case TINYGLTF_MODE_TRIANGLES: + mGLMode = LLRender::TRIANGLES; + break; + case TINYGLTF_MODE_TRIANGLE_STRIP: + mGLMode = LLRender::TRIANGLE_STRIP; + break; + case TINYGLTF_MODE_TRIANGLE_FAN: + mGLMode = LLRender::TRIANGLE_FAN; + break; + default: + mGLMode = GL_TRIANGLES; + } + + return *this; + } + + void allocateGLResources(Asset& asset); + }; + } +} diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp new file mode 100644 index 0000000000..429c9118f8 --- /dev/null +++ b/indra/newview/gltfscenemanager.cpp @@ -0,0 +1,469 @@ +/** + * @file gltfscenemanager.cpp + * @brief Builds menus out of items. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "gltfscenemanager.h" +#include "llviewermenufile.h" +#include "llappviewer.h" +#include "lltinygltfhelper.h" +#include "llvertexbuffer.h" +#include "llselectmgr.h" +#include "llagent.h" +#include "llnotificationsutil.h" +#include "llvoavatarself.h" +#include "llvolumeoctree.h" +#include "gltf/asset.h" +#include "pipeline.h" +#include "llviewershadermgr.h" + + +using namespace LL; + +// temporary location of LL GLTF Implementation +using namespace LL::GLTF; + +void GLTFSceneManager::load() +{ + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + + if (obj) + { + // Load a scene from disk + LLFilePickerReplyThread::startPicker( + [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) + { + if (LLAppViewer::instance()->quitRequested()) + { + return; + } + if (filenames.size() > 0) + { + GLTFSceneManager::instance().load(filenames[0]); + } + }, + LLFilePicker::FFLOAD_GLTF, + true); + } + else + { + LLNotificationsUtil::add("GLTFPreviewSelection"); + } +} + +void GLTFSceneManager::load(const std::string& filename) +{ + tinygltf::Model model; + LLTinyGLTFHelper::loadModel(filename, model); + + LLPointer<Asset> asset = new Asset(); + *asset = model; + + asset->allocateGLResources(filename, model); + asset->updateTransforms(); + + // hang the asset off the currently selected object, or off of the avatar if no object is selected + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + + if (obj) + { // assign to self avatar + obj->mGLTFAsset = asset; + mObjects.push_back(obj); + } +} + +GLTFSceneManager::~GLTFSceneManager() +{ + mObjects.clear(); +} + +LLMatrix4a getAssetToAgentTransform(LLViewerObject* obj) +{ + LLMatrix4 root; + root.initScale(obj->getScale()); + root.rotate(obj->getRenderRotation()); + root.translate(obj->getPositionAgent()); + + LLMatrix4a mat; + mat.loadu((F32*) root.mMatrix); + + return mat; +} + +LLMatrix4a getAgentToAssetTransform(LLViewerObject* obj) +{ + LLMatrix4 root; + LLVector3 scale = obj->getScale(); + scale.mV[0] = 1.f / scale.mV[0]; + scale.mV[1] = 1.f / scale.mV[1]; + scale.mV[2] = 1.f / scale.mV[2]; + + root.translate(-obj->getPositionAgent()); + root.rotate(~obj->getRenderRotation()); + + LLMatrix4 scale_mat; + scale_mat.initScale(scale); + + root *= scale_mat; + + + LLMatrix4a mat; + mat.loadu((F32*) root.mMatrix); + + return mat; +} + +void GLTFSceneManager::renderOpaque() +{ + // for debugging, just render the whole scene as opaque + // by traversing the whole scenegraph + // Assumes camera transform is already set and + // appropriate shader is already bound + + gGL.matrixMode(LLRender::MM_MODELVIEW); + + for (U32 i = 0; i < mObjects.size(); ++i) + { + if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr) + { + mObjects.erase(mObjects.begin() + i); + --i; + continue; + } + + Asset* asset = mObjects[i]->mGLTFAsset; + + gGL.pushMatrix(); + + LLMatrix4a mat = getAssetToAgentTransform(mObjects[i]); + + LLMatrix4a modelview; + modelview.loadu(gGLModelView); + + matMul(mat, modelview, modelview); + + asset->updateRenderTransforms(modelview); + asset->renderOpaque(); + + gGL.popMatrix(); + } +} + +LLMatrix4a inverse(const LLMatrix4a& mat) +{ + glh::matrix4f m((F32*)mat.mMatrix); + m = m.inverse(); + LLMatrix4a ret; + ret.loadu(m.m); + return ret; +} + +bool GLTFSceneManager::lineSegmentIntersect(LLVOVolume* obj, Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32* node_hit, S32* primitive_hit, + LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) + +{ + // line segment intersection test + // start and end should be in agent space + // volume space and asset space should be the same coordinate frame + // results should be transformed back to agent space + + bool ret = false; + + LLVector4a local_start; + LLVector4a local_end; + + LLMatrix4a asset_to_agent = getAssetToAgentTransform(obj); + LLMatrix4a agent_to_asset = inverse(asset_to_agent); + + agent_to_asset.affineTransform(start, local_start); + agent_to_asset.affineTransform(end, local_end); + + LLVector4a p; + LLVector4a n; + LLVector2 tc; + LLVector4a tn; + + if (intersection != NULL) + { + p = *intersection; + } + + if (tex_coord != NULL) + { + tc = *tex_coord; + } + + if (normal != NULL) + { + n = *normal; + } + + if (tangent != NULL) + { + tn = *tangent; + } + + S32 hit_node_index = asset->lineSegmentIntersect(local_start, local_end, &p, &tc, &n, &tn, primitive_hit); + + if (hit_node_index >= 0) + { + local_end = p; + if (node_hit != NULL) + { + *node_hit = hit_node_index; + } + + if (intersection != NULL) + { + asset_to_agent.affineTransform(p, *intersection); + } + + if (normal != NULL) + { + LLVector3 v_n(n.getF32ptr()); + normal->load3(obj->volumeDirectionToAgent(v_n).mV); + (*normal).normalize3fast(); + } + + if (tangent != NULL) + { + LLVector3 v_tn(tn.getF32ptr()); + + LLVector4a trans_tangent; + trans_tangent.load3(obj->volumeDirectionToAgent(v_tn).mV); + + LLVector4Logical mask; + mask.clear(); + mask.setElement<3>(); + + tangent->setSelectWithMask(mask, tn, trans_tangent); + (*tangent).normalize3fast(); + } + + if (tex_coord != NULL) + { + *tex_coord = tc; + } + + ret = true; + } + + return ret; +} + +LLDrawable* GLTFSceneManager::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + BOOL pick_transparent, + BOOL pick_rigged, + BOOL pick_unselectable, + BOOL pick_reflection_probe, + S32* node_hit, // return the index of the node that was hit + S32* primitive_hit, // return the index of the primitive that was hit + LLVector4a* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector4a* normal, // return the surface normal at the intersection point + LLVector4a* tangent) // return the surface tangent at the intersection point +{ + LLDrawable* drawable = nullptr; + + LLVector4a local_end = end; + LLVector4a position; + + for (U32 i = 0; i < mObjects.size(); ++i) + { + if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr || !mObjects[i]->getVolume()) + { + mObjects.erase(mObjects.begin() + i); + --i; + continue; + } + + // temporary debug -- always double check objects that have GLTF scenes hanging off of them even if the ray doesn't intersect the object bounds + if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset, start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent)) + { + local_end = position; + if (intersection) + { + *intersection = position; + } + drawable = mObjects[i]->mDrawable; + } + } + + return drawable; +} + +void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size); + +extern LLVector4a gDebugRaycastStart; +extern LLVector4a gDebugRaycastEnd; + +void renderOctreeRaycast(const LLVector4a& start, const LLVector4a& end, const LLVolumeOctree* octree); + +void renderAssetDebug(LLViewerObject* obj, Asset* asset) +{ + // render debug + // assumes appropriate shader is already bound + // assumes modelview matrix is already set + + gGL.pushMatrix(); + + // get raycast in asset space + LLMatrix4a asset_to_agent = getAssetToAgentTransform(obj); + LLMatrix4a agent_to_asset = getAgentToAssetTransform(obj); + + LLVector4a start; + LLVector4a end; + + agent_to_asset.affineTransform(gDebugRaycastStart, start); + agent_to_asset.affineTransform(gDebugRaycastEnd, end); + + + for (auto& node : asset->mNodes) + { + Mesh& mesh = asset->mMeshes[node.mMesh]; + + if (node.mMesh != INVALID_INDEX) + { + gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + + // draw bounding box of mesh primitives + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES)) + { + gGL.color3f(0.f, 1.f, 1.f); + + for (auto& primitive : mesh.mPrimitives) + { + auto* listener = (LLVolumeOctreeListener*) primitive.mOctree->getListener(0); + + LLVector4a center = listener->mBounds[0]; + LLVector4a size = listener->mBounds[1]; + + drawBoxOutline(center, size); + } + } + +#if 0 + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) + { + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + // convert raycast to node local space + LLVector4a local_start; + LLVector4a local_end; + + node.mAssetMatrixInv.affineTransform(start, local_start); + node.mAssetMatrixInv.affineTransform(end, local_end); + + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mOctree.notNull()) + { + renderOctreeRaycast(local_start, local_end, primitive.mOctree); + } + } + + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +#endif + } + } + + gGL.popMatrix(); +} + +void GLTFSceneManager::renderDebug() +{ + if (!gPipeline.hasRenderDebugMask( + LLPipeline::RENDER_DEBUG_BBOXES | + LLPipeline::RENDER_DEBUG_RAYCAST)) + { + return; + } + + gDebugProgram.bind(); + + LLGLDisable cullface(GL_CULL_FACE); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gPipeline.disableLights(); + + for (auto& obj : mObjects) + { + if (obj->isDead() || obj->mGLTFAsset == nullptr) + { + continue; + } + + Asset* asset = obj->mGLTFAsset; + + + LLMatrix4a mat = getAssetToAgentTransform(obj); + + LLMatrix4a modelview; + modelview.loadu(gGLModelView); + + matMul(mat, modelview, modelview); + + asset->updateRenderTransforms(modelview); + renderAssetDebug(obj, asset); + } + + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) + { + S32 node_hit = -1; + S32 primitive_hit = -1; + LLVector4a intersection; + + LLDrawable* drawable = lineSegmentIntersect(gDebugRaycastStart, gDebugRaycastEnd, TRUE, TRUE, TRUE, TRUE, &node_hit, &primitive_hit, &intersection, nullptr, nullptr, nullptr); + + if (drawable) + { + gGL.pushMatrix(); + Asset* asset = drawable->getVObj()->mGLTFAsset; + Node* node = &asset->mNodes[node_hit]; + Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit]; + + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + gGL.color3f(1, 0, 1); + drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f)); + + gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix); + + + + auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0); + drawBoxOutline(listener->mBounds[0], listener->mBounds[1]); + + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.popMatrix(); + } + } + gDebugProgram.unbind(); +} + diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h new file mode 100644 index 0000000000..50e1dd93da --- /dev/null +++ b/indra/newview/gltfscenemanager.h @@ -0,0 +1,64 @@ +#pragma once + +/** + * @file gltfscenemanager.h + * @brief Builds menus out of items. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llsingleton.h" +#include "llviewerobject.h" + +namespace LL +{ + class GLTFSceneManager : public LLSimpleton<GLTFSceneManager> + { + public: + ~GLTFSceneManager(); + // load GLTF file from disk + void load(); // open filepicker to choose asset + void load(const std::string& filename); // load asset from filename + void renderOpaque(); + + LLDrawable* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + BOOL pick_transparent, + BOOL pick_rigged, + BOOL pick_unselectable, + BOOL pick_reflection_probe, + S32* node_hit, // return the index of the node that was hit + S32* primitive_hit, // return the index of the primitive that was hit + LLVector4a* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector4a* normal, // return the surface normal at the intersection point + LLVector4a* tangent); // return the surface tangent at the intersection point + + bool lineSegmentIntersect(LLVOVolume* obj, GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32* face_hitp, S32* primitive_hitp, + LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); + + void renderDebug(); + + std::vector<LLPointer<LLViewerObject>> mObjects; + }; +} + + diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 31d9804fe3..46b95601af 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -240,6 +240,8 @@ #include "llavatariconctrl.h" #include "llgroupiconctrl.h" #include "llviewerassetstats.h" +#include "gltfscenemanager.h" + #include "workqueue.h" using namespace LL; @@ -1280,6 +1282,8 @@ bool LLAppViewer::init() LLWorld::createInstance(); LLSelectMgr::createInstance(); LLViewerCamera::createInstance(); + LL::GLTFSceneManager::createInstance(); + #if LL_WINDOWS if (!mSecondInstance) @@ -2155,7 +2159,7 @@ bool LLAppViewer::cleanup() ll_close_fail_log(); LLError::LLCallStacks::cleanup(); - + LL::GLTFSceneManager::deleteSingleton(); LLEnvironment::deleteSingleton(); LLSelectMgr::deleteSingleton(); LLViewerEventRecorder::deleteSingleton(); diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp index 86b790e2c5..a32382af92 100644 --- a/indra/newview/lldrawpoolpbropaque.cpp +++ b/indra/newview/lldrawpoolpbropaque.cpp @@ -30,6 +30,7 @@ #include "lldrawpoolpbropaque.h" #include "llviewershadermgr.h" #include "pipeline.h" +#include "gltfscenemanager.h" LLDrawPoolGLTFPBR::LLDrawPoolGLTFPBR(U32 type) : LLRenderPass(type) @@ -54,8 +55,11 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass) llassert(!LLPipeline::sRenderingHUDs); gDeferredPBROpaqueProgram.bind(); + + LL::GLTFSceneManager::instance().renderOpaque(); pushGLTFBatches(mRenderType); + gDeferredPBROpaqueProgram.bind(true); pushRiggedGLTFBatches(mRenderType + 1); } diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index fec550ae50..f3b5bb0565 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2866,10 +2866,8 @@ void renderLights(LLDrawable* drawablep) class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect { public: - - LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t) - : LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL) + : LLOctreeTriangleRayIntersect(start, dir, nullptr, closest_t, NULL, NULL, NULL, NULL) { } @@ -2893,7 +2891,7 @@ public: size.set(vl->mBounds[1].getF32ptr()); } - drawBoxOutline(center, size); + drawBoxOutline(center, size); for (U32 i = 0; i < 2; i++) { @@ -2937,6 +2935,13 @@ public: } }; +void renderOctreeRaycast(const LLVector4a& start, const LLVector4a& end, const LLVolumeOctree* octree) +{ + F32 t = 1.f; + LLRenderOctreeRaycast render(start, end, &t); + render.traverse(octree); +} + void renderRaycast(LLDrawable* drawablep) { if (drawablep->getNumFaces()) @@ -2994,29 +2999,22 @@ void renderRaycast(LLDrawable* drawablep) dir.setSub(end, start); gGL.flush(); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); { //render face positions - LLVertexBuffer::unbind(); - gGL.diffuseColor4f(0,1,1,0.5f); - glVertexPointer(3, GL_FLOAT, sizeof(LLVector4a), face.mPositions); - gGL.syncMatrices(); - glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices); + //gGL.diffuseColor4f(0,1,1,0.5f); + //LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mPositions, nullptr, face.mNumIndices, face.mIndices); } if (!volume->isUnique()) { - F32 t = 1.f; - if (!face.getOctree()) { ((LLVolumeFace*) &face)->createOctree(); } - LLRenderOctreeRaycast render(start, dir, &t); - - render.traverse(face.getOctree()); + renderOctreeRaycast(start, end, face.getOctree()); } gGL.popMatrix(); diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp index 5b75db37d0..40e6f285a8 100644 --- a/indra/newview/lltinygltfhelper.cpp +++ b/indra/newview/lltinygltfhelper.cpp @@ -147,7 +147,7 @@ const tinygltf::Image * LLTinyGLTFHelper::getImageFromTextureIndex(const tinyglt return nullptr; } -LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name) +LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name, bool flip) { const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index); LLImageRaw* rawImage = nullptr; @@ -159,14 +159,17 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny { name = image->name; rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component); - rawImage->verticalFlip(); + if (flip) + { + rawImage->verticalFlip(); + } rawImage->optimizeAwayAlpha(); } return rawImage; } -LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index) +LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, bool flip) { const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index); LLImageRaw* rawImage = nullptr; @@ -177,7 +180,10 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny image->component <= 4) { rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component); - rawImage->verticalFlip(); + if (flip) + { + rawImage->verticalFlip(); + } rawImage->optimizeAwayAlpha(); } @@ -237,7 +243,8 @@ bool LLTinyGLTFHelper::getMaterialFromModel( const tinygltf::Model& model_in, S32 mat_index, LLFetchedGLTFMaterial* material, - std::string& material_name) + std::string& material_name, + bool flip) { llassert(material); @@ -256,18 +263,18 @@ bool LLTinyGLTFHelper::getMaterialFromModel( material_name = material_in.name; // get base color texture - LLPointer<LLImageRaw> base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index); + LLPointer<LLImageRaw> base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, flip); // get normal map - LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index); + LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index, flip); // get metallic-roughness texture - LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index); + LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, flip); // get emissive texture - LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index); + LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index, flip); // get occlusion map if needed LLPointer<LLImageRaw> occlusion_img; if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index) { - occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index); + occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index, flip); } LLPointer<LLViewerFetchedTexture> base_color_tex; diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/lltinygltfhelper.h index 256f6c854f..da505b41e9 100644 --- a/indra/newview/lltinygltfhelper.h +++ b/indra/newview/lltinygltfhelper.h @@ -38,10 +38,8 @@ namespace LLTinyGLTFHelper { LLColor4 getColor(const std::vector<double>& in); const tinygltf::Image* getImageFromTextureIndex(const tinygltf::Model& model, S32 texture_index); - LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name); - LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index); - - LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index); + LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name, bool flip = true); + LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, bool flip = true); bool loadModel(const std::string& filename, tinygltf::Model& model_out); @@ -50,7 +48,8 @@ namespace LLTinyGLTFHelper const tinygltf::Model& model, S32 mat_index, LLFetchedGLTFMaterial* material, - std::string& material_name); + std::string& material_name, + bool flip = true); void initFetchedTextures(tinygltf::Material& material, LLPointer<LLImageRaw>& base_color_img, diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 37d02cd911..4cf948f20d 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -140,6 +140,7 @@ #include <boost/algorithm/string.hpp> #include "llcleanup.h" #include "llviewershadermgr.h" +#include "gltfscenemanager.h" using namespace LLAvatarAppearanceDefines; @@ -7921,6 +7922,17 @@ class LLAdvancedClickHDRIPreview: public view_listener_t } }; + +class LLAdvancedClickGLTFScenePreview : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools) + LL::GLTFSceneManager::instance().load(); + return true; + } +}; + // these are used in the gl menus to set control values that require shader recompilation class LLToggleShaderControl : public view_listener_t { @@ -9568,6 +9580,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile"); view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark"); view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview"); + view_listener_t::addMenu(new LLAdvancedClickGLTFScenePreview(), "Advanced.ClickGLTFScenePreview"); view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache"); view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 0c4d958aed..3d6903d177 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -45,6 +45,7 @@ #include "llbbox.h" #include "llrigginginfo.h" #include "llreflectionmap.h" +#include "gltf/asset.h" class LLAgent; // TODO: Get rid of this. class LLAudioSource; @@ -722,6 +723,8 @@ public: F32 mPhysicsDensity; F32 mPhysicsRestitution; + // Associated GLTF Asset + LLPointer<LL::GLTF::Asset> mGLTFAsset; // Pipeline classes LLPointer<LLDrawable> mDrawable; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index d4390c46cb..bcecd3a7fe 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -4606,7 +4606,7 @@ LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32 *face_hitp, - LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) + LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) { if (!mbCanSelect @@ -4814,7 +4814,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& } } } - + return ret; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 85f4ccd86f..37bdcbf88c 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -112,6 +112,7 @@ #include "llscenemonitor.h" #include "llprogressview.h" #include "llcleanup.h" +#include "gltfscenemanager.h" #include "llenvironment.h" #include "llsettingsvo.h" @@ -4530,6 +4531,8 @@ void LLPipeline::renderDebug() } } + LL::GLTFSceneManager::instance().renderDebug(); + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION)) { //render visible selected group occlusion geometry gDebugProgram.bind(); @@ -6341,6 +6344,15 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, } } } + + S32 node_hit = -1; + S32 primitive_hit = -1; + LLDrawable* hit = LL::GLTFSceneManager::instance().lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, &node_hit, &primitive_hit, &position, tex_coord, normal, tangent); + if (hit) + { + drawable = hit; + local_end = position; + } if (!sPickAvatar) { @@ -6557,6 +6569,11 @@ void LLPipeline::renderGLTFObjects(U32 type, bool texture, bool rigged) gGL.loadMatrix(gGLModelView); gGLLastMatrix = NULL; + + if (!rigged) + { + LL::GLTFSceneManager::instance().renderOpaque(); + } } // Currently only used for shadows -Cosmic,2023-04-19 diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 145276db57..c2f04231fc 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2846,6 +2846,12 @@ function="World.EnvPreset" <menu_item_call.on_click function="Advanced.ClickHDRIPreview" /> </menu_item_call> + <menu_item_call + label="GLTF Scene Preview" + name="GLTF Scene Preview"> + <menu_item_call.on_click + function="Advanced.ClickGLTFScenePreview" /> + </menu_item_call> </menu> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 24036bd799..8015c7d875 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12361,5 +12361,16 @@ Would you like to save them first? name="okignore" yestext="OK"/> </notification> - + + <notification + icon="alertmodal.tga" + name="GLTFPreviewSelection" + type="alert"> + You must select an object to act as a handle to the GLTF asset you are previewing. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + </notifications> |