summaryrefslogtreecommitdiff
path: root/indra/newview/gltf/primitive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/gltf/primitive.cpp')
-rw-r--r--indra/newview/gltf/primitive.cpp480
1 files changed, 480 insertions, 0 deletions
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
new file mode 100644
index 0000000000..dca3ccf706
--- /dev/null
+++ b/indra/newview/gltf/primitive.cpp
@@ -0,0 +1,480 @@
+/**
+ * @file primitive.cpp
+ * @brief LL GLTF Implementation
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "asset.h"
+#include "../lltinygltfhelper.h"
+
+using namespace LL::GLTF;
+
+#ifndef __PRETTY_FUNCTION__
+#define __PRETTY_FUNCTION__ __FUNCSIG__
+#endif
+
+// copy one vec3 from src to dst
+template<class S, class T>
+void copyVec2(S* src, T& dst)
+{
+ LL_ERRS() << "TODO: implement " << __PRETTY_FUNCTION__ << LL_ENDL;
+}
+
+// copy one vec3 from src to dst
+template<class S, class T>
+void copyVec3(S* src, T& dst)
+{
+ LL_ERRS() << "TODO: implement " << __PRETTY_FUNCTION__ << LL_ENDL;
+}
+
+// copy one vec4 from src to dst
+template<class S, class T>
+void copyVec4(S* src, T& dst)
+{
+ LL_ERRS() << "TODO: implement " << __PRETTY_FUNCTION__ << LL_ENDL;
+}
+
+template<>
+void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst)
+{
+ dst.set(src[0], src[1]);
+}
+
+template<>
+void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst)
+{
+ dst.load3(src);
+}
+
+template<>
+void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
+{
+ dst.set(src[0], src[1], src[2], 255);
+}
+
+template<>
+void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst)
+{
+ dst.loadua(src);
+}
+
+// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
+template<class S, class T>
+void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count)
+{
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyVec2(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+}
+
+// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
+template<class S, class T>
+void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count)
+{
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyVec3(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+}
+
+// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
+template<class S, class T>
+void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count)
+{
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyVec3(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+}
+
+template<class S, class T>
+void copyAttributeArray(Asset& asset, const Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride)
+{
+ if (accessor.mType == TINYGLTF_TYPE_VEC2)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride;
+ copyVec2((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == TINYGLTF_TYPE_VEC3)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride;
+ copyVec3((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == TINYGLTF_TYPE_VEC4)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;
+ copyVec4((S*)src, dst, stride, accessor.mCount);
+ }
+ else
+ {
+ LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL;
+ }
+}
+
+template <class T>
+void Primitive::copyAttribute(Asset& asset, S32 accessorIdx, LLStrider<T>& dst)
+{
+ const Accessor& accessor = asset.mAccessors[accessorIdx];
+ const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
+ const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+ const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
+
+ if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT)
+ {
+ copyAttributeArray(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
+ {
+ copyAttributeArray(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
+ {
+ copyAttributeArray(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
+ }
+ else
+
+ {
+ LL_ERRS() << "Unsupported component type" << LL_ENDL;
+ }
+}
+
+void Primitive::allocateGLResources(Asset& asset)
+{
+ // allocate vertex buffer
+ // We diverge from the intent of the GLTF format here to work with our existing render pipeline
+ // GLTF wants us to copy the buffer views into GPU storage as is and build render commands that source that data.
+ // For our engine, though, it's better to rearrange the buffers at load time into a layout that's more consistent.
+ // The GLTF native approach undoubtedly works well if you can count on VAOs, but VAOs perform much worse with our scenes.
+
+ // get the number of vertices
+ U32 numVertices = 0;
+ for (auto& it : mAttributes)
+ {
+ const Accessor& accessor = asset.mAccessors[it.second];
+ numVertices = accessor.mCount;
+ break;
+ }
+
+ // get the number of indices
+ U32 numIndices = 0;
+ if (mIndices != INVALID_INDEX)
+ {
+ const Accessor& accessor = asset.mAccessors[mIndices];
+ numIndices = accessor.mCount;
+ }
+
+ // create vertex buffer
+ mVertexBuffer = new LLVertexBuffer(ATTRIBUTE_MASK);
+ mVertexBuffer->allocateBuffer(numVertices, numIndices);
+
+ bool needs_color = true;
+ bool needs_texcoord = true;
+ bool needs_normal = true;
+ bool needs_tangent = true;
+
+ // load vertex data
+ for (auto& it : mAttributes)
+ {
+ const std::string& attribName = it.first;
+
+ // load vertex data
+ if (attribName == "POSITION")
+ {
+ // load position data
+ LLStrider<LLVector4a> dst;
+ mVertexBuffer->getVertexStrider(dst);
+
+ copyAttribute(asset, it.second, dst);
+ }
+ else if (attribName == "NORMAL")
+ {
+ needs_normal = false;
+ // load normal data
+ LLStrider<LLVector4a> dst;
+ mVertexBuffer->getNormalStrider(dst);
+
+ copyAttribute(asset, it.second, dst);
+ }
+ else if (attribName == "TANGENT")
+ {
+ needs_tangent = false;
+ // load tangent data
+
+ LLStrider<LLVector4a> dst;
+ mVertexBuffer->getTangentStrider(dst);
+
+ copyAttribute(asset, it.second, dst);
+ }
+ else if (attribName == "COLOR_0")
+ {
+ needs_color = false;
+ // load color data
+
+ LLStrider<LLColor4U> dst;
+ mVertexBuffer->getColorStrider(dst);
+
+ copyAttribute(asset, it.second, dst);
+ }
+ else if (attribName == "TEXCOORD_0")
+ {
+ needs_texcoord = false;
+ // load texcoord data
+ LLStrider<LLVector2> dst;
+ mVertexBuffer->getTexCoord0Strider(dst);
+
+ LLStrider<LLVector2> tc = dst;
+ copyAttribute(asset, it.second, dst);
+
+ // convert to OpenGL coordinate space
+ for (U32 i = 0; i < numVertices; ++i)
+ {
+ tc->mV[1] = 1.0f - tc->mV[1];;
+ tc++;
+ }
+ }
+ }
+
+ // copy index buffer
+ if (mIndices != INVALID_INDEX)
+ {
+ const Accessor& accessor = asset.mAccessors[mIndices];
+ const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
+ const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
+
+ LLStrider<U16> dst;
+ mVertexBuffer->getIndexStrider(dst);
+ mIndexArray.resize(numIndices);
+
+ if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
+ {
+ for (U32 i = 0; i < numIndices; ++i)
+ {
+ *(dst++) = (U16) * (U32*)src;
+ src += sizeof(U32);
+ }
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
+ {
+ for (U32 i = 0; i < numIndices; ++i)
+ {
+ *(dst++) = *(U16*)src;
+ src += sizeof(U16);
+ }
+ }
+ else
+ {
+ LL_ERRS("GLTF") << "Unsupported component type for indices" << LL_ENDL;
+ }
+
+ U16* idx = (U16*)mVertexBuffer->getMappedIndices();
+ for (U32 i = 0; i < numIndices; ++i)
+ {
+ mIndexArray[i] = idx[i];
+ }
+ }
+
+ // fill in default values for missing attributes
+ if (needs_color)
+ { // set default color
+ LLStrider<LLColor4U> dst;
+ mVertexBuffer->getColorStrider(dst);
+ for (U32 i = 0; i < numVertices; ++i)
+ {
+ *(dst++) = LLColor4U(255, 255, 255, 255);
+ }
+ }
+
+ if (needs_texcoord)
+ { // set default texcoord
+ LLStrider<LLVector2> dst;
+ mVertexBuffer->getTexCoord0Strider(dst);
+ for (U32 i = 0; i < numVertices; ++i)
+ {
+ *(dst++) = LLVector2(0.0f, 0.0f);
+ }
+ }
+
+ if (needs_normal)
+ { // set default normal
+ LLStrider<LLVector4a> dst;
+ mVertexBuffer->getNormalStrider(dst);
+ for (U32 i = 0; i < numVertices; ++i)
+ {
+ *(dst++) = LLVector4a(0.0f, 0.0f, 1.0f, 0.0f);
+ }
+ }
+
+ if (needs_tangent)
+ { // TODO: generate tangents if needed
+ LLStrider<LLVector4a> dst;
+ mVertexBuffer->getTangentStrider(dst);
+ for (U32 i = 0; i < numVertices; ++i)
+ {
+ *(dst++) = LLVector4a(1.0f, 0.0f, 0.0f, 1.0f);
+ }
+ }
+
+ mPositions.resize(numVertices);
+ mTexCoords.resize(numVertices);
+ mNormals.resize(numVertices);
+ mTangents.resize(numVertices);
+
+ LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX));
+ LLVector2* tc = (LLVector2*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TEXCOORD0));
+ LLVector4a* norm = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_NORMAL));
+ LLVector4a* tangent = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TANGENT));
+ for (U32 i = 0; i < numVertices; ++i)
+ {
+ mPositions[i] = pos[i];
+ mTexCoords[i] = tc[i];
+ mNormals[i] = norm[i];
+ mTangents[i] = tangent[i];
+ }
+ createOctree();
+
+ mVertexBuffer->unmapBuffer();
+}
+
+void Primitive::createOctree()
+{
+ // create octree
+ mOctree = new LLVolumeOctree();
+
+ if (mMode == TINYGLTF_MODE_TRIANGLES)
+ {
+ F32 scaler = 0.25f;
+
+ const U32 num_triangles = mVertexBuffer->getNumIndices() / 3;
+ // Initialize all the triangles we need
+ mOctreeTriangles.resize(num_triangles);
+
+ LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX));
+ U16* indices = (U16*)mVertexBuffer->getMappedIndices();
+
+ for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
+ { //for each triangle
+ const U32 index = triangle_index * 3;
+ LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
+ const LLVector4a& v0 = pos[indices[index]];
+ const LLVector4a& v1 = pos[indices[index + 1]];
+ const LLVector4a& v2 = pos[indices[index + 2]];
+
+ //store pointers to vertex data
+ tri->mV[0] = &v0;
+ tri->mV[1] = &v1;
+ tri->mV[2] = &v2;
+
+ //store indices
+ tri->mIndex[0] = indices[index];
+ tri->mIndex[1] = indices[index + 1];
+ tri->mIndex[2] = indices[index + 2];
+
+ //get minimum point
+ LLVector4a min = v0;
+ min.setMin(min, v1);
+ min.setMin(min, v2);
+
+ //get maximum point
+ LLVector4a max = v0;
+ max.setMax(max, v1);
+ max.setMax(max, v2);
+
+ //compute center
+ LLVector4a center;
+ center.setAdd(min, max);
+ center.mul(0.5f);
+
+ tri->mPositionGroup = center;
+
+ //compute "radius"
+ LLVector4a size;
+ size.setSub(max, min);
+
+ tri->mRadius = size.getLength3().getF32() * scaler;
+
+ //insert
+ mOctree->insert(tri);
+ }
+ }
+ else
+ {
+ LL_ERRS() << "Unsupported Primitive mode" << LL_ENDL;
+ }
+
+ //remove unneeded octree layers
+ while (!mOctree->balance()) {}
+
+ //calculate AABB for each node
+ LLVolumeOctreeRebound rebound;
+ rebound.traverse(mOctree);
+}
+
+const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out)
+{
+ if (mOctree.isNull())
+ {
+ return nullptr;
+ }
+
+ LLVector4a dir;
+ dir.setSub(end, start);
+
+ F32 closest_t = 2.f; // must be larger than 1
+
+ //create a proxy LLVolumeFace for the raycast
+ LLVolumeFace face;
+ face.mPositions = mPositions.data();
+ face.mTexCoords = mTexCoords.data();
+ face.mNormals = mNormals.data();
+ face.mTangents = mTangents.data();
+ face.mIndices = mIndexArray.data();
+
+ face.mNumIndices = mIndexArray.size();
+ face.mNumVertices = mPositions.size();
+
+ LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
+ intersect.traverse(mOctree);
+
+ // null out proxy data so it doesn't get freed
+ face.mPositions = face.mNormals = face.mTangents = nullptr;
+ face.mIndices = nullptr;
+ face.mTexCoords = nullptr;
+
+ return intersect.mHitTriangle;
+}
+
+Primitive::~Primitive()
+{
+ mOctree = nullptr;
+}
+