123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /**
- * @file llgltfprimitive.cpp
- * @brief LL GLTF 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 "linden_common.h"
- #include "tinygltf/tiny_gltf.h"
- #include "llgltfprimitive.h"
- #include "llgltfasset.h"
- #include "llgltfbufferutil.h"
- using namespace LLGLTF;
- Primitive::Primitive()
- : mMaterial(-1),
- mMode(TINYGLTF_MODE_TRIANGLES),
- mGLMode(LLRender::TRIANGLES),
- mIndices(-1)
- {
- }
- Primitive::~Primitive()
- {
- mOctree = NULL;
- }
- // Allocates a 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 is better to rearrange the buffers at
- // load time into a layout which is more consistent. The GLTF native approach
- // undoubtedly works well if you can count on VAOs, but VAOs perform much worse
- // with our scenes.
- // Returns true on success, or false on failure. HB
- bool Primitive::allocateGLResources(Asset& asset)
- {
- // Load vertex data
- for (auto& it : mAttributes)
- {
- const std::string& attribName = it.first;
- Accessor& accessor = asset.mAccessors[it.second];
- // load vertex data
- if (attribName == "POSITION")
- {
- copy(asset, accessor, mPositions);
- }
- else if (attribName == "NORMAL")
- {
- copy(asset, accessor, mNormals);
- }
- else if (attribName == "TANGENT")
- {
- copy(asset, accessor, mTangents);
- }
- else if (attribName == "COLOR_0")
- {
- copy(asset, accessor, mColors);
- }
- else if (attribName == "TEXCOORD_0")
- {
- copy(asset, accessor, mTexCoords);
- }
- else if (attribName == "JOINTS_0")
- {
- copy(asset, accessor, mJoints);
- }
- else if (attribName == "WEIGHTS_0")
- {
- copy(asset, accessor, mWeights);
- }
- }
- if (mPositions.size() > 65536)
- {
- // This would trigger an llerrs in LLVertexBuffer::allocateBuffer(). HB
- llwarns << "Too many vertices: " << mPositions.size() << ". Aborted."
- << llendl
- return false;
- }
- // Copy index buffer
- if (mIndices != INVALID_INDEX)
- {
- Accessor& accessor = asset.mAccessors[mIndices];
- copy(asset, accessor, mIndexArray);
- }
- U32 mask = ATTRIBUTE_MASK;
-
- if (!mWeights.empty())
- {
- mask |= LLVertexBuffer::MAP_WEIGHT4;
- }
- mVertexBuffer = new LLVertexBuffer(mask);
- // Double the size of the index buffer for 32-bit indices
- mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size() * 2);
- mVertexBuffer->setBuffer();
- mVertexBuffer->setPositionData(mPositions.data());
- if (!mIndexArray.empty())
- {
- mVertexBuffer->setIndexData(mIndexArray.data());
- }
- if (mTexCoords.empty())
- {
- mTexCoords.resize(mPositions.size());
- }
- // Flip texcoord y, upload, then flip back (keep the off-spec data in VRAM
- //only)
- for (auto& tc : mTexCoords)
- {
- tc[1] = 1.f - tc[1];
- }
- mVertexBuffer->setTexCoord0Data(mTexCoords.data());
- for (auto& tc : mTexCoords)
- {
- tc[1] = 1.f - tc[1];
- }
- if (mColors.empty())
- {
- mColors.resize(mPositions.size(), LLColor4U::white);
- }
-
- // bake material basecolor into color array
- if (mMaterial != INVALID_INDEX)
- {
- const Material& material = asset.mMaterials[mMaterial];
- LLColor4 baseColor = material.mMaterial->mBaseColor;
- for (auto& dst : mColors)
- {
- dst = LLColor4U(baseColor * LLColor4(dst));
- }
- }
- mVertexBuffer->setColorData(mColors.data());
- if (mNormals.empty())
- {
- mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
- }
-
- mVertexBuffer->setNormalData(mNormals.data());
- if (mTangents.empty())
- {
- // TODO: generate tangents if needed
- mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1));
- }
- mVertexBuffer->setTangentData(mTangents.data());
- if (!mWeights.empty())
- {
- std::vector<LLVector4a> weight_data;
- weight_data.resize(mWeights.size());
- F32 max_weight = 1.f - FLT_EPSILON * 100.f;
- LLVector4a maxw(max_weight, max_weight, max_weight, max_weight);
- for (U32 i = 0; i < mWeights.size(); ++i)
- {
- LLVector4a& w = weight_data[i];
- w.setMin(mWeights[i], maxw);
- w.add(mJoints[i]);
- }
- mVertexBuffer->setWeight4Data(weight_data.data());
- }
-
- createOctree();
-
- mVertexBuffer->unbind();
- return true;
- }
- static void init_octree_triangle(LLVolumeTriangle* trianglep, S32 i0, S32 i1,
- S32 i2, const LLVector4a& v0,
- const LLVector4a& v1, const LLVector4a& v2)
- {
- // Store pointers to vertex data
- trianglep->mV[0] = &v0;
- trianglep->mV[1] = &v1;
- trianglep->mV[2] = &v2;
- // Store indices
- trianglep->mIndex[0] = i0;
- trianglep->mIndex[1] = i1;
- trianglep->mIndex[2] = i2;
- // 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);
- trianglep->mPositionGroup = center;
- // Compute "radius"
- LLVector4a size;
- size.setSub(max, min);
- constexpr F32 scaler = 0.25f;
- trianglep->mRadius = size.getLength3().getF32() * scaler;
- }
- void Primitive::createOctree()
- {
- mOctree = new LLVolumeOctree();
- const U32 num_indices = mVertexBuffer->getNumIndices();
- if (num_indices < 3)
- {
- // Degenerate triangle: no volume ! HB
- llwarns << "Degenerate triangle found" << llendl;
- }
- else if (mMode == TINYGLTF_MODE_TRIANGLES)
- {
- const U32 num_triangles = num_indices / 3;
- // Initialize all the triangles we need
- mOctreeTriangles.resize(num_triangles);
- for (U32 tri_idx = 0; tri_idx < num_triangles; ++tri_idx)
- {
- const U32 index = tri_idx * 3;
- S32 i0 = mIndexArray[index];
- S32 i1 = mIndexArray[index + 1];
- S32 i2 = mIndexArray[index + 2];
- const LLVector4a& v0 = mPositions[i0];
- const LLVector4a& v1 = mPositions[i1];
- const LLVector4a& v2 = mPositions[i2];
-
- LLVolumeTriangle* trianglep = &mOctreeTriangles[tri_idx];
- init_octree_triangle(trianglep, i0, i1, i2, v0, v1, v2);
- mOctree->insert(trianglep);
- }
- }
- else if (mMode == TINYGLTF_MODE_TRIANGLE_STRIP)
- {
- const U32 num_triangles = num_indices - 2;
- // Initialize all the triangles we need
- mOctreeTriangles.resize(num_triangles);
- for (U32 tri_idx = 0; tri_idx < num_triangles; ++tri_idx)
- {
- const U32 index = tri_idx + 2;
- S32 i0 = mIndexArray[index];
- S32 i1 = mIndexArray[index - 1];
- S32 i2 = mIndexArray[index - 2];
- const LLVector4a& v0 = mPositions[i0];
- const LLVector4a& v1 = mPositions[i1];
- const LLVector4a& v2 = mPositions[i2];
- LLVolumeTriangle* trianglep = &mOctreeTriangles[tri_idx];
- init_octree_triangle(trianglep, i0, i1, i2, v0, v1, v2);
- mOctree->insert(trianglep);
- }
- }
- else if (mMode == TINYGLTF_MODE_TRIANGLE_FAN)
- {
- const U32 num_triangles = num_indices - 2;
- // Initialize all the triangles we need
- mOctreeTriangles.resize(num_triangles);
- for (U32 tri_idx = 0; tri_idx < num_triangles; ++tri_idx)
- {
- const U32 index = tri_idx + 2;
- S32 i0 = mIndexArray[0];
- S32 i1 = mIndexArray[index - 1];
- S32 i2 = mIndexArray[index - 2];
- const LLVector4a& v0 = mPositions[i0];
- const LLVector4a& v1 = mPositions[i1];
- const LLVector4a& v2 = mPositions[i2];
- LLVolumeTriangle* trianglep = &mOctreeTriangles[tri_idx];
- init_octree_triangle(trianglep, i0, i1, i2, v0, v1, v2);
- mOctree->insert(trianglep);
- }
- }
- else
- {
- llwarns << "Unsupported primitive mode: " << mMode << llendl;
- }
- // 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* interp,
- LLVector2* tcoordp,
- LLVector4a* normp,
- LLVector4a* tgtp)
- {
- if (mOctree.isNull())
- {
- return NULL;
- }
- 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 = NULL; // unreferenced
- face.mNumIndices = mIndexArray.size();
- face.mNumVertices = mPositions.size();
- LLOctreeTriangleRayIntersectNoOwnership intersect(start, dir, &face,
- &closest_t, interp,
- tcoordp, normp, tgtp);
- intersect.traverse(mOctree);
- // Null out proxy data so it does not get freed
- face.mPositions = face.mNormals = face.mTangents = NULL;
- face.mIndices = NULL;
- face.mTexCoords = NULL;
- return intersect.mHitTriangle;
- }
- const Primitive& 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;
- }
|