* #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 offsmaster
parent
5a47a3cb23
commit
b2a450a308
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue