summaryrefslogtreecommitdiff
path: root/indra/newview/gltf
diff options
context:
space:
mode:
authorDave Parks <davep@lindenlab.com>2024-06-21 13:13:08 -0500
committerGitHub <noreply@github.com>2024-06-21 13:13:08 -0500
commit80ea30af1a8b38360f71c29cc45872c4399dab0d (patch)
treedb07a706a4c896b11b8abca8a7d8815797a8d0be /indra/newview/gltf
parent9fb9e8f33cb33a1535f43b4be030009c192ea92b (diff)
#1769 gltf optimization pass (#1816)
#1814 and #1517 Fix mirror update rate and occlusion culling
Diffstat (limited to 'indra/newview/gltf')
-rw-r--r--indra/newview/gltf/animation.cpp98
-rw-r--r--indra/newview/gltf/animation.h3
-rw-r--r--indra/newview/gltf/asset.cpp299
-rw-r--r--indra/newview/gltf/asset.h127
-rw-r--r--indra/newview/gltf/common.h14
-rw-r--r--indra/newview/gltf/primitive.cpp90
-rw-r--r--indra/newview/gltf/primitive.h21
7 files changed, 489 insertions, 163 deletions
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp
index 8b85eba3e5..3dff67d746 100644
--- a/indra/newview/gltf/animation.cpp
+++ b/indra/newview/gltf/animation.cpp
@@ -70,6 +70,14 @@ bool Animation::prep(Asset& asset)
}
}
+ for (auto& channel : mScaleChannels)
+ {
+ if (!channel.prep(asset, mSamplers[channel.mSampler]))
+ {
+ return false;
+ }
+ }
+
return true;
}
@@ -82,18 +90,37 @@ void Animation::update(Asset& asset, F32 dt)
void Animation::apply(Asset& asset, float time)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
+
// convert time to animation loop time
time = fmod(time, mMaxTime - mMinTime) + mMinTime;
// apply each channel
- for (auto& channel : mRotationChannels)
{
- channel.apply(asset, mSamplers[channel.mSampler], time);
+ LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - rotation");
+
+ for (auto& channel : mRotationChannels)
+ {
+ channel.apply(asset, mSamplers[channel.mSampler], time);
+ }
+ }
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - translation");
+
+ for (auto& channel : mTranslationChannels)
+ {
+ channel.apply(asset, mSamplers[channel.mSampler], time);
+ }
}
- for (auto& channel : mTranslationChannels)
{
- channel.apply(asset, mSamplers[channel.mSampler], time);
+ LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - scale");
+
+ for (auto& channel : mScaleChannels)
+ {
+ channel.apply(asset, mSamplers[channel.mSampler], time);
+ }
}
};
@@ -178,7 +205,8 @@ const Animation::Channel& Animation::Channel::operator=(const Value& src)
void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
{
- LL_PROFILE_ZONE_SCOPED;
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
+ llassert(mFrameTimes.size() > 1); // if there is only one frame, there is no need to interpolate
if (time < mMinTime)
{
@@ -187,32 +215,33 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F
return;
}
- if (mFrameTimes.size() > 1)
+ frameIndex = U32(mFrameTimes.size()) - 2;
+ t = 1.f;
+
+ if (time > mMaxTime)
{
- llassert(mFrameTimes.size() <= size_t(U32_MAX));
- frameIndex = U32(mFrameTimes.size()) - 2;
- t = 1.f;
+ return;
+ }
- if (time > mMaxTime)
- {
- return;
- }
+ if (time < mLastFrameTime)
+ {
+ mLastFrameIndex = 0;
+ }
- for (U32 i = 0; i < (U32)mFrameTimes.size() - 1; i++)
+ mLastFrameTime = time;
+
+ U32 idx = mLastFrameIndex;
+
+ for (U32 i = idx; i < (U32)mFrameTimes.size() - 1; i++)
+ {
+ if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1])
{
- if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1])
- {
- frameIndex = i;
- t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]);
- return;
- }
+ frameIndex = i;
+ t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]);
+ mLastFrameIndex = frameIndex;
+ return;
}
}
- else
- {
- frameIndex = 0;
- t = 0.0f;
- }
}
bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler)
@@ -231,14 +260,14 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
Node& node = asset.mNodes[mTarget.mNode];
- sampler.getFrameInfo(asset, time, frameIndex, t);
-
- if (sampler.mFrameTimes.size() == 1)
+ if (sampler.mFrameTimes.size() < 2)
{
node.setRotation(mRotations[0]);
}
else
{
+ sampler.getFrameInfo(asset, time, frameIndex, t);
+
// interpolate
quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t);
@@ -264,14 +293,14 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti
Node& node = asset.mNodes[mTarget.mNode];
- sampler.getFrameInfo(asset, time, frameIndex, t);
-
- if (sampler.mFrameTimes.size() == 1)
+ if (sampler.mFrameTimes.size() < 2)
{
node.setTranslation(mTranslations[0]);
}
else
{
+ sampler.getFrameInfo(asset, time, frameIndex, t);
+
// interpolate
const vec3& v0 = mTranslations[frameIndex];
const vec3& v1 = mTranslations[frameIndex + 1];
@@ -298,14 +327,14 @@ void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
Node& node = asset.mNodes[mTarget.mNode];
- sampler.getFrameInfo(asset, time, frameIndex, t);
-
- if (sampler.mFrameTimes.size() == 1)
+ if (sampler.mFrameTimes.size() < 2)
{
node.setScale(mScales[0]);
}
else
{
+ sampler.getFrameInfo(asset, time, frameIndex, t);
+
// interpolate
const vec3& v0 = mScales[frameIndex];
const vec3& v1 = mScales[frameIndex + 1];
@@ -373,6 +402,7 @@ Skin::~Skin()
void Skin::uploadMatrixPalette(Asset& asset)
{
// prepare matrix palette
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount();
diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h
index d5426fd4ce..ab8839470a 100644
--- a/indra/newview/gltf/animation.h
+++ b/indra/newview/gltf/animation.h
@@ -49,6 +49,9 @@ namespace LL
S32 mOutput = INVALID_INDEX;
std::string mInterpolation;
+ F32 mLastFrameTime = 0.f;
+ U32 mLastFrameIndex = 0;
+
bool prep(Asset& asset);
void serialize(boost::json::object& dst) const;
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 21be69aae2..a454e68a92 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -35,6 +35,7 @@
#include "buffer_util.h"
#include <boost/url.hpp>
#include "llimagejpeg.h"
+#include "../llskinningutil.h"
using namespace LL::GLTF;
using namespace boost::json;
@@ -86,7 +87,6 @@ namespace LL
}
}
-
void Scene::updateTransforms(Asset& asset)
{
mat4 identity = glm::identity<mat4>();
@@ -98,26 +98,6 @@ void Scene::updateTransforms(Asset& asset)
}
}
-void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview)
-{
- for (auto& nodeIndex : mNodes)
- {
- Node& node = asset.mNodes[nodeIndex];
- node.updateRenderTransforms(asset, modelview);
- }
-}
-
-void Node::updateRenderTransforms(Asset& asset, const mat4& modelview)
-{
- mRenderMatrix = modelview * mMatrix;
-
- for (auto& childIndex : mChildren)
- {
- Node& child = asset.mNodes[childIndex];
- child.updateRenderTransforms(asset, mRenderMatrix);
- }
-}
-
void Node::updateTransforms(Asset& asset, const mat4& parentMatrix)
{
makeMatrixValid();
@@ -137,19 +117,119 @@ void Node::updateTransforms(Asset& asset, const mat4& parentMatrix)
void Asset::updateTransforms()
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
for (auto& scene : mScenes)
{
scene.updateTransforms(*this);
}
+
+ uploadTransforms();
}
-void Asset::updateRenderTransforms(const mat4& modelview)
+void Asset::uploadTransforms()
{
- // use mAssetMatrix to update render transforms from node list
- for (auto& node : mNodes)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
+ // prepare matrix palette
+ U32 max_nodes = LLSkinningUtil::getMaxGLTFJointCount();
+
+ size_t node_count = llmin<size_t>(max_nodes, mNodes.size());
+
+ std::vector<mat4> t_mp;
+
+ t_mp.resize(node_count);
+
+ for (U32 i = 0; i < node_count; ++i)
{
- node.mRenderMatrix = modelview * node.mAssetMatrix;
+ Node& node = mNodes[i];
+ // build matrix palette in asset space
+ t_mp[i] = node.mAssetMatrix;
}
+
+ std::vector<F32> glmp;
+
+ glmp.resize(node_count * 12);
+
+ F32* mp = glmp.data();
+
+ for (U32 i = 0; i < node_count; ++i)
+ {
+ F32* m = glm::value_ptr(t_mp[i]);
+
+ U32 idx = i * 12;
+
+ mp[idx + 0] = m[0];
+ mp[idx + 1] = m[1];
+ mp[idx + 2] = m[2];
+ mp[idx + 3] = m[12];
+
+ mp[idx + 4] = m[4];
+ mp[idx + 5] = m[5];
+ mp[idx + 6] = m[6];
+ mp[idx + 7] = m[13];
+
+ mp[idx + 8] = m[8];
+ mp[idx + 9] = m[9];
+ mp[idx + 10] = m[10];
+ mp[idx + 11] = m[14];
+ }
+
+ if (mNodesUBO == 0)
+ {
+ glGenBuffers(1, &mNodesUBO);
+ }
+
+ glBindBuffer(GL_UNIFORM_BUFFER, mNodesUBO);
+ glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
+
+void Asset::uploadMaterials()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
+ // see pbrmetallicroughnessV.glsl for the layout of the material UBO
+ std::vector<vec4> md;
+
+ U32 material_size = sizeof(vec4) * 12;
+ U32 max_materials = gGLManager.mMaxUniformBlockSize / material_size;
+
+ U32 mat_count = (U32)mMaterials.size();
+ mat_count = llmin(mat_count, max_materials);
+
+ md.resize(mat_count * 12);
+
+ for (U32 i = 0; i < mat_count*12; i += 12)
+ {
+ Material& material = mMaterials[i/12];
+
+ // add texture transforms and UV indices
+ material.mPbrMetallicRoughness.mBaseColorTexture.mTextureTransform.getPacked(&md[i+0]);
+ md[i + 1].g = (F32)material.mPbrMetallicRoughness.mBaseColorTexture.getTexCoord();
+ material.mNormalTexture.mTextureTransform.getPacked(&md[i + 2]);
+ md[i + 3].g = (F32)material.mNormalTexture.getTexCoord();
+ material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTextureTransform.getPacked(&md[i+4]);
+ md[i + 5].g = (F32)material.mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord();
+ material.mEmissiveTexture.mTextureTransform.getPacked(&md[i + 6]);
+ md[i + 7].g = (F32)material.mEmissiveTexture.getTexCoord();
+ material.mOcclusionTexture.mTextureTransform.getPacked(&md[i + 8]);
+ md[i + 9].g = (F32)material.mOcclusionTexture.getTexCoord();
+
+ // add material properties
+ F32 min_alpha = material.mAlphaMode == Material::AlphaMode::MASK ? material.mAlphaCutoff : -1.0f;
+ md[i + 10] = vec4(material.mEmissiveFactor, 1.f);
+ md[i + 11] = vec4(0.f,
+ material.mPbrMetallicRoughness.mRoughnessFactor,
+ material.mPbrMetallicRoughness.mMetallicFactor,
+ min_alpha);
+ }
+
+ if (mMaterialsUBO == 0)
+ {
+ glGenBuffers(1, &mMaterialsUBO);
+ }
+
+ glBindBuffer(GL_UNIFORM_BUFFER, mMaterialsUBO);
+ glBufferData(GL_UNIFORM_BUFFER, md.size() * sizeof(vec4), md.data(), GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
@@ -363,6 +443,7 @@ const Image& Image::operator=(const Value& src)
void Asset::update()
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
F32 dt = gFrameTimeSeconds - mLastUpdateTime;
if (dt > 0.f)
@@ -383,11 +464,27 @@ void Asset::update()
{
skin.uploadMatrixPalette(*this);
}
+
+ uploadMaterials();
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltf - addTextureStats");
+
+ for (auto& image : mImages)
+ {
+ if (image.mTexture.notNull())
+ { // HACK - force texture to be loaded full rez
+ // TODO: calculate actual vsize
+ image.mTexture->addTextureStats(2048.f * 2048.f);
+ }
+ }
+ }
}
}
bool Asset::prep()
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
// check required extensions and fail if not supported
bool unsupported = false;
for (auto& extension : mExtensionsRequired)
@@ -445,6 +542,127 @@ bool Asset::prep()
}
}
+ // prepare vertex buffers
+
+ // material count is number of materials + 1 for default material
+ U32 mat_count = (U32) mMaterials.size() + 1;
+
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
+ }
+
+ for (S32 double_sided = 0; double_sided < 2; ++double_sided)
+ {
+ RenderData& rd = mRenderData[double_sided];
+ for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
+ {
+ rd.mBatches[i].resize(mat_count);
+ }
+
+ // for each material
+ for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
+ {
+ // for each shader variant
+ U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
+ U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
+
+ S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
+ if (ds_mat != double_sided)
+ {
+ continue;
+ }
+
+ for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
+ {
+ U32 attribute_mask = 0;
+ // for each mesh
+ for (auto& mesh : mMeshes)
+ {
+ // for each primitive
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
+ {
+ // accumulate vertex and index counts
+ primitive.mVertexOffset = vertex_count[variant];
+ primitive.mIndexOffset = index_count[variant];
+
+ vertex_count[variant] += primitive.getVertexCount();
+ index_count[variant] += primitive.getIndexCount();
+
+ // all primitives of a given variant and material should all have the same attribute mask
+ llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
+ attribute_mask |= primitive.mAttributeMask;
+ }
+ }
+ }
+
+ // allocate vertex buffer and pack it
+ if (vertex_count[variant] > 0)
+ {
+ U32 mat_idx = mat_id + 1;
+ LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
+
+ rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
+ vb->allocateBuffer(vertex_count[variant],
+ index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
+ vb->setBuffer();
+
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
+ {
+ primitive.upload(vb);
+ }
+ }
+ }
+
+ vb->unmapBuffer();
+
+ vb->unbind();
+ }
+ }
+ }
+ }
+
+ // sanity check that all primitives have a vertex buffer
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ llassert(primitive.mVertexBuffer.notNull());
+ }
+ }
+
+ // build render batches
+ for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
+ {
+ Node& node = mNodes[node_id];
+
+ if (node.mMesh != INVALID_INDEX)
+ {
+ auto& mesh = mMeshes[node.mMesh];
+
+ S32 mat_idx = mesh.mPrimitives[0].mMaterial + 1;
+
+ S32 double_sided = mat_idx == 0 ? 0 : mMaterials[mat_idx - 1].mDoubleSided;
+
+ for (S32 j = 0; j < mesh.mPrimitives.size(); ++j)
+ {
+ auto& primitive = mesh.mPrimitives[j];
+
+ S32 variant = primitive.mShaderVariant;
+
+ RenderData& rd = mRenderData[double_sided];
+ RenderBatch& rb = rd.mBatches[variant][mat_idx];
+
+ rb.mPrimitives.push_back({ j, node_id });
+ }
+ }
+ }
return true;
}
@@ -455,6 +673,7 @@ Asset::Asset(const Value& src)
bool Asset::load(std::string_view filename)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
mFilename = filename;
std::string ext = gDirUtilp->getExtension(mFilename);
@@ -903,14 +1122,14 @@ bool Image::save(Asset& asset, const std::string& folder)
return true;
}
-void Material::TextureInfo::serialize(object& dst) const
+void TextureInfo::serialize(object& dst) const
{
write(mIndex, "index", dst, INVALID_INDEX);
write(mTexCoord, "texCoord", dst, 0);
write_extensions(dst, &mTextureTransform, "KHR_texture_transform");
}
-S32 Material::TextureInfo::getTexCoord() const
+S32 TextureInfo::getTexCoord() const
{
if (mTextureTransform.mPresent && mTextureTransform.mTexCoord != INVALID_INDEX)
{
@@ -928,7 +1147,7 @@ bool Material::isMultiUV() const
mEmissiveTexture.getTexCoord() != 0;
}
-const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src)
+const TextureInfo& TextureInfo::operator=(const Value& src)
{
if (src.is_object())
{
@@ -940,23 +1159,23 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src)
return *this;
}
-bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const
+bool TextureInfo::operator==(const TextureInfo& rhs) const
{
return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord;
}
-bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const
+bool TextureInfo::operator!=(const TextureInfo& rhs) const
{
return !(*this == rhs);
}
-void Material::OcclusionTextureInfo::serialize(object& dst) const
+void OcclusionTextureInfo::serialize(object& dst) const
{
TextureInfo::serialize(dst);
write(mStrength, "strength", dst, 1.f);
}
-const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src)
+const OcclusionTextureInfo& OcclusionTextureInfo::operator=(const Value& src)
{
TextureInfo::operator=(src);
@@ -968,13 +1187,13 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(
return *this;
}
-void Material::NormalTextureInfo::serialize(object& dst) const
+void NormalTextureInfo::serialize(object& dst) const
{
TextureInfo::serialize(dst);
write(mScale, "scale", dst, 1.f);
}
-const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src)
+const NormalTextureInfo& NormalTextureInfo::operator=(const Value& src)
{
TextureInfo::operator=(src);
if (src.is_object())
@@ -1035,18 +1254,12 @@ void Material::Unlit::serialize(object& dst) const
// no members and object has already been created, nothing to do
}
-void TextureTransform::getPacked(F32* packed) const
+void TextureTransform::getPacked(vec4* packed) const
{
- packed[0] = mScale.x;
- packed[1] = mScale.y;
- packed[2] = mRotation;
- packed[3] = mOffset.x;
- packed[4] = mOffset.y;
-
- packed[5] = packed[6] = packed[7] = 0.f;
+ packed[0] = vec4(mScale.x, mScale.y, mRotation, mOffset.x);
+ packed[1] = vec4(mOffset.y, 0.f, 0.f, 0.f);
}
-
const TextureTransform& TextureTransform::operator=(const Value& src)
{
mPresent = true;
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index ea3f7d480a..27821659db 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -34,6 +34,7 @@
#include "boost/json.hpp"
#include "common.h"
#include "../llviewertexture.h"
+#include "llglslshader.h"
extern F32SecondsImplicit gFrameTimeSeconds;
@@ -65,14 +66,51 @@ namespace LL
vec2 mScale = vec2(1.f, 1.f);
S32 mTexCoord = INVALID_INDEX;
- // get the texture transform as a packed array of floats
- // dst MUST point to at least 8 floats
- void getPacked(F32* dst) const;
+ // get the texture transform as a packed array of vec4's
+ // dst MUST point to at least 2 vec4's
+ void getPacked(vec4* dst) const;
const TextureTransform& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
+ class TextureInfo
+ {
+ public:
+ S32 mIndex = INVALID_INDEX;
+ S32 mTexCoord = 0;
+
+ TextureTransform mTextureTransform;
+
+ bool operator==(const TextureInfo& rhs) const;
+ bool operator!=(const TextureInfo& rhs) const;
+
+ // get the UV channel that should be used for sampling this texture
+ // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord
+ S32 getTexCoord() const;
+
+ const TextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class NormalTextureInfo : public TextureInfo
+ {
+ public:
+ F32 mScale = 1.0f;
+
+ const NormalTextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class OcclusionTextureInfo : public TextureInfo
+ {
+ public:
+ F32 mStrength = 1.0f;
+
+ const OcclusionTextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
class Material
{
public:
@@ -91,42 +129,6 @@ namespace LL
BLEND
};
- class TextureInfo
- {
- public:
- S32 mIndex = INVALID_INDEX;
- S32 mTexCoord = 0;
-
- TextureTransform mTextureTransform;
-
- bool operator==(const TextureInfo& rhs) const;
- bool operator!=(const TextureInfo& rhs) const;
-
- // get the UV channel that should be used for sampling this texture
- // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord
- S32 getTexCoord() const;
-
- const TextureInfo& operator=(const Value& src);
- void serialize(boost::json::object& dst) const;
- };
-
- class NormalTextureInfo : public TextureInfo
- {
- public:
- F32 mScale = 1.0f;
-
- const NormalTextureInfo& operator=(const Value& src);
- void serialize(boost::json::object& dst) const;
- };
-
- class OcclusionTextureInfo : public TextureInfo
- {
- public:
- F32 mStrength = 1.0f;
-
- const OcclusionTextureInfo& operator=(const Value& src);
- void serialize(boost::json::object& dst) const;
- };
class PbrMetallicRoughness
{
@@ -179,7 +181,6 @@ namespace LL
{
public:
mat4 mMatrix = glm::identity<mat4>(); //local transform
- mat4 mRenderMatrix; //transform for rendering
mat4 mAssetMatrix; //transform from local to asset space
mat4 mAssetMatrixInv; //transform from asset to local space
@@ -206,10 +207,6 @@ namespace LL
const Node& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
- // Set mRenderMatrix to a transform that can be used for the current render pass
- // modelview -- parent's render matrix
- void updateRenderTransforms(Asset& asset, const mat4& modelview);
-
// update mAssetMatrix and mAssetMatrixInv
void updateTransforms(Asset& asset, const mat4& parentMatrix);
@@ -322,6 +319,31 @@ namespace LL
bool prep(Asset& asset);
};
+ // Render Batch -- vertex buffer and list of primitives to render using
+ // said vertex buffer
+ class RenderBatch
+ {
+ public:
+ struct PrimitiveData
+ {
+ S32 mPrimitiveIndex = INVALID_INDEX;
+ S32 mNodeIndex = INVALID_INDEX;
+ };
+
+ LLPointer<LLVertexBuffer> mVertexBuffer;
+ std::vector<PrimitiveData> mPrimitives;
+ };
+
+ class RenderData
+ {
+ public:
+ // list of render batches
+ // indexed by [material index + 1](0 is reserved for default material)
+ // there should be exactly one render batch per material per variant
+ std::vector<RenderBatch> mBatches[LLGLSLShader::NUM_GLTF_VARIANTS];
+ };
+
+
// C++ representation of a GLTF Asset
class Asset
{
@@ -359,6 +381,16 @@ namespace LL
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
+ // data used for rendering
+ // 0 - single sided
+ // 1 - double sided
+ RenderData mRenderData[2];
+
+ // UBO for storing node transforms
+ U32 mNodesUBO = 0;
+
+ // UBO for storing material data
+ U32 mMaterialsUBO = 0;
// prepare for first time use
bool prep();
@@ -373,8 +405,11 @@ namespace LL
// update asset-to-node and node-to-asset transforms
void updateTransforms();
- // update node render transforms
- void updateRenderTransforms(const mat4& modelview);
+ // upload matrices to UBO
+ void uploadTransforms();
+
+ // upload materils to UBO
+ void uploadMaterials();
// 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
diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h
index 4f660d7cfc..b9698d4017 100644
--- a/indra/newview/gltf/common.h
+++ b/indra/newview/gltf/common.h
@@ -64,6 +64,9 @@ namespace LL
class Asset;
class Material;
+ class TextureInfo;
+ class NormalTextureInfo;
+ class OcclusionTextureInfo;
class Mesh;
class Node;
class Scene;
@@ -78,6 +81,17 @@ namespace LL
class Accessor;
class BufferView;
class Buffer;
+
+ enum class TextureType : U8
+ {
+ BASE_COLOR = 0,
+ NORMAL,
+ METALLIC_ROUGHNESS,
+ OCCLUSION,
+ EMISSIVE
+ };
+
+ constexpr U32 TEXTURE_TYPE_COUNT = 5;
}
}
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index 2280c7004e..e1579374d4 100644
--- a/indra/newview/gltf/primitive.cpp
+++ b/indra/newview/gltf/primitive.cpp
@@ -380,11 +380,22 @@ bool Primitive::prep(Asset& asset)
}
}
}
+ else
+ { //everything must be indexed at runtime
+ mIndexArray.resize(mPositions.size());
+ for (U32 i = 0; i < mPositions.size(); ++i)
+ {
+ mIndexArray[i] = i;
+ }
+ }
U32 mask = LLVertexBuffer::MAP_VERTEX;
+ mShaderVariant = 0;
+
if (!mWeights.empty())
{
+ mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED;
mask |= LLVertexBuffer::MAP_WEIGHT4;
mask |= LLVertexBuffer::MAP_JOINT;
}
@@ -406,9 +417,6 @@ bool Primitive::prep(Asset& asset)
mColors.resize(mPositions.size(), LLColor4U::white);
}
- mShaderVariant = 0;
-
- // TODO: support colorless vertex buffers
mask |= LLVertexBuffer::MAP_COLOR;
bool unlit = false;
@@ -506,69 +514,79 @@ bool Primitive::prep(Asset& asset)
mask |= LLVertexBuffer::MAP_TANGENT;
}
- if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
- { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
- gDebugProgram.bind();
+ mAttributeMask = mask;
+
+ if (mMaterial != INVALID_INDEX)
+ {
+ Material& material = asset.mMaterials[mMaterial];
+ if (material.mAlphaMode == Material::AlphaMode::BLEND)
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ }
}
- mVertexBuffer = new LLVertexBuffer(mask);
+ createOctree();
+
+ return true;
+}
+
+void Primitive::upload(LLVertexBuffer* buffer)
+{
+ mVertexBuffer = buffer;
// we store these buffer sizes as S32 elsewhere
llassert(mPositions.size() <= size_t(S32_MAX));
llassert(mIndexArray.size() <= size_t(S32_MAX / 2));
- mVertexBuffer->allocateBuffer(U32(mPositions.size()), U32(mIndexArray.size() * 2)); // double the size of the index buffer for 32-bit indices
- mVertexBuffer->setBuffer();
- mVertexBuffer->setPositionData(mPositions.data());
- mVertexBuffer->setColorData(mColors.data());
+ llassert(mVertexBuffer != nullptr);
+
+ // assert that buffer can hold this primitive
+ llassert(mVertexBuffer->getNumVerts() >= mPositions.size() + mVertexOffset);
+ llassert(mVertexBuffer->getNumIndices() >= mIndexArray.size() + mIndexOffset);
+ llassert(mVertexBuffer->getTypeMask() == mAttributeMask);
+
+ U32 offset = mVertexOffset;
+ U32 count = getVertexCount();
+
+ mVertexBuffer->setPositionData(mPositions.data(), offset, count);
+ mVertexBuffer->setColorData(mColors.data(), offset, count);
if (!mNormals.empty())
{
- mVertexBuffer->setNormalData(mNormals.data());
+ mVertexBuffer->setNormalData(mNormals.data(), offset, count);
}
if (!mTangents.empty())
{
- mVertexBuffer->setTangentData(mTangents.data());
+ mVertexBuffer->setTangentData(mTangents.data(), offset, count);
}
if (!mWeights.empty())
{
- mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED;
- mVertexBuffer->setWeight4Data(mWeights.data());
- mVertexBuffer->setJointData(mJoints.data());
+ mVertexBuffer->setWeight4Data(mWeights.data(), offset, count);
+ mVertexBuffer->setJointData(mJoints.data(), offset, count);
}
// flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
vertical_flip(mTexCoords0);
- mVertexBuffer->setTexCoord0Data(mTexCoords0.data());
+ mVertexBuffer->setTexCoord0Data(mTexCoords0.data(), offset, count);
vertical_flip(mTexCoords0);
if (!mTexCoords1.empty())
{
vertical_flip(mTexCoords1);
- mVertexBuffer->setTexCoord1Data(mTexCoords1.data());
+ mVertexBuffer->setTexCoord1Data(mTexCoords1.data(), offset, count);
vertical_flip(mTexCoords1);
}
-
if (!mIndexArray.empty())
{
- mVertexBuffer->setIndexData(mIndexArray.data());
- }
-
- createOctree();
-
- mVertexBuffer->unbind();
-
- if (mMaterial != INVALID_INDEX)
- {
- Material& material = asset.mMaterials[mMaterial];
- if (material.mAlphaMode == Material::AlphaMode::BLEND)
+ std::vector<U32> index_array;
+ index_array.resize(mIndexArray.size());
+ for (U32 i = 0; i < mIndexArray.size(); ++i)
{
- mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ index_array[i] = mIndexArray[i] + mVertexOffset;
}
+ mVertexBuffer->setIndexData(index_array.data(), mIndexOffset, getIndexCount());
}
-
- return true;
}
void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
@@ -616,7 +634,7 @@ void Primitive::createOctree()
if (mMode == Mode::TRIANGLES)
{
- const U32 num_triangles = mVertexBuffer->getNumIndices() / 3;
+ const U32 num_triangles = getIndexCount() / 3;
// Initialize all the triangles we need
mOctreeTriangles.resize(num_triangles);
@@ -640,7 +658,7 @@ void Primitive::createOctree()
}
else if (mMode == Mode::TRIANGLE_STRIP)
{
- const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
+ const U32 num_triangles = getIndexCount() - 2;
// Initialize all the triangles we need
mOctreeTriangles.resize(num_triangles);
@@ -664,7 +682,7 @@ void Primitive::createOctree()
}
else if (mMode == Mode::TRIANGLE_FAN)
{
- const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
+ const U32 num_triangles = getIndexCount() - 2;
// Initialize all the triangles we need
mOctreeTriangles.resize(num_triangles);
diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h
index 7cc05cf831..304eb26432 100644
--- a/indra/newview/gltf/primitive.h
+++ b/indra/newview/gltf/primitive.h
@@ -54,10 +54,7 @@ namespace LL
~Primitive();
- // GPU copy of mesh data
- LLPointer<LLVertexBuffer> mVertexBuffer;
-
- // CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code
+ // CPU copy of mesh data
std::vector<LLVector2> mTexCoords0;
std::vector<LLVector2> mTexCoords1;
std::vector<LLVector4a> mNormals;
@@ -80,6 +77,17 @@ namespace LL
// shader variant according to LLGLSLShader::GLTFVariant flags
U8 mShaderVariant = 0;
+ // vertex attribute mask
+ U32 mAttributeMask = 0;
+
+ // backpointer to vertex buffer (owned by Asset)
+ LLPointer<LLVertexBuffer> mVertexBuffer;
+ U32 mVertexOffset = 0;
+ U32 mIndexOffset = 0;
+
+ U32 getVertexCount() const { return (U32) mPositions.size(); }
+ U32 getIndexCount() const { return (U32) mIndexArray.size(); }
+
std::unordered_map<std::string, S32> mAttributes;
// create octree based on vertex buffer
@@ -100,6 +108,11 @@ namespace LL
const Primitive& operator=(const Value& src);
bool prep(Asset& asset);
+
+ // upload geometry to given vertex buffer
+ // asserts that buffer is bound
+ // asserts that buffer is valid for this primitive
+ void upload(LLVertexBuffer* buffer);
};
}
}