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 parsermaster
parent
db627bc354
commit
2f41200384
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// ========================================================================================================
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ namespace LL
|
|||
constexpr S32 MIRRORED_REPEAT = 33648;
|
||||
constexpr S32 REPEAT = 10497;
|
||||
|
||||
|
||||
class Asset;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() ;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 (...)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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...">
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue