Remove tinygltf dependency from LL::GLTF (#1541)

* #1535 Image loading/saving support in boost::json driven GLTF parser
* #1536 GLB Support in boost::json drvien GLTF parser
master
Dave Parks 2024-05-28 09:45:40 -05:00 committed by GitHub
parent db627bc354
commit 2f41200384
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1221 additions and 1332 deletions

View File

@ -37,8 +37,8 @@ class LLMutex;
const S32 UUID_BYTES = 16;
const S32 UUID_WORDS = 4;
const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below
const S32 UUID_STR_SIZE = 37;
const S32 UUID_STR_LENGTH = 37; // number of bytes needed to store a UUID as a string
const S32 UUID_STR_SIZE = 36; // .size() of a UUID in a std::string
const S32 UUID_BASE85_LENGTH = 21; // including the trailing NULL.
struct uuid_time_t {

View File

@ -2232,6 +2232,61 @@ LLImageFormatted* LLImageFormatted::createFromType(S8 codec)
return image;
}
// static
S8 LLImageFormatted::getCodecFromMimeType(std::string_view mimetype)
{
if (mimetype == "image/bmp")
{
return IMG_CODEC_BMP;
}
else if (mimetype == "image/tga")
{
return IMG_CODEC_TGA;
}
else if (mimetype == "image/jpeg")
{
return IMG_CODEC_JPEG;
}
else if (mimetype == "image/png")
{
return IMG_CODEC_PNG;
}
else if (mimetype == "image/j2c")
{
return IMG_CODEC_J2C;
}
else if (mimetype == "image/dxt")
{
return IMG_CODEC_DXT;
}
return IMG_CODEC_INVALID;
}
// static
LLImageFormatted* LLImageFormatted::createFromMimeType(std::string_view mimetype)
{
S8 codec = getCodecFromMimeType(mimetype);
return createFromType(codec);
}
// static
LLImageFormatted* LLImageFormatted::loadFromMemory(const U8* data_in, U32 size, std::string_view mimetype)
{
LLImageFormatted* image = createFromMimeType(mimetype);
if (image)
{
U8* data = image->allocateData(size);
memcpy(data, data_in, size);
if (!image->updateData())
{
delete image;
image = NULL;
}
}
return image;
}
// static
LLImageFormatted* LLImageFormatted::createFromExtension(const std::string& instring)
{
@ -2412,6 +2467,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
//----------------------------------------------------------------------------
bool LLImageFormatted::load(const std::string &filename, int load_size)
{
resetLastError();

View File

@ -322,7 +322,10 @@ class LLImageFormatted : public LLImageBase
{
public:
static LLImageFormatted* createFromType(S8 codec);
static LLImageFormatted* loadFromMemory(const U8* data, U32 size, std::string_view mimetype);
static LLImageFormatted* createFromExtension(const std::string& instring);
static LLImageFormatted* createFromMimeType(std::string_view mimetype);
static S8 getCodecFromMimeType(std::string_view mimetype);
protected:
/*virtual*/ ~LLImageFormatted();

View File

@ -1049,6 +1049,12 @@ void LLGLSLShader::bind()
}
}
void LLGLSLShader::bind(LLGLSLShader::GLTFVariant variant)
{
llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
mGLTFVariants[variant].bind();
}
void LLGLSLShader::bind(bool rigged)
{
if (rigged)

View File

@ -44,6 +44,7 @@ public:
bool hasTransport = false; // implies no lighting (it's possible to have neither though)
bool hasSkinning = false;
bool hasObjectSkinning = false;
bool mGLTF = false;
bool hasAtmospherics = false;
bool hasGamma = false;
bool hasShadows = false;
@ -318,6 +319,22 @@ public:
// this pointer should be set to whichever shader represents this shader's rigged variant
LLGLSLShader* mRiggedVariant = nullptr;
// variants for use by GLTF renderer
// "this" is considered to be OPAQUE
enum GLTFVariant
{
STATIC_OPAQUE,
STATIC_BLEND,
RIGGED_OPAQUE,
RIGGED_BLEND,
NUM_GLTF_VARIANTS
};
std::vector<LLGLSLShader> mGLTFVariants;
//helper to bind GLTF variant
void bind(GLTFVariant variant);
// hacky flag used for optimization in LLDrawPoolAlpha
bool mCanBindFast = false;

View File

@ -0,0 +1,117 @@
/**
* @file pbrmetallicroughnessF.glsl
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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$
*/
/*[EXTRA_CODE_HERE]*/
// GLTF pbrMetallicRoughness implementation
uniform sampler2D diffuseMap; //always in sRGB space
uniform float metallicFactor;
uniform float roughnessFactor;
uniform vec3 emissiveColor;
uniform sampler2D bumpMap;
uniform sampler2D emissiveMap;
uniform sampler2D specularMap; // Packed: Occlusion, Metal, Roughness
out vec4 frag_data[4];
in vec3 vary_position;
in vec4 vertex_color;
in vec3 vary_normal;
in vec3 vary_tangent;
flat in float vary_sign;
in vec2 base_color_texcoord;
in vec2 normal_texcoord;
in vec2 metallic_roughness_texcoord;
in vec2 emissive_texcoord;
uniform float minimum_alpha; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlphaCutoff()
vec3 linear_to_srgb(vec3 c);
vec3 srgb_to_linear(vec3 c);
uniform vec4 clipPlane;
uniform float clipSign;
void mirrorClip(vec3 pos);
uniform mat3 normal_matrix;
void main()
{
mirrorClip(vary_position);
vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba;
basecolor.rgb = srgb_to_linear(basecolor.rgb);
basecolor *= vertex_color;
if (basecolor.a < minimum_alpha)
{
discard;
}
vec3 col = basecolor.rgb;
// from mikktspace.com
vec3 vNt = texture(bumpMap, normal_texcoord.xy).xyz*2.0-1.0;
float sign = vary_sign;
vec3 vN = vary_normal;
vec3 vT = vary_tangent.xyz;
vec3 vB = sign * cross(vN, vT);
vec3 tnorm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
// RGB = Occlusion, Roughness, Metal
// default values, see LLViewerTexture::sDefaultPBRORMImagep
// occlusion 1.0
// roughness 0.0
// metal 0.0
vec3 spec = texture(specularMap, metallic_roughness_texcoord.xy).rgb;
spec.g *= roughnessFactor;
spec.b *= metallicFactor;
vec3 emissive = emissiveColor;
emissive *= srgb_to_linear(texture(emissiveMap, emissive_texcoord.xy).rgb);
tnorm *= gl_FrontFacing ? 1.0 : -1.0;
//spec.rgb = vec3(1,1,0);
//col = vec3(0,0,0);
//emissive = vary_tangent.xyz*0.5+0.5;
//emissive = vec3(sign*0.5+0.5);
//emissive = vNt * 0.5 + 0.5;
//emissive = tnorm*0.5+0.5;
// See: C++: addDeferredAttachments(), GLSL: softenLightF
frag_data[0] = max(vec4(col, 0.0), vec4(0)); // Diffuse
frag_data[1] = max(vec4(spec.rgb,0.0), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
frag_data[2] = vec4(tnorm, GBUFFER_FLAG_HAS_PBR); // normal, environment intensity, flags
frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive
}

View File

@ -0,0 +1,106 @@
/**
* @file pbrmetallicroughnessV.glsl
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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$
*/
// GLTF pbrMetallicRoughness implementation
uniform mat4 modelview_matrix;
#ifdef HAS_SKIN
uniform mat4 projection_matrix;
mat4 getObjectSkinnedTransform();
#else
uniform mat3 normal_matrix;
uniform mat4 modelview_projection_matrix;
#endif
uniform mat4 texture_matrix0;
uniform vec4[2] texture_base_color_transform;
uniform vec4[2] texture_normal_transform;
uniform vec4[2] texture_metallic_roughness_transform;
uniform vec4[2] texture_emissive_transform;
in vec3 position;
in vec4 diffuse_color;
in vec3 normal;
in vec4 tangent;
in vec2 texcoord0;
out vec2 base_color_texcoord;
out vec2 normal_texcoord;
out vec2 metallic_roughness_texcoord;
out vec2 emissive_texcoord;
out vec4 vertex_color;
out vec3 vary_tangent;
flat out float vary_sign;
out vec3 vary_normal;
out vec3 vary_position;
vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
void main()
{
#ifdef HAS_SKIN
mat4 mat = getObjectSkinnedTransform();
mat = modelview_matrix * mat;
vec3 pos = (mat*vec4(position.xyz,1.0)).xyz;
vary_position = pos;
gl_Position = projection_matrix*vec4(pos,1.0);
#else
vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
//transform vertex
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
#endif
base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);
metallic_roughness_texcoord = texture_transform(texcoord0, texture_metallic_roughness_transform, texture_matrix0);
emissive_texcoord = texture_transform(texcoord0, texture_emissive_transform, texture_matrix0);
#ifdef HAS_SKIN
vec3 n = (mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz;
vec3 t = (mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz;
#else //HAS_SKIN
vec3 n = normal_matrix * normal;
vec3 t = normal_matrix * tangent.xyz;
#endif
n = normalize(n);
vary_tangent = normalize(tangent_space_transform(vec4(t, tangent.w), n, texture_normal_transform, texture_matrix0));
vary_sign = tangent.w;
vary_normal = n;
vertex_color = diffuse_color;
}

View File

@ -1,13 +1,13 @@
# Linden Lab GLTF Implementation
Currently in prototype stage. Much functionality is missing (blend shapes,
Currently in prototype stage. Much functionality is missing (blend shapes,
multiple texture coordinates, etc).
GLTF Specification can be found here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html.
If this implementation disagrees with the GLTF Specification, the specification is correct.
Class structure and naming should match the GLTF Specification as closely as possible while
conforming to the LL coding standards. All code in headers should be contained in the
conforming to the LL coding standards. All code in headers should be contained in the
LL::GLTF namespace.
The implementation serves both the client and the server.
@ -18,7 +18,7 @@ The implementation serves both the client and the server.
- The implementation MUST use the same indexing scheme as the GLTF specification. Do not store pointers where the
- GLTF specification stores indices, store indices.
- Limit dependencies on llcommon as much as possible. Prefer std::, boost::, and (soon) glm:: over LL facsimiles.
- Usage of LLSD is forbidden in the LL::GLTF namespace.
- Usage of LLSD is forbidden in the LL::GLTF namespace.
- Use "using namespace" liberally in .cpp files, but never in .h files.
- "using Foo = Bar" is permissible in .h files within the LL::GLTF namespace.
@ -69,14 +69,14 @@ Parameters to "write" and "copy" MUST be ordered "src" before "dst"
so the code reads as "write src to dst" and "copy src to dst".
When reading string constants from GLTF json (i.e. "OPAQUE", "TRIANGLES"), these
strings should be converted to enums inside operator=. It is permissible to
strings should be converted to enums inside operator=. It is permissible to
store the original strings during prototyping to aid in development, but eventually
we'll purge these strings from the implementation. However, implementations MUST
preserve any and all "name" members.
"write" and "copy" implementations MUST be stored in buffer_util.h.
As implementers encounter new data types, you'll see compiler errors
pointing at templates in buffer_util.h. See vec3 as a known good
pointing at templates in buffer_util.h. See vec3 as a known good
example of how to add support for a new type (there are bad examples, so beware):
```
@ -120,29 +120,29 @@ inline bool write(const vec3& src, Value& dst)
Speed is important, but so is safety. In writers, try to avoid redundant copies
(prefer resize over push_back, convert dst to an empty array and fill it, don't
make an array on the stack and copy it into dst).
make an array on the stack and copy it into dst).
boost::json WILL throw exceptions if you call as_foo() on a mismatched type but
WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo
boost::json WILL throw exceptions if you call as_foo() on a mismatched type but
WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo
before calling as_foo or get_foo. DO NOT add exception handlers. If boost throws
an exception in serialization, the fix is to add type checks. If we see a large
number of crash reports from boost::json exceptions, each of those reports
indicates a place where we're missing "is_foo" checks. They are gold. Do not
indicates a place where we're missing "is_foo" checks. They are gold. Do not
bury them with an exception handler.
DO NOT rely on existing type conversion tools in the LL codebase -- LL data models
conflict with the GLTF specification so we MUST provide conversions independent of
DO NOT rely on existing type conversion tools in the LL codebase -- LL data models
conflict with the GLTF specification so we MUST provide conversions independent of
our existing implementations.
### JSON Serialization ###
NEVER include buffer_util.h from a header.
NEVER include buffer_util.h from a header.
Loading from and saving to disk (import/export) is currently done using tinygltf, but this is not a long term
solution. Eventually the implementation should rely solely on boost::json for reading and writing .gltf
files and should handle .bin files natively.
files and should handle .bin files natively.
When serializing Images and Buffers to the server, clients MUST store a single UUID "uri" field and nothing else.
The server MUST reject any data that violates this requirement.
@ -152,5 +152,5 @@ Servers MAY reject Assets that contain Buffers with unreferenced data.
... to be continued.

View File

@ -28,6 +28,7 @@
#include "asset.h"
#include "buffer_util.h"
#include "llfilesystem.h"
using namespace LL::GLTF;
using namespace boost::json;
@ -107,6 +108,8 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
mData.erase(mData.begin() + offset, mData.begin() + offset + length);
mByteLength = mData.size();
for (BufferView& view : asset.mBufferViews)
{
if (view.mBuffer == idx)
@ -119,6 +122,95 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
}
}
bool Buffer::prep(Asset& asset)
{
// PRECONDITION: mByteLength must not be 0
llassert(mByteLength != 0);
LLUUID id;
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
{ // loaded from an asset, fetch the buffer data from the asset store
LLFileSystem file(id, LLAssetType::AT_GLTF_BIN, LLFileSystem::READ);
mData.resize(file.getSize());
if (!file.read((U8*)mData.data(), mData.size()))
{
LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL;
return false;
}
}
else if (mUri.find("data:") == 0)
{ // loaded from a data URI, load the texture from the data
LL_WARNS() << "Data URIs not yet supported" << LL_ENDL;
return false;
}
else if (!asset.mFilename.empty() &&
!mUri.empty()) // <-- uri could be empty if we're loading from .glb
{
std::string dir = gDirUtilp->getDirName(asset.mFilename);
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
std::ifstream file(bin_file, std::ios::binary);
if (!file.is_open())
{
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
return false;
}
file.seekg(0, std::ios::end);
if (mByteLength > file.tellg())
{
LL_WARNS("GLTF") << "Unexpected file size: " << bin_file << " is " << file.tellg() << " bytes, expected " << mByteLength << LL_ENDL;
return false;
}
file.seekg(0, std::ios::beg);
mData.resize(mByteLength);
file.read((char*)mData.data(), mData.size());
}
// POSTCONDITION: on success, mData.size == mByteLength
llassert(mData.size() == mByteLength);
return true;
}
bool Buffer::save(Asset& asset, const std::string& folder)
{
if (mUri.substr(0, 5) == "data:")
{
LL_WARNS("GLTF") << "Data URIs not yet supported" << LL_ENDL;
return false;
}
std::string bin_file = folder + gDirUtilp->getDirDelimiter();
if (mUri.empty())
{
if (mName.empty())
{
S32 idx = this - &asset.mBuffers[0];
mUri = llformat("buffer_%d.bin", idx);
}
else
{
mUri = mName + ".bin";
}
}
bin_file += mUri;
std::ofstream file(bin_file, std::ios::binary);
if (!file.is_open())
{
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
return false;
}
file.write((char*)mData.data(), mData.size());
return true;
}
void Buffer::serialize(object& dst) const
{
write(mName, "name", dst);
@ -132,23 +224,15 @@ const Buffer& Buffer::operator=(const Value& src)
{
copy(src, "name", mName);
copy(src, "uri", mUri);
// NOTE: DO NOT attempt to handle the uri here.
copy(src, "byteLength", mByteLength);
// NOTE: DO NOT attempt to handle the uri here.
// The uri is a reference to a file that is not loaded until
// after the json document is parsed
}
return *this;
}
const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
{
mData = src.data;
mName = src.name;
mUri = src.uri;
return *this;
}
void BufferView::serialize(object& dst) const
{
write_always(mBuffer, "buffer", dst);
@ -173,43 +257,6 @@ const BufferView& BufferView::operator=(const Value& src)
return *this;
}
const BufferView& 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;
}
Accessor::Type tinygltf_type_to_enum(S32 type)
{
switch (type)
{
case TINYGLTF_TYPE_SCALAR:
return Accessor::Type::SCALAR;
case TINYGLTF_TYPE_VEC2:
return Accessor::Type::VEC2;
case TINYGLTF_TYPE_VEC3:
return Accessor::Type::VEC3;
case TINYGLTF_TYPE_VEC4:
return Accessor::Type::VEC4;
case TINYGLTF_TYPE_MAT2:
return Accessor::Type::MAT2;
case TINYGLTF_TYPE_MAT3:
return Accessor::Type::MAT3;
case TINYGLTF_TYPE_MAT4:
return Accessor::Type::MAT4;
}
LL_WARNS("GLTF") << "Unknown tinygltf accessor type: " << type << LL_ENDL;
llassert(false);
return Accessor::Type::SCALAR;
}
void Accessor::serialize(object& dst) const
{
write(mName, "name", dst);
@ -240,18 +287,3 @@ const Accessor& Accessor::operator=(const Value& src)
return *this;
}
const Accessor& Accessor::operator=(const tinygltf::Accessor& src)
{
mBufferView = src.bufferView;
mByteOffset = src.byteOffset;
mComponentType = src.componentType;
mCount = src.count;
mType = tinygltf_type_to_enum(src.type);
mNormalized = src.normalized;
mName = src.name;
mMax = src.maxValues;
mMin = src.minValues;
return *this;
}

View File

@ -26,7 +26,6 @@
* $/LicenseInfo$
*/
#include "../lltinygltfhelper.h"
#include "llstrider.h"
#include "boost/json.hpp"
@ -51,9 +50,12 @@ namespace LL
// also updates all buffer views in given asset that reference this buffer
void erase(Asset& asset, S32 offset, S32 length);
bool prep(Asset& asset);
void serialize(boost::json::object& obj) const;
const Buffer& operator=(const Value& value);
const Buffer& operator=(const tinygltf::Buffer& src);
bool save(Asset& asset, const std::string& folder);
};
class BufferView
@ -69,37 +71,44 @@ namespace LL
void serialize(boost::json::object& obj) const;
const BufferView& operator=(const Value& value);
const BufferView& operator=(const tinygltf::BufferView& src);
};
class Accessor
{
public:
S32 mBufferView = INVALID_INDEX;
S32 mByteOffset = 0;
S32 mComponentType = 0;
S32 mCount = 0;
std::vector<double> mMax;
std::vector<double> mMin;
enum class Type : S32
enum class Type : U8
{
SCALAR = TINYGLTF_TYPE_SCALAR,
VEC2 = TINYGLTF_TYPE_VEC2,
VEC3 = TINYGLTF_TYPE_VEC3,
VEC4 = TINYGLTF_TYPE_VEC4,
MAT2 = TINYGLTF_TYPE_MAT2,
MAT3 = TINYGLTF_TYPE_MAT3,
MAT4 = TINYGLTF_TYPE_MAT4
SCALAR,
VEC2,
VEC3,
VEC4,
MAT2,
MAT3,
MAT4
};
enum class ComponentType : U32
{
BYTE = 5120,
UNSIGNED_BYTE = 5121,
SHORT = 5122,
UNSIGNED_SHORT = 5123,
UNSIGNED_INT = 5125,
FLOAT = 5126
};
std::vector<double> mMax;
std::vector<double> mMin;
std::string mName;
S32 mBufferView = INVALID_INDEX;
S32 mByteOffset = 0;
ComponentType mComponentType = ComponentType::BYTE;
S32 mCount = 0;
Type mType = Type::SCALAR;
bool mNormalized = false;
std::string mName;
void serialize(boost::json::object& obj) const;
const Accessor& operator=(const Value& value);
const Accessor& operator=(const tinygltf::Accessor& src);
};
// convert from "SCALAR", "VEC2", etc to Accessor::Type

View File

@ -32,7 +32,7 @@
using namespace LL::GLTF;
using namespace boost::json;
void Animation::allocateGLResources(Asset& asset)
bool Animation::prep(Asset& asset)
{
if (!mSamplers.empty())
{
@ -40,7 +40,10 @@ void Animation::allocateGLResources(Asset& asset)
mMaxTime = -FLT_MAX;
for (auto& sampler : mSamplers)
{
sampler.allocateGLResources(asset);
if (!sampler.prep(asset))
{
return false;
}
mMinTime = llmin(sampler.mMinTime, mMinTime);
mMaxTime = llmax(sampler.mMaxTime, mMaxTime);
}
@ -52,13 +55,21 @@ void Animation::allocateGLResources(Asset& asset)
for (auto& channel : mRotationChannels)
{
channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
if (!channel.prep(asset, mSamplers[channel.mSampler]))
{
return false;
}
}
for (auto& channel : mTranslationChannels)
{
channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
if (!channel.prep(asset, mSamplers[channel.mSampler]))
{
return false;
}
}
return true;
}
void Animation::update(Asset& asset, F32 dt)
@ -85,7 +96,7 @@ void Animation::apply(Asset& asset, float time)
}
};
void Animation::Sampler::allocateGLResources(Asset& asset)
bool Animation::Sampler::prep(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mInput];
mMinTime = accessor.mMin[0];
@ -95,6 +106,8 @@ void Animation::Sampler::allocateGLResources(Asset& asset)
LLStrider<F32> frame_times = mFrameTimes.data();
copy(asset, accessor, frame_times);
return true;
}
@ -120,16 +133,6 @@ const Animation::Sampler& Animation::Sampler::operator=(const Value& src)
return *this;
}
const Animation::Sampler& Animation::Sampler::operator=(const tinygltf::AnimationSampler& src)
{
mInput = src.input;
mOutput = src.output;
mInterpolation = src.interpolation;
return *this;
}
bool Animation::Channel::Target::operator==(const Channel::Target& rhs) const
{
return mNode == rhs.mNode && mPath == rhs.mPath;
@ -172,17 +175,6 @@ const Animation::Channel& Animation::Channel::operator=(const Value& src)
return *this;
}
const Animation::Channel& Animation::Channel::operator=(const tinygltf::AnimationChannel& src)
{
mSampler = src.sampler;
mTarget.mNode = src.target_node;
mTarget.mPath = src.target_path;
return *this;
}
void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
{
LL_PROFILE_ZONE_SCOPED;
@ -223,11 +215,13 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F
}
}
void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mRotations);
return true;
}
void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@ -254,11 +248,13 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
}
}
void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
bool Animation::TranslationChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mTranslations);
return true;
}
void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@ -286,11 +282,13 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti
}
}
void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
bool Animation::ScaleChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mScales);
return true;
}
void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@ -364,47 +362,15 @@ const Animation& Animation::operator=(const Value& src)
return *this;
}
const Animation& Animation::operator=(const tinygltf::Animation& src)
{
mName = src.name;
mSamplers.resize(src.samplers.size());
for (U32 i = 0; i < src.samplers.size(); ++i)
{
mSamplers[i] = src.samplers[i];
}
for (U32 i = 0; i < src.channels.size(); ++i)
{
if (src.channels[i].target_path == "rotation")
{
mRotationChannels.push_back(RotationChannel());
mRotationChannels.back() = src.channels[i];
}
if (src.channels[i].target_path == "translation")
{
mTranslationChannels.push_back(TranslationChannel());
mTranslationChannels.back() = src.channels[i];
}
if (src.channels[i].target_path == "scale")
{
mScaleChannels.push_back(ScaleChannel());
mScaleChannels.back() = src.channels[i];
}
}
return *this;
}
void Skin::allocateGLResources(Asset& asset)
bool Skin::prep(Asset& asset)
{
if (mInverseBindMatrices != INVALID_INDEX)
{
Accessor& accessor = asset.mAccessors[mInverseBindMatrices];
copy(asset, accessor, mInverseBindMatricesData);
}
return true;
}
const Skin& Skin::operator=(const Value& src)
@ -419,16 +385,6 @@ const Skin& Skin::operator=(const Value& src)
return *this;
}
const Skin& Skin::operator=(const tinygltf::Skin& src)
{
mName = src.name;
mSkeleton = src.skeleton;
mInverseBindMatrices = src.inverseBindMatrices;
mJoints = src.joints;
return *this;
}
void Skin::serialize(object& obj) const
{
write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX);

View File

@ -49,12 +49,10 @@ namespace LL
S32 mOutput = INVALID_INDEX;
std::string mInterpolation;
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
void serialize(boost::json::object& dst) const;
const Sampler& operator=(const Value& value);
const Sampler& operator=(const tinygltf::AnimationSampler& src);
// get the frame index and time for the specified time
// asset -- the asset to reference for Accessors
@ -85,7 +83,6 @@ namespace LL
void serialize(boost::json::object& dst) const;
const Channel& operator=(const Value& value);
const Channel& operator=(const tinygltf::AnimationChannel& src);
};
class RotationChannel : public Channel
@ -96,16 +93,10 @@ namespace LL
std::vector<quat> mRotations;
const RotationChannel& operator=(const tinygltf::AnimationChannel& src)
{
Channel::operator=(src);
return *this;
}
// prepare data needed for rendering
// asset -- asset to reference for Accessors
// sampler -- Sampler associated with this channel
void allocateGLResources(Asset& asset, Sampler& sampler);
bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@ -118,16 +109,10 @@ namespace LL
std::vector<vec3> mTranslations;
const TranslationChannel& operator=(const tinygltf::AnimationChannel& src)
{
Channel::operator=(src);
return *this;
}
// prepare data needed for rendering
// asset -- asset to reference for Accessors
// sampler -- Sampler associated with this channel
void allocateGLResources(Asset& asset, Sampler& sampler);
bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@ -140,16 +125,10 @@ namespace LL
std::vector<vec3> mScales;
const ScaleChannel& operator=(const tinygltf::AnimationChannel& src)
{
Channel::operator=(src);
return *this;
}
// prepare data needed for rendering
// asset -- asset to reference for Accessors
// sampler -- Sampler associated with this channel
void allocateGLResources(Asset& asset, Sampler& sampler);
bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@ -160,7 +139,7 @@ namespace LL
// min/max time values for all samplers combined
F32 mMinTime = 0.f;
F32 mMaxTime = 0.f;
// current time of the animation
F32 mTime = 0.f;
@ -170,9 +149,8 @@ namespace LL
void serialize(boost::json::object& dst) const;
const Animation& operator=(const Value& value);
const Animation& operator=(const tinygltf::Animation& src);
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
void update(Asset& asset, float dt);

File diff suppressed because it is too large Load Diff

View File

@ -28,12 +28,12 @@
#include "llvertexbuffer.h"
#include "llvolumeoctree.h"
#include "../lltinygltfhelper.h"
#include "accessor.h"
#include "primitive.h"
#include "animation.h"
#include "boost/json.hpp"
#include "common.h"
#include "../llviewertexture.h"
extern F32SecondsImplicit gFrameTimeSeconds;
@ -69,7 +69,6 @@ namespace LL
bool operator==(const TextureInfo& rhs) const;
bool operator!=(const TextureInfo& rhs) const;
const TextureInfo& operator=(const tinygltf::TextureInfo& src);
const TextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -79,7 +78,6 @@ namespace LL
public:
F32 mScale = 1.0f;
const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src);
const NormalTextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -89,7 +87,6 @@ namespace LL
public:
F32 mStrength = 1.0f;
const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src);
const OcclusionTextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -105,22 +102,16 @@ namespace LL
bool operator==(const PbrMetallicRoughness& rhs) const;
bool operator!=(const PbrMetallicRoughness& rhs) const;
const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src);
const PbrMetallicRoughness& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
// 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<LLFetchedGLTFMaterial> mMaterial;
PbrMetallicRoughness mPbrMetallicRoughness;
NormalTextureInfo mNormalTexture;
OcclusionTextureInfo mOcclusionTexture;
TextureInfo mEmissiveTexture;
std::string mName;
vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f);
AlphaMode mAlphaMode = AlphaMode::OPAQUE;
@ -129,11 +120,8 @@ namespace LL
// bind for rendering
void bind(Asset& asset);
const Material& operator=(const tinygltf::Material& src);
const Material& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
void allocateGLResources(Asset& asset);
};
class Mesh
@ -143,11 +131,10 @@ namespace LL
std::vector<double> mWeights;
std::string mName;
const Mesh& operator=(const tinygltf::Mesh& src);
const Mesh& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
};
class Node
@ -178,7 +165,6 @@ namespace LL
std::string mName;
const Node& operator=(const tinygltf::Node& src);
const Node& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@ -217,10 +203,9 @@ namespace LL
std::string mName;
std::vector<mat4> mInverseBindMatricesData;
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
void uploadMatrixPalette(Asset& asset, Node& node);
const Skin& operator=(const tinygltf::Skin& src);
const Skin& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -231,7 +216,6 @@ namespace LL
std::vector<S32> mNodes;
std::string mName;
const Scene& operator=(const tinygltf::Scene& src);
const Scene& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@ -246,7 +230,6 @@ namespace LL
S32 mSource = INVALID_INDEX;
std::string mName;
const Texture& operator=(const tinygltf::Texture& src);
const Texture& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -260,7 +243,6 @@ namespace LL
S32 mWrapT = REPEAT;
std::string mName;
const Sampler& operator=(const tinygltf::Sampler& src);
const Sampler& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -274,7 +256,6 @@ namespace LL
S32 mBufferView = INVALID_INDEX;
std::vector<U8> mData;
S32 mWidth = -1;
S32 mHeight = -1;
S32 mComponent = -1;
@ -283,19 +264,20 @@ namespace LL
LLPointer<LLViewerFetchedTexture> mTexture;
const Image& operator=(const tinygltf::Image& src);
const Image& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
// save image clear local data, and set uri
void decompose(Asset& asset, const std::string& filename);
// save image to disk
// may remove image data from bufferviews and convert to
// file uri if necessary
bool save(Asset& asset, const std::string& filename);
// erase the buffer view associated with this image
// free any associated resources
// free any associated GLTF resources
// preserve only uri and name
void clearData(Asset& asset);
void allocateGLResources();
bool prep(Asset& asset);
};
// C++ representation of a GLTF Asset
@ -322,16 +304,20 @@ namespace LL
std::string mMinVersion;
std::string mCopyright;
S32 mDefaultScene = INVALID_INDEX;
S32 mScene = INVALID_INDEX;
Value mExtras;
U32 mPendingBuffers = 0;
// local file this asset was loaded from (if any)
std::string mFilename;
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
// prepare the asset for rendering
void allocateGLResources(const std::string& filename = "", const tinygltf::Model& model = tinygltf::Model());
// prepare for first time use
bool prep();
// Called periodically (typically once per frame)
// Any ongoing work (such as animations) should be handled here
@ -361,18 +347,25 @@ namespace LL
);
Asset() = default;
Asset(const tinygltf::Model& src);
Asset(const Value& src);
const Asset& operator=(const tinygltf::Model& src);
// load from given file
// accepts .gltf and .glb files
// Any existing data will be lost
// returns result of prep() on success
bool load(std::string_view filename);
// load .glb contents from memory
// data - binary contents of .glb file
// returns result of prep() on success
bool loadBinary(const std::string& data);
const Asset& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
// save the asset to a tinygltf model
void save(tinygltf::Model& dst);
// decompose the asset to the given .gltf file
void decompose(const std::string& filename);
// save the asset to the given .gltf file
// saves images and bins alongside the gltf file
bool save(const std::string& filename);
// remove the bufferview at the given index
// updates all bufferview indices in this Asset as needed

View File

@ -31,7 +31,7 @@
// whenever we add support for more types
#ifdef _MSC_VER
#define LL_FUNCSIG __FUNCSIG__
#define LL_FUNCSIG __FUNCSIG__
#else
#define LL_FUNCSIG __PRETTY_FUNCTION__
#endif
@ -353,37 +353,29 @@ namespace LL
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT)
switch (accessor.mComponentType)
{
LL::GLTF::copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
{
LL::GLTF::copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
{
LL::GLTF::copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)
{
LL::GLTF::copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_SHORT)
{
LL::GLTF::copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_BYTE)
{
LL::GLTF::copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)
{
LL::GLTF::copy(asset, accessor, (const F64*)src, dst, bufferView.mByteStride);
}
else
{
LL_ERRS("GLTF") << "Unsupported component type" << LL_ENDL;
case Accessor::ComponentType::FLOAT:
copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::UNSIGNED_INT:
copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::SHORT:
copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::UNSIGNED_SHORT:
copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::BYTE:
copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::UNSIGNED_BYTE:
copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
break;
default:
LL_ERRS("GLTF") << "Invalid component type" << LL_ENDL;
break;
}
}
@ -400,7 +392,7 @@ namespace LL
//=========================================================================================================
// boost::json copying utilities
// ========================================================================================================
//====================== unspecialized base template, single value ===========================
// to/from Value
@ -528,7 +520,7 @@ namespace LL
}
return false;
}
template<typename T>
inline bool write(const std::unordered_map<std::string, T>& src, string_view member, boost::json::object& dst, const std::unordered_map<std::string, T>& default_value = std::unordered_map<std::string, T>())
{
@ -571,6 +563,44 @@ namespace LL
return false;
}
// Accessor::ComponentType
template<>
inline bool copy(const Value& src, Accessor::ComponentType& dst)
{
if (src.is_int64())
{
dst = (Accessor::ComponentType)src.get_int64();
return true;
}
return false;
}
template<>
inline bool write(const Accessor::ComponentType& src, Value& dst)
{
dst = (S32)src;
return true;
}
//Primitive::Mode
template<>
inline bool copy(const Value& src, Primitive::Mode& dst)
{
if (src.is_int64())
{
dst = (Primitive::Mode)src.get_int64();
return true;
}
return false;
}
template<>
inline bool write(const Primitive::Mode& src, Value& dst)
{
dst = (S32)src;
return true;
}
// vec4
template<>
inline bool copy(const Value& src, vec4& dst)
@ -881,7 +911,7 @@ namespace LL
return true;
}
//
//
// ========================================================================================================
}

View File

@ -60,6 +60,7 @@ namespace LL
constexpr S32 MIRRORED_REPEAT = 33648;
constexpr S32 REPEAT = 10497;
class Asset;
}
}

View File

@ -9,7 +9,7 @@
* 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.
* 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
@ -30,12 +30,10 @@
#include "buffer_util.h"
#include "../llviewershadermgr.h"
#include "../lltinygltfhelper.h"
using namespace LL::GLTF;
using namespace boost::json;
void Primitive::allocateGLResources(Asset& asset)
bool Primitive::prep(Asset& asset)
{
// allocate vertex buffer
// We diverge from the intent of the GLTF format here to work with our existing render pipeline
@ -85,10 +83,19 @@ void Primitive::allocateGLResources(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mIndices];
copy(asset, accessor, mIndexArray);
for (auto& idx : mIndexArray)
{
if (idx >= mPositions.size())
{
LL_WARNS("GLTF") << "Invalid index array" << LL_ENDL;
return false;
}
}
}
U32 mask = ATTRIBUTE_MASK;
if (!mWeights.empty())
{
mask |= LLVertexBuffer::MAP_WEIGHT4;
@ -130,7 +137,7 @@ void Primitive::allocateGLResources(Asset& asset)
{
mColors.resize(mPositions.size(), LLColor4U::white);
}
// bake material basecolor into color array
if (mMaterial != INVALID_INDEX)
{
@ -148,7 +155,7 @@ void Primitive::allocateGLResources(Asset& asset)
{
mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
}
mVertexBuffer->setNormalData(mNormals.data());
if (mTangents.empty())
@ -175,10 +182,12 @@ void Primitive::allocateGLResources(Asset& asset)
mVertexBuffer->setWeight4Data(weight_data.data());
}
createOctree();
mVertexBuffer->unbind();
return true;
}
void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
@ -224,7 +233,7 @@ void Primitive::createOctree()
F32 scaler = 0.25f;
if (mMode == TINYGLTF_MODE_TRIANGLES)
if (mMode == Mode::TRIANGLES)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() / 3;
// Initialize all the triangles we need
@ -241,14 +250,14 @@ void Primitive::createOctree()
const LLVector4a& v0 = mPositions[i0];
const LLVector4a& v1 = mPositions[i1];
const LLVector4a& v2 = mPositions[i2];
initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2);
//insert
mOctree->insert(tri);
}
}
else if (mMode == TINYGLTF_MODE_TRIANGLE_STRIP)
else if (mMode == Mode::TRIANGLE_STRIP)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
// Initialize all the triangles we need
@ -272,7 +281,7 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
else if (mMode == TINYGLTF_MODE_TRIANGLE_FAN)
else if (mMode == Mode::TRIANGLE_FAN)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
// Initialize all the triangles we need
@ -296,14 +305,14 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
else if (mMode == TINYGLTF_MODE_POINTS ||
mMode == TINYGLTF_MODE_LINE ||
mMode == TINYGLTF_MODE_LINE_LOOP ||
mMode == TINYGLTF_MODE_LINE_STRIP)
else if (mMode == Mode::POINTS ||
mMode == Mode::LINES ||
mMode == Mode::LINE_LOOP ||
mMode == Mode::LINE_STRIP)
{
// nothing to do, no volume... maybe add some collision geometry around these primitive types?
}
else
{
LL_ERRS() << "Unsupported Primitive mode" << LL_ENDL;
@ -357,23 +366,23 @@ Primitive::~Primitive()
mOctree = nullptr;
}
U32 gltf_mode_to_gl_mode(U32 mode)
LLRender::eGeomModes gltf_mode_to_gl_mode(Primitive::Mode mode)
{
switch (mode)
{
case TINYGLTF_MODE_POINTS:
case Primitive::Mode::POINTS:
return LLRender::POINTS;
case TINYGLTF_MODE_LINE:
case Primitive::Mode::LINES:
return LLRender::LINES;
case TINYGLTF_MODE_LINE_LOOP:
case Primitive::Mode::LINE_LOOP:
return LLRender::LINE_LOOP;
case TINYGLTF_MODE_LINE_STRIP:
case Primitive::Mode::LINE_STRIP:
return LLRender::LINE_STRIP;
case TINYGLTF_MODE_TRIANGLES:
case Primitive::Mode::TRIANGLES:
return LLRender::TRIANGLES;
case TINYGLTF_MODE_TRIANGLE_STRIP:
case Primitive::Mode::TRIANGLE_STRIP:
return LLRender::TRIANGLE_STRIP;
case TINYGLTF_MODE_TRIANGLE_FAN:
case Primitive::Mode::TRIANGLE_FAN:
return LLRender::TRIANGLE_FAN;
default:
return LLRender::TRIANGLES;
@ -383,7 +392,7 @@ U32 gltf_mode_to_gl_mode(U32 mode)
void Primitive::serialize(boost::json::object& dst) const
{
write(mMaterial, "material", dst, -1);
write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES);
write(mMode, "mode", dst, Primitive::Mode::TRIANGLES);
write(mIndices, "indices", dst, INVALID_INDEX);
write(mAttributes, "attributes", dst);
}
@ -402,24 +411,3 @@ const Primitive& Primitive::operator=(const Value& src)
return *this;
}
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;
}
mGLMode = gltf_mode_to_gl_mode(mMode);
return *this;
}

View File

@ -48,6 +48,17 @@ namespace LL
class Primitive
{
public:
enum class Mode : U8
{
POINTS,
LINES,
LINE_LOOP,
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
TRIANGLE_FAN
};
~Primitive();
// GPU copy of mesh data
@ -66,10 +77,10 @@ namespace LL
// raycast acceleration structure
LLPointer<LLVolumeOctree> mOctree;
std::vector<LLVolumeTriangle> mOctreeTriangles;
S32 mMaterial = -1;
S32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles
U32 mGLMode = LLRender::TRIANGLES;
Mode mMode = Mode::TRIANGLES; // default to triangles
LLRender::eGeomModes mGLMode = LLRender::TRIANGLES; // for use with LLRender
S32 mIndices = -1;
std::unordered_map<std::string, S32> mAttributes;
@ -77,7 +88,7 @@ namespace LL
// must be called before buffer is unmapped and after buffer is populated with good data
void createOctree();
//get the LLVolumeTriangle that intersects with the given line segment at the point
//get the LLVolumeTriangle that intersects with the given line segment at the point
//closest to start. Moves end to the point of intersection. Returns nullptr if no intersection.
//Line segment must be in the same coordinate frame as this Primitive
const LLVolumeTriangle* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
@ -86,12 +97,11 @@ namespace LL
LLVector4a* normal = NULL, // return the surface normal at the intersection point
LLVector4a* tangent = NULL // return the surface tangent at the intersection point
);
void serialize(boost::json::object& obj) const;
const Primitive& operator=(const Value& src);
const Primitive& operator=(const tinygltf::Primitive& src);
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
};
}
}

View File

@ -107,32 +107,6 @@ void GLTFSceneManager::saveAs()
}
}
void GLTFSceneManager::decomposeSelection()
{
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
if (obj && obj->mGLTFAsset)
{
LLFilePickerReplyThread::startPicker(
[](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter)
{
if (LLAppViewer::instance()->quitRequested())
{
return;
}
if (filenames.size() > 0)
{
GLTFSceneManager::instance().decomposeSelection(filenames[0]);
}
},
LLFilePicker::FFSAVE_GLTF,
"scene.gltf");
}
else
{
LLNotificationsUtil::add("GLTFSaveSelection");
}
}
void GLTFSceneManager::uploadSelection()
{
if (mUploadingAsset)
@ -153,77 +127,74 @@ void GLTFSceneManager::uploadSelection()
for (auto& image : asset.mImages)
{
if (!image.mData.empty())
if (image.mTexture.notNull())
{
mPendingImageUploads++;
LLPointer<LLImageRaw> raw = new LLImageRaw(image.mWidth, image.mHeight, image.mComponent);
U8* data = raw->allocateData();
llassert_always(image.mData.size() == raw->getDataSize());
memcpy(data, image.mData.data(), image.mData.size());
LLPointer<LLImageRaw> raw = image.mTexture->getCachedRawImage();
// for GLTF native content, store image in GLTF orientation
raw->verticalFlip();
LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
std::string buffer;
buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
LLUUID asset_id = LLUUID::generateNewID();
std::string name;
S32 idx = (S32)(&image - &asset.mImages[0]);
if (image.mName.empty())
if (raw.notNull())
{
LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
name = llformat("Image_%d", idx);
}
else
{
name = image.mName;
}
std::string buffer;
buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
LLUUID asset_id = LLUUID::generateNewID();
std::string name;
S32 idx = (S32)(&image - &asset.mImages[0]);
if (image.mName.empty())
{
// TODO: handle failure
mPendingImageUploads--;
return false;
};
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
name = llformat("Image_%d", idx);
}
else
{
if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
name = image.mName;
}
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
mUploadingAsset->mImages[idx].mUri = assetId.asString();
// TODO: handle failure
mPendingImageUploads--;
}
};
return false;
};
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
name,
name,
0,
LLFolderType::FT_TEXTURE,
LLInventoryType::IT_TEXTURE,
LLAssetType::AT_TEXTURE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
{
if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
{
mUploadingAsset->mImages[idx].mUri = assetId.asString();
mPendingImageUploads--;
}
};
upload_new_resource(uploadInfo);
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
image.clearData(asset);
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
name,
name,
0,
LLFolderType::FT_TEXTURE,
LLInventoryType::IT_TEXTURE,
LLAssetType::AT_TEXTURE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
upload_new_resource(uploadInfo);
image.clearData(asset);
}
}
}
@ -297,62 +268,45 @@ void GLTFSceneManager::uploadSelection()
}
}
void GLTFSceneManager::decomposeSelection(const std::string& filename)
{
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
if (obj && obj->mGLTFAsset)
{
// copy asset out for decomposition
Asset asset = *obj->mGLTFAsset;
// decompose the asset into component parts
asset.decompose(filename);
// copy decomposed asset into tinygltf for serialization
tinygltf::Model model;
asset.save(model);
LLTinyGLTFHelper::saveModel(filename, model);
}
}
void GLTFSceneManager::save(const std::string& filename)
{
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
if (obj && obj->mGLTFAsset)
{
Asset* asset = obj->mGLTFAsset.get();
tinygltf::Model model;
asset->save(model);
LLTinyGLTFHelper::saveModel(filename, model);
if (!asset->save(filename))
{
LLNotificationsUtil::add("GLTFSaveFailed");
}
}
}
void GLTFSceneManager::load(const std::string& filename)
{
tinygltf::Model model;
LLTinyGLTFHelper::loadModel(filename, model);
std::shared_ptr<Asset> asset = std::make_shared<Asset>();
*asset = model;
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
asset->allocateGLResources(filename, model);
asset->updateTransforms();
if (asset->load(filename))
{
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
asset->updateTransforms();
// hang the asset off the currently selected object, or off of the avatar if no object is selected
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
// hang the asset off the currently selected object, or off of the avatar if no object is selected
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
if (obj)
{ // assign to self avatar
obj->mGLTFAsset = asset;
obj->markForUpdate();
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
{
mObjects.push_back(obj);
if (obj)
{ // assign to self avatar
obj->mGLTFAsset = asset;
obj->markForUpdate();
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
{
mObjects.push_back(obj);
}
}
}
else
{
LLNotificationsUtil::add("GLTFLoadFailed");
}
}
GLTFSceneManager::~GLTFSceneManager()
@ -392,32 +346,19 @@ void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::ETyp
// find the Buffer with the given id in the asset
if (obj->mGLTFAsset)
{
for (auto& buffer : obj->mGLTFAsset->mBuffers)
obj->mGLTFAsset->mPendingBuffers--;
if (obj->mGLTFAsset->mPendingBuffers == 0)
{
LLUUID buffer_id;
if (LLUUID::parseUUID(buffer.mUri, &buffer_id) && buffer_id == id)
obj->mGLTFAsset->prep();
GLTFSceneManager& mgr = GLTFSceneManager::instance();
if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
{
LLFileSystem file(id, asset_type, LLFileSystem::READ);
buffer.mData.resize(file.getSize());
file.read((U8*)buffer.mData.data(), buffer.mData.size());
obj->mGLTFAsset->mPendingBuffers--;
if (obj->mGLTFAsset->mPendingBuffers == 0)
{
obj->mGLTFAsset->allocateGLResources();
GLTFSceneManager& mgr = GLTFSceneManager::instance();
if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
{
GLTFSceneManager::instance().mObjects.push_back(obj);
}
}
GLTFSceneManager::instance().mObjects.push_back(obj);
}
}
}
}
}
else
@ -492,30 +433,9 @@ void GLTFSceneManager::update()
{
if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0)
{
std::string filename(gDirUtilp->getTempDir() + "/upload.gltf");
#if 0
tinygltf::Model model;
mUploadingAsset->save(model);
tinygltf::TinyGLTF writer;
writer.WriteGltfSceneToFile(&model, filename, false, false, true, false);
#else
boost::json::object obj;
mUploadingAsset->serialize(obj);
std::string json = boost::json::serialize(obj, {});
{
std::ofstream o(filename);
o << json;
}
#endif
std::ifstream t(filename);
std::stringstream str;
str << t.rdbuf();
std::string buffer = str.str();
std::string buffer = boost::json::serialize(obj, {});
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{

View File

@ -52,8 +52,6 @@ namespace LL
void saveAs(); // open filepicker and choose file to save selected asset to
void save(const std::string& filename); // save selected asset to filename (suitable for use in external programs)
void decomposeSelection(); // open file picker and choose a location to decompose to
void decomposeSelection(const std::string& filename); // decompose selected asset into simulator-ready .gltf, .bin, and .j2c files
void uploadSelection(); // decompose selected asset and upload to simulator
void update();

View File

@ -515,11 +515,11 @@ bool LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename,
case FFSAVE_GLTF:
if (filename.empty())
{
wcsncpy( mFilesW,L"untitled.glb", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
wcsncpy( mFilesW,L"untitled.gltf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
}
mOFN.lpstrDefExt = L"glb";
mOFN.lpstrDefExt = L"gltf";
mOFN.lpstrFilter =
L"glTF Asset File (*.gltf *.glb)\0*.gltf;*.glb\0" \
L"glTF Asset File (*.gltf)\0*.gltf\0" \
L"\0";
break;
case FFSAVE_XML:
@ -790,7 +790,7 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension,
case LLFilePicker::FFSAVE_GLTF:
type = "\?\?\?\?";
creator = "\?\?\?\?";
extension = "glb,gltf";
extension = "gltf";
break;
case LLFilePicker::FFSAVE_XML:

View File

@ -1091,8 +1091,9 @@ bool LLLocalBitmapMgr::checkTextureDimensions(std::string filename)
return false;
}
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
// allow loading up to 4x max rez but implicitly downrez to max rez before upload
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X")*4;
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y")*4;
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
{
@ -1137,6 +1138,20 @@ void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
}
}
LLUUID LLLocalBitmapMgr::getTrackingID(const LLUUID& world_id) const
{
for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
{
LLLocalBitmap* unit = *iter;
if (unit->getWorldID() == world_id)
{
return unit->getTrackingID();
}
}
return LLUUID::null;
}
LLUUID LLLocalBitmapMgr::getWorldID(const LLUUID &tracking_id) const
{
LLUUID world_id = LLUUID::null;

View File

@ -135,6 +135,7 @@ public:
void delUnit(LLUUID tracking_id);
bool checkTextureDimensions(std::string filename);
LLUUID getTrackingID(const LLUUID& world_id) const;
LLUUID getWorldID(const LLUUID &tracking_id) const;
bool isLocal(const LLUUID& world_id) const;
std::string getFilename(const LLUUID &tracking_id) const;

View File

@ -8102,15 +8102,6 @@ class LLAdvancedClickGLTFSaveAs : public view_listener_t
}
};
class LLAdvancedClickGLTFDecompose : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
LL::GLTFSceneManager::instance().decomposeSelection();
return true;
}
};
class LLAdvancedClickGLTFUpload: public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@ -9793,7 +9784,6 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview");
view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen");
view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs");
view_listener_t::addMenu(new LLAdvancedClickGLTFDecompose(), "Advanced.ClickGLTFDecompose");
view_listener_t::addMenu(new LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload");
view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");

View File

@ -507,192 +507,6 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data)
}
}
// S32 exported_object_count = 0;
// S32 exported_image_count = 0;
// S32 current_object_count = 0;
// S32 current_image_count = 0;
// extern LLNotifyBox *gExporterNotify;
// extern LLUUID gExporterRequestID;
// extern std::string gExportDirectory;
// extern LLUploadDialog *gExportDialog;
// std::string gExportedFile;
// std::map<LLUUID, std::string> gImageChecksums;
// void export_complete()
// {
// LLUploadDialog::modalUploadFinished();
// gExporterRequestID.setNull();
// gExportDirectory = "";
// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */
// fseek(fXML, 0, SEEK_END);
// long length = ftell(fXML);
// fseek(fXML, 0, SEEK_SET);
// U8 *buffer = new U8[length + 1];
// size_t nread = fread(buffer, 1, length, fXML);
// if (nread < (size_t) length)
// {
// LL_WARNS("Messaging") << "Short read" << LL_ENDL;
// }
// buffer[nread] = '\0';
// fclose(fXML);
// char *pos = (char *)buffer;
// while ((pos = strstr(pos+1, "<sl:image ")) != 0)
// {
// char *pos_check = strstr(pos, "checksum=\"");
// if (pos_check)
// {
// char *pos_uuid = strstr(pos_check, "\">");
// if (pos_uuid)
// {
// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */
// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */
// image_uuid_str[UUID_STR_SIZE-1] = 0;
// LLUUID image_uuid(image_uuid_str);
// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL;
// std::map<LLUUID, std::string>::iterator itor = gImageChecksums.find(image_uuid);
// if (itor != gImageChecksums.end())
// {
// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL;
// if (!itor->second.empty())
// {
// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */
// }
// }
// }
// }
// }
// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */
// if (fwrite(buffer, 1, length, fXMLOut) != length)
// {
// LL_WARNS("Messaging") << "Short write" << LL_ENDL;
// }
// fclose(fXMLOut);
// delete [] buffer;
// }
// void exported_item_complete(const LLTSCode status, void *user_data)
// {
// //std::string *filename = (std::string *)user_data;
// if (status < LLTS_OK)
// {
// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL;
// }
// else
// {
// ++current_object_count;
// if (current_image_count == exported_image_count && current_object_count == exported_object_count)
// {
// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL;
// export_complete();
// }
// else
// {
// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
// }
// }
// }
// struct exported_image_info
// {
// LLUUID image_id;
// std::string filename;
// U32 image_num;
// };
// void exported_j2c_complete(const LLTSCode status, void *user_data)
// {
// exported_image_info *info = (exported_image_info *)user_data;
// LLUUID image_id = info->image_id;
// U32 image_num = info->image_num;
// std::string filename = info->filename;
// delete info;
// if (status < LLTS_OK)
// {
// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL;
// }
// else
// {
// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
// if (fIn)
// {
// LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C;
// LLPointer<LLImageTGA> TargaUtility = new LLImageTGA;
// fseek(fIn, 0, SEEK_END);
// S32 length = ftell(fIn);
// fseek(fIn, 0, SEEK_SET);
// U8 *buffer = ImageUtility->allocateData(length);
// if (fread(buffer, 1, length, fIn) != length)
// {
// LL_WARNS("Messaging") << "Short read" << LL_ENDL;
// }
// fclose(fIn);
// LLFile::remove(filename);
// // Convert to TGA
// LLPointer<LLImageRaw> image = new LLImageRaw();
// ImageUtility->updateData();
// ImageUtility->decode(image, 100000.0f);
// TargaUtility->encode(image);
// U8 *data = TargaUtility->getData();
// S32 data_size = TargaUtility->getDataSize();
// std::string file_path = gDirUtilp->getDirName(filename);
// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename;
// //S32 name_len = output_file.length();
// //strcpy(&output_file[name_len-3], "tga");
// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */
// char md5_hash_string[33]; /* Flawfinder: ignore */
// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */
// if (fOut)
// {
// if (fwrite(data, 1, data_size, fOut) != data_size)
// {
// LL_WARNS("Messaging") << "Short write" << LL_ENDL;
// }
// fseek(fOut, 0, SEEK_SET);
// fclose(fOut);
// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */
// LLMD5 my_md5_hash(fOut);
// my_md5_hash.hex_digest(md5_hash_string);
// }
// gImageChecksums.insert(std::pair<LLUUID, std::string>(image_id, md5_hash_string));
// }
// }
// ++current_image_count;
// if (current_image_count == exported_image_count && current_object_count == exported_object_count)
// {
// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL;
// export_complete();
// }
// else
// {
// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
// }
//}
void process_derez_ack(LLMessageSystem*, void**)
{
if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount();

View File

@ -228,6 +228,9 @@ LLGLSLShader gDeferredPBRAlphaProgram;
LLGLSLShader gDeferredSkinnedPBRAlphaProgram;
LLGLSLShader gDeferredPBRTerrainProgram;
LLGLSLShader gGLTFPBRMetallicRoughnessProgram;
//helper for making a rigged variant of a given shader
static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader)
{
@ -235,6 +238,7 @@ static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader
riggedShader.mFeatures = shader.mFeatures;
riggedShader.mFeatures.hasObjectSkinning = true;
riggedShader.mDefines = shader.mDefines; // NOTE: Must come before addPermutation
riggedShader.addPermutation("HAS_SKIN", "1");
riggedShader.mShaderFiles = shader.mShaderFiles;
riggedShader.mShaderLevel = shader.mShaderLevel;
@ -244,6 +248,49 @@ static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader
return riggedShader.createShader(NULL, NULL);
}
static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool alpha_blend, bool rigged)
{
variant.mName = shader.mName.c_str();
variant.mFeatures = shader.mFeatures;
variant.mShaderFiles = shader.mShaderFiles;
variant.mShaderLevel = shader.mShaderLevel;
variant.mShaderGroup = shader.mShaderGroup;
variant.mDefines = shader.mDefines; // NOTE: Must come before addPermutation
if (alpha_blend)
{
variant.addPermutation("ALPHA_BLEND", "1");
}
if (rigged)
{
variant.addPermutation("HAS_SKIN", "1");
variant.mFeatures.hasObjectSkinning = true;
}
return variant.createShader(NULL, NULL);
}
static bool make_gltf_variants(LLGLSLShader& shader)
{
shader.mFeatures.mGLTF = true;
shader.mGLTFVariants.resize(LLGLSLShader::NUM_GLTF_VARIANTS);
for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
{
bool alpha_blend = i & 1;
bool rigged = i & 2;
if (!make_gltf_variant(shader, shader.mGLTFVariants[i], alpha_blend, rigged))
{
return false;
}
}
return true;
}
#ifdef SHOW_ASSERT
// return true if there are no redundant shaders in the given vector
// also checks for redundant variants
@ -329,6 +376,7 @@ void LLViewerShaderMgr::finalizeShaderList()
mShaderList.push_back(&gDeferredDiffuseProgram);
mShaderList.push_back(&gDeferredBumpProgram);
mShaderList.push_back(&gDeferredPBROpaqueProgram);
mShaderList.push_back(&gGLTFPBRMetallicRoughnessProgram);
mShaderList.push_back(&gDeferredAvatarProgram);
mShaderList.push_back(&gDeferredTerrainProgram);
mShaderList.push_back(&gDeferredPBRTerrainProgram);
@ -1019,6 +1067,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDPBROpaqueProgram.unload();
gPBRGlowProgram.unload();
gDeferredPBROpaqueProgram.unload();
gGLTFPBRMetallicRoughnessProgram.unload();
gDeferredSkinnedPBROpaqueProgram.unload();
gDeferredPBRAlphaProgram.unload();
gDeferredSkinnedPBRAlphaProgram.unload();
@ -1209,6 +1258,22 @@ bool LLViewerShaderMgr::loadShadersDeferred()
llassert(success);
}
if (success)
{
gGLTFPBRMetallicRoughnessProgram.mName = "GLTF PBR Metallic Roughness Shader";
gGLTFPBRMetallicRoughnessProgram.mFeatures.hasSrgb = true;
gGLTFPBRMetallicRoughnessProgram.mShaderFiles.clear();
gGLTFPBRMetallicRoughnessProgram.mShaderFiles.push_back(make_pair("gltf/pbrmetallicroughnessV.glsl", GL_VERTEX_SHADER));
gGLTFPBRMetallicRoughnessProgram.mShaderFiles.push_back(make_pair("gltf/pbrmetallicroughnessF.glsl", GL_FRAGMENT_SHADER));
gGLTFPBRMetallicRoughnessProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gGLTFPBRMetallicRoughnessProgram.clearPermutations();
success = make_gltf_variants(gGLTFPBRMetallicRoughnessProgram);
llassert(success);
}
if (success)
{
gPBRGlowProgram.mName = " PBR Glow Shader";

View File

@ -335,6 +335,12 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const s
return gTextureList.getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
}
//static
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype)
{
return gTextureList.getImageFromMemory(data, size, mimetype);
}
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host)
{
return gTextureList.getImageFromHost(image_id, f_type, host);

View File

@ -440,8 +440,6 @@ private:
bool mForceCallbackFetch;
protected:
std::string mLocalFileName;
S32 mOrigWidth;
S32 mOrigHeight;
@ -692,6 +690,8 @@ public:
static LLViewerFetchedTexture* getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host) ;
static LLViewerFetchedTexture* getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype);
static void init() ;
static void cleanup() ;
};

View File

@ -360,7 +360,7 @@ void LLViewerTextureList::shutdown()
mImageList.clear();
mInitialized = false; //prevent loading textures again.
mInitialized = false ; //prevent loading textures again.
}
void LLViewerTextureList::dump()
@ -518,6 +518,26 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string&
return imagep;
}
LLViewerFetchedTexture* LLViewerTextureList::getImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
{
LLPointer<LLImageFormatted> image = LLImageFormatted::loadFromMemory(data, size, mimetype);
if (image)
{
LLPointer<LLImageRaw> raw_image = new LLImageRaw();
image->decode(raw_image, 0.f);
LLPointer<LLViewerFetchedTexture> imagep = new LLViewerFetchedTexture(raw_image, FTT_LOCAL_FILE, true);
addImage(imagep, TEX_LIST_STANDARD);
imagep->dontDiscard();
imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_PREVIEW);
return imagep;
}
else
{
return nullptr;
}
}
LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id,
FTType f_type,
@ -631,8 +651,8 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,
imagep->forceActive() ;
}
mFastCacheList.insert(imagep);
imagep->setInFastCacheList(true);
mFastCacheList.insert(imagep);
imagep->setInFastCacheList(true);
return imagep ;
}
@ -1310,63 +1330,63 @@ bool LLViewerTextureList::createUploadFile(const std::string& filename,
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
try
{
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
return false;
}
if (!image->load(filename))
{
image->setLastError("Couldn't load the image to be uploaded.");
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
image->setLastError("Couldn't decode the image to be uploaded.");
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
{
std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
min_image_dimentions,
min_image_dimentions,
image->getWidth(),
image->getHeight());
image->setLastError(reason);
return false;
}
// Convert to j2c (JPEG2000) and save the file locally
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
return false;
}
if (!compressedImage->save(out_filename))
{
image->setLastError("Couldn't create the jpeg2000 image for upload.");
LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
return false;
}
// Test to see if the encode and save worked
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
if (!integrity_test->loadAndValidate(out_filename))
{
image->setLastError("The created jpeg2000 image is corrupt.");
LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
return false;
}
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
return false;
}
if (!image->load(filename))
{
image->setLastError("Couldn't load the image to be uploaded.");
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
image->setLastError("Couldn't decode the image to be uploaded.");
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
{
std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
min_image_dimentions,
min_image_dimentions,
image->getWidth(),
image->getHeight());
image->setLastError(reason);
return false;
}
// Convert to j2c (JPEG2000) and save the file locally
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
return false;
}
if (!compressedImage->save(out_filename))
{
image->setLastError("Couldn't create the jpeg2000 image for upload.");
LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
return false;
}
// Test to see if the encode and save worked
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
if (!integrity_test->loadAndValidate( out_filename ))
{
image->setLastError("The created jpeg2000 image is corrupt.");
LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
return false;
}
}
catch (...)
{

View File

@ -192,6 +192,8 @@ private:
const LLUUID& force_id = LLUUID::null
);
LLViewerFetchedTexture* getImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
LLViewerFetchedTexture* createImage(const LLUUID &image_id,
FTType f_type,
bool usemipmap = true,

View File

@ -2879,14 +2879,6 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClickGLTFSaveAs" />
</menu_item_call>
<menu_item_call
label="Decompose..."
name="Decompose...">
<menu_item_call.on_enable
function="EnableGLTF"/>
<menu_item_call.on_click
function="Advanced.ClickGLTFDecompose" />
</menu_item_call>
<menu_item_call
label="Upload..."
name="Upload...">

View File

@ -12470,6 +12470,28 @@ are wearing now.
yestext="OK"/>
</notification>
<notification
icon="alertmodal.tga"
name="GLTFLoadFailed"
type="alert">
Failed to load GLTF file. See log for details.
<tag>fail</tag>
<usetemplate
name="okbutton"
yestext="OK"/>
</notification>
<notification
icon="alertmodal.tga"
name="GLTFSaveFailed"
type="alert">
Failed to save GLTF file. See log for details.
<tag>fail</tag>
<usetemplate
name="okbutton"
yestext="OK"/>
</notification>
<notification
icon="alertmodal.tga"
name="GLTFSaveSelection"