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> | 
