#1126 gltf scene import prototype (#1172)

* #1126 GLTF Scene import initial prototype (working geometry import for some assets)

* #1126 WIP -- Expand support for more vertex formats, PoC material import, shadow support, scale support

* #1126 move GLTF implementation to newview/gltf

* #1126 Refactor attribute loading to be less copy/pasta for each combination of types

* #1126 Partially working object selection.  Ability to have multiple scenes at once.  Helpful message on how to use the preview button.

* #1126 Add bounding box debug display and untangle GLTF raycast from LLVOVolume raycast

* #1126 Working raycast on GLTF scenes.

* #1126 Remove some #pragma optimize offs
master
RunitaiLinden 2024-04-09 19:21:10 -05:00 committed by GitHub
parent 5a47a3cb23
commit b2a450a308
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 2015 additions and 123 deletions

View File

@ -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;
}

View File

@ -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);

View File

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

View File

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

View File

@ -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();
}

View File

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

View File

@ -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;
}

445
indra/newview/gltf/asset.h Normal file
View File

@ -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;
}
};
}
}

View File

@ -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;
}

View File

@ -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);
};
}
}

View File

@ -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();
}

View File

@ -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;
};
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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();

View File

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

View File

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

View File

@ -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");

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

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