summaryrefslogtreecommitdiff
path: root/indra/newview/gltf/primitive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/gltf/primitive.cpp')
-rw-r--r--indra/newview/gltf/primitive.cpp604
1 files changed, 499 insertions, 105 deletions
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index b57a0af18d..2280c7004e 100644
--- a/indra/newview/gltf/primitive.cpp
+++ b/indra/newview/gltf/primitive.cpp
@@ -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
@@ -28,12 +28,295 @@
#include "asset.h"
#include "buffer_util.h"
+#include "../llviewershadermgr.h"
+
+#include "mikktspace/mikktspace.hh"
+
+#include "meshoptimizer/meshoptimizer.h"
-#include "../lltinygltfhelper.h"
using namespace LL::GLTF;
+using namespace boost::json;
+
+
+// Mesh data useful for Mikktspace tangent generation (and flat normal generation)
+struct MikktMesh
+{
+ std::vector<LLVector3> p; //positions
+ std::vector<LLVector3> n; //normals
+ std::vector<LLVector4> t; //tangents
+ std::vector<LLVector2> tc0; //texcoords 0
+ std::vector<LLVector2> tc1; //texcoords 1
+ std::vector<LLColor4U> c; //colors
+ std::vector<LLVector4> w; //weights
+ std::vector<U64> j; //joints
+
+ // initialize from src primitive and make an unrolled triangle list
+ // returns false if the Primitive cannot be converted to a triangle list
+ bool copy(const Primitive* prim)
+ {
+ bool indexed = !prim->mIndexArray.empty();
+ size_t vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size();
+
+ size_t triangle_count = 0;
+
+ if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP ||
+ prim->mMode == Primitive::Mode::TRIANGLE_FAN)
+ {
+ triangle_count = vert_count - 2;
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLES)
+ {
+ triangle_count = vert_count / 3;
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32)prim->mMode << LL_ENDL;
+ return false;
+ }
+
+ vert_count = triangle_count * 3;
+ llassert(vert_count <= size_t(U32_MAX)); // triangle_count will also naturally be under the limit
+
+ p.resize(vert_count);
+ n.resize(vert_count);
+ tc0.resize(vert_count);
+ c.resize(vert_count);
+
+ bool has_normals = !prim->mNormals.empty();
+ if (has_normals)
+ {
+ n.resize(vert_count);
+ }
+ bool has_tangents = !prim->mTangents.empty();
+ if (has_tangents)
+ {
+ t.resize(vert_count);
+ }
+
+ bool rigged = !prim->mWeights.empty();
+ if (rigged)
+ {
+ w.resize(vert_count);
+ j.resize(vert_count);
+ }
+
+ bool multi_uv = !prim->mTexCoords1.empty();
+ if (multi_uv)
+ {
+ tc1.resize(vert_count);
+ }
+
+ for (U32 tri_idx = 0; tri_idx < U32(triangle_count); ++tri_idx)
+ {
+ U32 idx[3];
+
+ if (prim->mMode == Primitive::Mode::TRIANGLES)
+ {
+ idx[0] = tri_idx * 3;
+ idx[1] = tri_idx * 3 + 1;
+ idx[2] = tri_idx * 3 + 2;
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP)
+ {
+ idx[0] = tri_idx;
+ idx[1] = tri_idx + 1;
+ idx[2] = tri_idx + 2;
+
+ if (tri_idx % 2 != 0)
+ {
+ std::swap(idx[1], idx[2]);
+ }
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLE_FAN)
+ {
+ idx[0] = 0;
+ idx[1] = tri_idx + 1;
+ idx[2] = tri_idx + 2;
+ }
+
+ if (indexed)
+ {
+ idx[0] = prim->mIndexArray[idx[0]];
+ idx[1] = prim->mIndexArray[idx[1]];
+ idx[2] = prim->mIndexArray[idx[2]];
+ }
+
+ for (U32 v = 0; v < 3; ++v)
+ {
+ U32 i = tri_idx * 3 + v;
+ p[i].set(prim->mPositions[idx[v]].getF32ptr());
+ tc0[i].set(prim->mTexCoords0[idx[v]]);
+ c[i] = prim->mColors[idx[v]];
+
+ if (multi_uv)
+ {
+ tc1[i].set(prim->mTexCoords1[idx[v]]);
+ }
+
+ if (has_normals)
+ {
+ n[i].set(prim->mNormals[idx[v]].getF32ptr());
+ }
+
+ if (rigged)
+ {
+ w[i].set(prim->mWeights[idx[v]].getF32ptr());
+ j[i] = prim->mJoints[idx[v]];
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void genNormals()
+ {
+ size_t tri_count = p.size() / 3;
+ for (size_t i = 0; i < tri_count; ++i)
+ {
+ LLVector3 v0 = p[i * 3];
+ LLVector3 v1 = p[i * 3 + 1];
+ LLVector3 v2 = p[i * 3 + 2];
+
+ LLVector3 normal = (v1 - v0) % (v2 - v0);
+ normal.normalize();
+
+ n[i * 3] = normal;
+ n[i * 3 + 1] = normal;
+ n[i * 3 + 2] = normal;
+ }
+ }
+
+ void genTangents()
+ {
+ t.resize(p.size());
+ mikk::Mikktspace ctx(*this);
+ ctx.genTangSpace();
+ }
+
+ // write to target primitive as an indexed triangle list
+ // Only modifies runtime data, does not modify the original GLTF data
+ void write(Primitive* prim) const
+ {
+ //re-weld
+ std::vector<meshopt_Stream> mos =
+ {
+ { &p[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &n[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &t[0], sizeof(LLVector4), sizeof(LLVector4) },
+ { &tc0[0], sizeof(LLVector2), sizeof(LLVector2) },
+ { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) }
+ };
+
+ if (!w.empty())
+ {
+ mos.push_back({ &w[0], sizeof(LLVector4), sizeof(LLVector4) });
+ mos.push_back({ &j[0], sizeof(U64), sizeof(U64) });
+ }
+
+ if (!tc1.empty())
+ {
+ mos.push_back({ &tc1[0], sizeof(LLVector2), sizeof(LLVector2) });
+ }
+
+ std::vector<U32> remap;
+ remap.resize(p.size());
+
+ size_t stream_count = mos.size();
+
+ size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos.data(), stream_count);
+
+ prim->mTexCoords0.resize(vert_count);
+ prim->mNormals.resize(vert_count);
+ prim->mTangents.resize(vert_count);
+ prim->mPositions.resize(vert_count);
+ prim->mColors.resize(vert_count);
+ if (!w.empty())
+ {
+ prim->mWeights.resize(vert_count);
+ prim->mJoints.resize(vert_count);
+ }
+ if (!tc1.empty())
+ {
+ prim->mTexCoords1.resize(vert_count);
+ }
+
+ prim->mIndexArray.resize(remap.size());
+
+ for (int i = 0; i < remap.size(); ++i)
+ {
+ U32 src_idx = i;
+ U32 dst_idx = remap[i];
+
+ prim->mIndexArray[i] = dst_idx;
+
+ prim->mPositions[dst_idx].load3(p[src_idx].mV);
+ prim->mNormals[dst_idx].load3(n[src_idx].mV);
+ prim->mTexCoords0[dst_idx] = tc0[src_idx];
+ prim->mTangents[dst_idx].loadua(t[src_idx].mV);
+ prim->mColors[dst_idx] = c[src_idx];
+
+ if (!w.empty())
+ {
+ prim->mWeights[dst_idx].loadua(w[src_idx].mV);
+ prim->mJoints[dst_idx] = j[src_idx];
+ }
+
+ if (!tc1.empty())
+ {
+ prim->mTexCoords1[dst_idx] = tc1[src_idx];
+ }
+ }
+
+ prim->mGLMode = LLRender::TRIANGLES;
+ }
+
+ uint32_t GetNumFaces()
+ {
+ return uint32_t(p.size()/3);
+ }
+
+ uint32_t GetNumVerticesOfFace(const uint32_t face_num)
+ {
+ return 3;
+ }
+
+ mikk::float3 GetPosition(const uint32_t face_num, const uint32_t vert_num)
+ {
+ F32* v = p[face_num * 3 + vert_num].mV;
+ return mikk::float3(v);
+ }
+
+ mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num)
+ {
+ F32* uv = tc0[face_num * 3 + vert_num].mV;
+ return mikk::float3(uv[0], 1.f-uv[1], 1.0f);
+ }
+
+ mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num)
+ {
+ F32* normal = n[face_num * 3 + vert_num].mV;
+ return mikk::float3(normal);
+ }
+
+ void SetTangentSpace(const uint32_t face_num, const uint32_t vert_num, mikk::float3 T, bool orientation)
+ {
+ S32 i = face_num * 3 + vert_num;
+ t[i].set(T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
+ }
+};
+
+
+static void vertical_flip(std::vector<LLVector2>& texcoords)
+{
+ for (auto& tc : texcoords)
+ {
+ tc[1] = 1.f - tc[1];
+ }
+}
-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
@@ -66,7 +349,11 @@ void Primitive::allocateGLResources(Asset& asset)
}
else if (attribName == "TEXCOORD_0")
{
- copy(asset, accessor, mTexCoords);
+ copy(asset, accessor, mTexCoords0);
+ }
+ else if (attribName == "TEXCOORD_1")
+ {
+ copy(asset, accessor, mTexCoords1);
}
else if (attribName == "JOINTS_0")
{
@@ -83,96 +370,205 @@ void Primitive::allocateGLResources(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mIndices];
copy(asset, accessor, mIndexArray);
- }
- U32 mask = ATTRIBUTE_MASK;
-
- if (!mWeights.empty())
- {
- mask |= LLVertexBuffer::MAP_WEIGHT4;
+ for (auto& idx : mIndexArray)
+ {
+ if (idx >= mPositions.size())
+ {
+ LL_WARNS("GLTF") << "Invalid index array" << LL_ENDL;
+ return false;
+ }
+ }
}
- mVertexBuffer = new LLVertexBuffer(mask);
- mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices
-
- mVertexBuffer->setBuffer();
- mVertexBuffer->setPositionData(mPositions.data());
+ U32 mask = LLVertexBuffer::MAP_VERTEX;
- if (!mIndexArray.empty())
+ if (!mWeights.empty())
{
- mVertexBuffer->setIndexData(mIndexArray.data());
+ mask |= LLVertexBuffer::MAP_WEIGHT4;
+ mask |= LLVertexBuffer::MAP_JOINT;
}
- if (mTexCoords.empty())
+ if (mTexCoords0.empty())
{
- mTexCoords.resize(mPositions.size());
+ mTexCoords0.resize(mPositions.size());
}
- // flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
- for (auto& tc : mTexCoords)
- {
- tc[1] = 1.f - tc[1];
- }
- mVertexBuffer->setTexCoordData(mTexCoords.data());
+ mask |= LLVertexBuffer::MAP_TEXCOORD0;
- for (auto& tc : mTexCoords)
+ if (!mTexCoords1.empty())
{
- tc[1] = 1.f - tc[1];
+ mask |= LLVertexBuffer::MAP_TEXCOORD1;
}
if (mColors.empty())
{
mColors.resize(mPositions.size(), LLColor4U::white);
}
-
+
+ mShaderVariant = 0;
+
+ // TODO: support colorless vertex buffers
+ mask |= LLVertexBuffer::MAP_COLOR;
+
+ bool unlit = false;
+
// bake material basecolor into color array
if (mMaterial != INVALID_INDEX)
{
const Material& material = asset.mMaterials[mMaterial];
- LLColor4 baseColor = material.mMaterial->mBaseColor;
+ LLColor4 baseColor(glm::value_ptr(material.mPbrMetallicRoughness.mBaseColorFactor));
for (auto& dst : mColors)
{
dst = LLColor4U(baseColor * LLColor4(dst));
}
+
+ if (material.mUnlit.mPresent)
+ { // material uses KHR_materials_unlit
+ mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
+ unlit = true;
+ }
+
+ if (material.isMultiUV())
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::MULTI_UV;
+ }
}
- mVertexBuffer->setColorData(mColors.data());
+ if (mNormals.empty() && !unlit)
+ {
+ mTangents.clear();
+
+ if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP)
+ { //no normals and no surfaces, this primitive is unlit
+ mTangents.clear();
+ mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
+ unlit = true;
+ }
+ else
+ {
+ // unroll into non-indexed array of flat shaded triangles
+ MikktMesh data;
+ if (!data.copy(this))
+ {
+ return false;
+ }
+
+ data.genNormals();
+ data.genTangents();
+ data.write(this);
+ }
+ }
- if (mNormals.empty())
+ if (mTangents.empty() && !unlit)
+ { // NOTE: must be done last because tangent generation rewrites the other arrays
+ // adapted from usage of Mikktspace in llvolume.cpp
+ if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP)
+ {
+ // for points and lines, just make sure tangent is perpendicular to normal
+ mTangents.resize(mNormals.size());
+ LLVector4a up(0.f, 0.f, 1.f, 0.f);
+ LLVector4a left(1.f, 0.f, 0.f, 0.f);
+ for (U32 i = 0; i < mNormals.size(); ++i)
+ {
+ if (fabsf(mNormals[i].getF32ptr()[2]) < 0.999f)
+ {
+ mTangents[i] = up.cross3(mNormals[i]);
+ }
+ else
+ {
+ mTangents[i] = left.cross3(mNormals[i]);
+ }
+
+ mTangents[i].getF32ptr()[3] = 1.f;
+ }
+ }
+ else
+ {
+ MikktMesh data;
+ if (!data.copy(this))
+ {
+ return false;
+ }
+
+ data.genTangents();
+ data.write(this);
+ }
+ }
+
+ if (!mNormals.empty())
{
- mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
+ mask |= LLVertexBuffer::MAP_NORMAL;
}
-
- mVertexBuffer->setNormalData(mNormals.data());
- if (mTangents.empty())
+ if (!mTangents.empty())
{
- // TODO: generate tangents if needed
- mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1));
+ mask |= LLVertexBuffer::MAP_TANGENT;
}
- mVertexBuffer->setTangentData(mTangents.data());
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
+ }
+
+ mVertexBuffer = new LLVertexBuffer(mask);
+ // we store these buffer sizes as S32 elsewhere
+ llassert(mPositions.size() <= size_t(S32_MAX));
+ llassert(mIndexArray.size() <= size_t(S32_MAX / 2));
+ mVertexBuffer->allocateBuffer(U32(mPositions.size()), U32(mIndexArray.size() * 2)); // double the size of the index buffer for 32-bit indices
+
+ mVertexBuffer->setBuffer();
+ mVertexBuffer->setPositionData(mPositions.data());
+ mVertexBuffer->setColorData(mColors.data());
+
+ if (!mNormals.empty())
+ {
+ mVertexBuffer->setNormalData(mNormals.data());
+ }
+ if (!mTangents.empty())
+ {
+ mVertexBuffer->setTangentData(mTangents.data());
+ }
if (!mWeights.empty())
{
- std::vector<LLVector4a> weight_data;
- weight_data.resize(mWeights.size());
+ mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED;
+ mVertexBuffer->setWeight4Data(mWeights.data());
+ mVertexBuffer->setJointData(mJoints.data());
+ }
+
+ // flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
+ vertical_flip(mTexCoords0);
+ mVertexBuffer->setTexCoord0Data(mTexCoords0.data());
+ vertical_flip(mTexCoords0);
+
+ if (!mTexCoords1.empty())
+ {
+ vertical_flip(mTexCoords1);
+ mVertexBuffer->setTexCoord1Data(mTexCoords1.data());
+ vertical_flip(mTexCoords1);
+ }
- F32 max_weight = 1.f - FLT_EPSILON*100.f;
- LLVector4a maxw(max_weight, max_weight, max_weight, max_weight);
- for (U32 i = 0; i < mWeights.size(); ++i)
- {
- LLVector4a& w = weight_data[i];
- w.setMin(mWeights[i], maxw);
- w.add(mJoints[i]);
- };
- mVertexBuffer->setWeight4Data(weight_data.data());
+ if (!mIndexArray.empty())
+ {
+ mVertexBuffer->setIndexData(mIndexArray.data());
}
-
+
createOctree();
-
+
mVertexBuffer->unbind();
+
+ if (mMaterial != INVALID_INDEX)
+ {
+ Material& material = asset.mMaterials[mMaterial];
+ if (material.mAlphaMode == Material::AlphaMode::BLEND)
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ }
+ }
+
+ return true;
}
void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
@@ -218,7 +614,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
@@ -235,14 +631,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
@@ -266,7 +662,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
@@ -290,14 +686,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;
@@ -327,13 +723,13 @@ const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start,
//create a proxy LLVolumeFace for the raycast
LLVolumeFace face;
face.mPositions = mPositions.data();
- face.mTexCoords = mTexCoords.data();
+ face.mTexCoords = mTexCoords0.data();
face.mNormals = mNormals.data();
face.mTangents = mTangents.data();
face.mIndices = nullptr; // unreferenced
- face.mNumIndices = mIndexArray.size();
- face.mNumVertices = mPositions.size();
+ face.mNumIndices = S32(mIndexArray.size());
+ face.mNumVertices = S32(mPositions.size());
LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
intersect.traverse(mOctree);
@@ -351,50 +747,48 @@ Primitive::~Primitive()
mOctree = nullptr;
}
-
-const Primitive& Primitive::operator=(const tinygltf::Primitive& src)
+LLRender::eGeomModes gltf_mode_to_gl_mode(Primitive::Mode mode)
{
- // 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;
- }
-
- switch (mMode)
- {
- case TINYGLTF_MODE_POINTS:
- mGLMode = LLRender::POINTS;
- break;
- case TINYGLTF_MODE_LINE:
- mGLMode = LLRender::LINES;
- break;
- case TINYGLTF_MODE_LINE_LOOP:
- mGLMode = LLRender::LINE_LOOP;
- break;
- case TINYGLTF_MODE_LINE_STRIP:
- mGLMode = LLRender::LINE_STRIP;
- break;
- case TINYGLTF_MODE_TRIANGLES:
- mGLMode = LLRender::TRIANGLES;
- break;
- case TINYGLTF_MODE_TRIANGLE_STRIP:
- mGLMode = LLRender::TRIANGLE_STRIP;
- break;
- case TINYGLTF_MODE_TRIANGLE_FAN:
- mGLMode = LLRender::TRIANGLE_FAN;
- break;
+ switch (mode)
+ {
+ case Primitive::Mode::POINTS:
+ return LLRender::POINTS;
+ case Primitive::Mode::LINES:
+ return LLRender::LINES;
+ case Primitive::Mode::LINE_LOOP:
+ return LLRender::LINE_LOOP;
+ case Primitive::Mode::LINE_STRIP:
+ return LLRender::LINE_STRIP;
+ case Primitive::Mode::TRIANGLES:
+ return LLRender::TRIANGLES;
+ case Primitive::Mode::TRIANGLE_STRIP:
+ return LLRender::TRIANGLE_STRIP;
+ case Primitive::Mode::TRIANGLE_FAN:
+ return LLRender::TRIANGLE_FAN;
default:
- mGLMode = GL_TRIANGLES;
+ return LLRender::TRIANGLES;
}
+}
+
+void Primitive::serialize(boost::json::object& dst) const
+{
+ write(mMaterial, "material", dst, -1);
+ write(mMode, "mode", dst, Primitive::Mode::TRIANGLES);
+ write(mIndices, "indices", dst, INVALID_INDEX);
+ write(mAttributes, "attributes", dst);
+}
+const Primitive& Primitive::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "material", mMaterial);
+ copy(src, "mode", mMode);
+ copy(src, "indices", mIndices);
+ copy(src, "attributes", mAttributes);
+
+ mGLMode = gltf_mode_to_gl_mode(mMode);
+ }
return *this;
}
+