#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 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 mMax; std::vector 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 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 mPrimitives; std::vector 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 mChildren; S32 mParent = INVALID_INDEX; S32 mMesh = INVALID_INDEX; std::string mName; const Node& operator=(const tinygltf::Node& src); // 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 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 mData; S32 mWidth; S32 mHeight; S32 mComponent; S32 mBits; LLPointer 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 mScenes; std::vector mNodes; std::vector mMeshes; std::vector mMaterials; std::vector mBuffers; std::vector mBufferViews; std::vector mTextures; std::vector mSamplers; std::vector mImages; std::vector mAccessors; void allocateGLResources(const std::string& filename, const tinygltf::Model& model) { // do images first as materials may depend on images for (auto& image : mImages) { image.allocateGLResources(); } // do materials before meshes as meshes may depend on materials for (U32 i = 0; i < mMaterials.size(); ++i) { mMaterials[i].allocateGLResources(*this); LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); } for (auto& mesh : mMeshes) { mesh.allocateGLResources(*this); } } // update asset-to-node and node-to-asset transforms void updateTransforms(); // update node render transforms void updateRenderTransforms(const LLMatrix4a& modelview); void render(bool opaque); void renderOpaque(); void renderTransparent(); // 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; } }; } }