123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /**
- * @file llgltfscenemanager.cpp
- * @brief LLGLTFSceneManager class implementation.
- *
- * $LicenseInfo:firstyear=2024&license=viewergpl$
- *
- * Copyright (c) 2024, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "tinygltf/tiny_gltf.h"
- #include "llgltfscenemanager.h"
- #include "llrender.h"
- #include "llrenderutils.h" // For gl_draw_box_outline()
- #include "llappviewer.h"
- #include "llpipeline.h"
- #include "lltinygltfhelper.h"
- #include "llviewerobjectlist.h"
- #include "llviewershadermgr.h"
- #include "llviewerwindow.h" // For gDebugRaycast*
- using namespace LLGLTF;
- //static
- LLGLTFSceneManager::objects_set_t LLGLTFSceneManager::sObjects;
- //static
- void LLGLTFSceneManager::cleanup()
- {
- sObjects.clear();
- }
- //static
- std::string LLGLTFSceneManager::load(const std::string& filename,
- const LLUUID& handle_obj_id)
- {
- LLViewerObject* objp = gObjectList.findObject(handle_obj_id);
- if (!objp || objp->isDead() || !objp->asVolume() ||
- objp->mDrawable.isNull() || objp->isAttachment())
- {
- // *TODO: translate
- return "No valid object to act as a handle for the gLTF scene.";
- }
- if (filename.empty())
- {
- objp->mGLTFAsset = NULL;
- objects_set_t::iterator it = sObjects.find(objp);
- if (it != sObjects.end())
- {
- sObjects.erase(it);
- }
- // Ensure the draw info will be regenerated, so to show the handle
- // object. HB
- objp->markForUpdate(true);
- // No error
- return "";
- }
- tinygltf::Model model;
- if (!LLTinyGLTFHelper::loadModel(filename, model))
- {
- // *TODO: translate
- return "TinyGLTF could not load this model.";
- }
- LLPointer<Asset> assetp = new Asset();
- *assetp = model;
- // Bind a shader to satisfy LLVertexBuffer assertions
- gDebugProgram.bind();
- if (!assetp->allocateGLResources(filename, model))
- {
- // For now, this is the only identified cause for an failure. HB
- // *TODO: translate
- return "Too many vertices used in a primitive.";
- }
- assetp->updateTransforms();
- objp->mGLTFAsset = assetp;
- sObjects.emplace(objp);
- // Ensure the draw info will be regenerated, so to hide the handle
- // object. HB
- objp->markForUpdate(true);
- // No error
- return "";
- }
- //static
- bool LLGLTFSceneManager::lineSegmentIntersect(LLVOVolume* volp, Asset* assetp,
- const LLVector4a& start,
- const LLVector4a& end,
- S32 face, bool, bool,
- S32* node_hitp, S32* prim_hitp,
- LLVector4a* interp,
- LLVector2* tcoordp,
- LLVector4a* normp,
- LLVector4a* tgtp)
- {
- LLVector4a local_start, local_end, p, n, tn;
- LLVector2 tc;
- if (interp)
- {
- p = *interp;
- }
- if (tcoordp)
- {
- tc = *tcoordp;
- }
- if (normp)
- {
- n = *normp;
- }
- if (tgtp)
- {
- tn = *tgtp;
- }
- // 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.
- LLMatrix4a asset_to_agent = volp->getGLTFAssetToAgentTransform();
- LLMatrix4a agent_to_asset = asset_to_agent;
- agent_to_asset.invert();
- agent_to_asset.affineTransform(start, local_start);
- agent_to_asset.affineTransform(end, local_end);
- S32 hit_node_index = assetp->lineSegmentIntersect(local_start, local_end,
- &p, &tc, &n, &tn,
- prim_hitp);
- if (hit_node_index < 0)
- {
- return false;
- }
- local_end = p;
- if (node_hitp)
- {
- *node_hitp = hit_node_index;
- }
- if (interp)
- {
- asset_to_agent.affineTransform(p, *interp);
- }
- if (normp)
- {
- LLVector3 v_n(n.getF32ptr());
- normp->load3(volp->volumeDirectionToAgent(v_n).mV);
- normp->normalize3fast();
- }
- if (tgtp)
- {
- LLVector3 v_tn(tn.getF32ptr());
- LLVector4a trans_tangent;
- trans_tangent.load3(volp->volumeDirectionToAgent(v_tn).mV);
- LLVector4Logical mask;
- mask.clear();
- mask.setElement<3>();
- tgtp->setSelectWithMask(mask, tn, trans_tangent);
- tgtp->normalize3fast();
- }
- if (tcoordp)
- {
- *tcoordp = tc;
- }
- return true;
- }
- //static
- LLDrawable* LLGLTFSceneManager::lineSegmentIntersect(const LLVector4a& start,
- const LLVector4a& end,
- bool pick_transparent,
- bool pick_rigged,
- S32* node_hitp,
- S32* prim_hitp,
- LLVector4a* interp,
- LLVector2* tcoordp,
- LLVector4a* normp,
- LLVector4a* tgtp)
- {
- LLDrawable* drawp = NULL;
- LLVector4a local_end = end;
- LLVector4a position;
- for (objects_set_t::iterator it = sObjects.begin(), end = sObjects.end();
- it != end; )
- {
- LLVOVolume* volp = it->get()->asVolume();
- if (!volp || volp->isDead() || volp->mGLTFAsset.isNull())
- {
- it = sObjects.erase(it);
- continue;
- }
- // Temporary debug: always double check objects that have GLTF scenes
- // hanging off of them even if the ray does not intersect the object
- // bounds.
- if (lineSegmentIntersect(volp, volp->mGLTFAsset,start, local_end, -1,
- pick_transparent, pick_rigged, node_hitp, prim_hitp, &position,
- tcoordp, normp, tgtp))
- {
- local_end = position;
- if (interp)
- {
- *interp = position;
- }
- drawp = volp->mDrawable;
- }
- ++it;
- }
- return drawp;
- }
- //static
- void LLGLTFSceneManager::update()
- {
- for (objects_set_t::iterator it = sObjects.begin(), end = sObjects.end();
- it != end; )
- {
- LLViewerObject* objp = it->get();
- if (objp->isDead() || objp->mGLTFAsset.isNull())
- {
- it = sObjects.erase(it);
- }
- else
- {
- objp->mGLTFAsset->update();
- ++it;
- }
- }
- }
- //static
- void LLGLTFSceneManager::render(bool opaque, bool rigged)
- {
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- for (objects_set_t::iterator it = sObjects.begin(), end = sObjects.end();
- it != end; )
- {
- LLViewerObject* objp = it->get();
- if (objp->isDead() || objp->mGLTFAsset.isNull())
- {
- it = sObjects.erase(it);
- continue;
- }
- LLMatrix4a mat = objp->getGLTFAssetToAgentTransform();
- mat.matMul(mat, gGLModelView);
- gGL.pushMatrix();
- Asset* assetp = objp->mGLTFAsset;
- assetp->updateRenderTransforms(mat);
- assetp->render(opaque, rigged);
- gGL.popMatrix();
- ++it;
- }
- }
- // This function assumes that the appropriate shader is already bound and the
- // model view matrix for the asset is already set.
- static void draw_asset_box(LLViewerObject* objp, Asset* assetp)
- {
- gGL.pushMatrix();
- for (auto& node : assetp->mNodes)
- {
- if (node.mMesh == INVALID_INDEX)
- {
- continue;
- }
- Mesh& mesh = assetp->mMeshes[node.mMesh];
- gGL.loadMatrix(node.mRenderMatrix.getF32ptr());
- // Draw bounding box of mesh primitives
- gGL.color3f(0.f, 1.f, 1.f);
- for (auto& primitive : mesh.mPrimitives)
- {
- LLVolumeOctree* octreep = primitive.mOctree.get();
- if (octreep)
- {
- LLVolumeOctreeListenerNoOwnership* listenerp =
- (LLVolumeOctreeListenerNoOwnership*)octreep->getListener(0);
- if (listenerp)
- {
- LLVector4a center = listenerp->mBounds[0];
- LLVector4a size = listenerp->mBounds[1];
- gl_draw_box_outline(center, size);
- }
- }
- }
- }
- gGL.popMatrix();
- }
- //static
- void LLGLTFSceneManager::renderDebug()
- {
- 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();
- // Force update all mRenderMatrix, not just nodes with meshes
- for (objects_set_t::iterator it = sObjects.begin(), end = sObjects.end();
- it != end; ++it)
- {
- LLViewerObject* objp = it->get();
- if (objp->isDead() || objp->mGLTFAsset.isNull())
- {
- continue;
- }
- LLMatrix4a mat = objp->getGLTFAssetToAgentTransform();
- mat.matMul(mat, gGLModelView);
- for (auto& node : objp->mGLTFAsset->mNodes)
- {
- node.mRenderMatrix.matMul(node.mAssetMatrix, mat);
- }
- }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
- {
- for (objects_set_t::iterator it = sObjects.begin(),
- end = sObjects.end();
- it != end; ++it)
- {
- LLViewerObject* objp = it->get();
- if (!objp->isDead() && objp->mGLTFAsset.notNull())
- {
- draw_asset_box(objp, objp->mGLTFAsset);
- }
- }
- }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_NODES))
- {
- // Render nodes hierarchy
- for (U32 i = 0; i < 2; ++i)
- {
- LLGLDepthTest depth(GL_TRUE, i ? GL_TRUE : GL_FALSE,
- i ? GL_LEQUAL : GL_GREATER);
- LLGLState blend(GL_BLEND, i ? GL_FALSE : GL_TRUE);
- gGL.pushMatrix();
- for (objects_set_t::iterator it = sObjects.begin(),
- end = sObjects.end();
- it != end; ++it)
- {
- LLViewerObject* objp = it->get();
- if (objp->isDead() || objp->mGLTFAsset.isNull())
- {
- continue;
- }
- Asset* assetp = objp->mGLTFAsset;
- LLMatrix4a mat = objp->getGLTFAssetToAgentTransform();
- mat.matMul(mat, gGLModelView);
- for (auto& node : assetp->mNodes)
- {
- node.mRenderMatrix.matMul(node.mAssetMatrix, mat);
- gGL.loadMatrix(node.mRenderMatrix.getF32ptr());
- // Render x-axis red, y-axis green, z-axis blue
- gGL.color4f(1.f, 0.f, 0.f, 0.5f);
- gGL.begin(LLRender::LINES);
- gGL.vertex3f(0.f, 0.f, 0.f);
- gGL.vertex3f(1.f, 0.f, 0.f);
- gGL.end(true);
- gGL.color4f(0.f, 1.f, 0.f, 0.5f);
- gGL.begin(LLRender::LINES);
- gGL.vertex3f(0.f, 0.f, 0.f);
- gGL.vertex3f(0.f, 1.f, 0.f);
- gGL.end(true);
- gGL.color4f(0.f, 0.f, 1.f, 0.5f);
- gGL.begin(LLRender::LINES);
- gGL.vertex3f(0.f, 0.f, 0.f);
- gGL.vertex3f(0.f, 0.f, 1.f);
- gGL.end(true);
- // Render path to child nodes cyan
- gGL.color4f(0.f, 1.f, 1.f, 0.5f);
- gGL.begin(LLRender::LINES);
- for (auto& child_idx : node.mChildren)
- {
- Node& child = assetp->mNodes[child_idx];
- gGL.vertex3f(0.f, 0.f, 0.f);
- gGL.vertex3fv(child.mMatrix.getTranslation().getF32ptr());
- }
- gGL.end(true);
- }
- }
- gGL.popMatrix();
- }
- }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
- {
- S32 node_hit = -1;
- S32 prim_hit = -1;
- LLVector4a inter;
- LLDrawable* drawp = lineSegmentIntersect(gDebugRaycastStart,
- gDebugRaycastEnd, true, true,
- &node_hit, &prim_hit, &inter,
- NULL, NULL, NULL);
- if (drawp && !drawp->isDead() && drawp->getVObj() &&
- node_hit >= 0 && prim_hit >= 0)
- {
- Asset* assetp = drawp->getVObj()->mGLTFAsset;
- Node* nodep = &assetp->mNodes[node_hit];
- Primitive* primp =
- &assetp->mMeshes[nodep->mMesh].mPrimitives[prim_hit];
- gGL.pushMatrix();
- gGL.flush();
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- gGL.color3f(1.f, 0.f, 1.f);
- gl_draw_box_outline(inter, LLVector4a(0.1f, 0.1f, 0.1f, 0.f));
- gGL.loadMatrix(nodep->mRenderMatrix.getF32ptr());
- LLVolumeOctree* octreep = primp->mOctree.get();
- if (octreep)
- {
- LLVolumeOctreeListenerNoOwnership* listenerp =
- (LLVolumeOctreeListenerNoOwnership*)octreep->getListener(0);
- if (listenerp)
- {
- gl_draw_box_outline(listenerp->mBounds[0],
- listenerp->mBounds[1]);
- }
- }
- gGL.flush();
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- gGL.popMatrix();
- }
- }
- gDebugProgram.unbind();
- }
|