Change gltf structures representing weights and jointIds to single data structure to make code cleaner and simplify further generalization. Added array versions of AttributeDefinition and AddAttributeToPrimitive to facilitate this.

This commit is contained in:
hhalen 2019-04-18 16:20:36 -07:00
parent 467e7b2171
commit c4395b9b92
7 changed files with 164 additions and 162 deletions

View File

@ -140,10 +140,11 @@ int main(int argc, char* argv[]) {
gltfOptions.useBlendShapeTangents,
"Include blend shape tangents, if reported present by the FBX SDK.");
app.add_flag(
app.add_option(
"--normalize-weights",
gltfOptions.normalizeSkinningWeights,
"Normalize skinning weights.");
"Normalize skinning weights.",
true);
app.add_option(
"--skinning-weights",
@ -156,8 +157,7 @@ int main(int argc, char* argv[]) {
"-k,--keep-attribute",
[&](std::vector<std::string> attributes) -> bool {
gltfOptions.keepAttribs =
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 |
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3;
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
for (std::string attribute : attributes) {
if (attribute == "position") {
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION;

View File

@ -123,6 +123,31 @@ class GltfModel {
return accessor;
};
template <class T>
std::shared_ptr<AccessorData> AddAttributeArrayToPrimitive(
BufferData& buffer,
const RawModel& surfaceModel,
PrimitiveData& primitive,
const AttributeArrayDefinition<T>& attrDef) {
// copy attribute data into vector
std::vector<T> attribArr;
surfaceModel.GetArrayAttributeArray<T>(attribArr, attrDef.rawAttributeIx, attrDef.arrayOffset);
std::shared_ptr<AccessorData> accessor;
if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) {
primitive.AddDracoArrayAttrib(attrDef, attribArr);
accessor = accessors.hold(new AccessorData(attrDef.glType));
accessor->count = attribArr.size();
}
else {
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));
}
primitive.AddAttrib(attrDef.gltfName, *accessor);
return accessor;
};
template <class T>
void serializeHolder(json& glTFJson, std::string key, const Holder<T> holder) {
if (!holder.ptrs.empty()) {

View File

@ -532,77 +532,31 @@ ModelData* Raw2Gltf(
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS(
"JOINTS_0",
&RawVertex::jointIndices0,
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
for (int i = 0; i < surfaceModel.GetGlobalWeightCount(); i += 4) {
const AttributeArrayDefinition<Vec4i> ATTR_JOINTS(
std::string("JOINTS_") + std::to_string(i/4),
//"JOINTS_0",
&RawVertex::jointIndices,
GLT_VEC4I,
draco::GeometryAttribute::GENERIC,
draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
draco::DT_UINT16,
i/4);
gltf->AddAttributeArrayToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
}
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
"WEIGHTS_0",
&RawVertex::jointWeights0,
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
for (int i = 0; i < surfaceModel.GetGlobalWeightCount(); i += 4) {
const AttributeArrayDefinition<Vec4f> ATTR_WEIGHTS(
std::string("WEIGHTS_") + std::to_string(i/4),
//"WEIGHTS_0",
&RawVertex::jointWeights,
GLT_VEC4F,
draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS(
"JOINTS_1",
&RawVertex::jointIndices1,
GLT_VEC4I,
draco::GeometryAttribute::GENERIC,
draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
"WEIGHTS_1",
&RawVertex::jointWeights1,
GLT_VEC4F,
draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS(
"JOINTS_0",
&RawVertex::jointIndices2,
GLT_VEC4I,
draco::GeometryAttribute::GENERIC,
draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
"WEIGHTS_0",
&RawVertex::jointWeights2,
GLT_VEC4F,
draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS(
"JOINTS_0",
&RawVertex::jointIndices3,
GLT_VEC4I,
draco::GeometryAttribute::GENERIC,
draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
"WEIGHTS_0",
&RawVertex::jointWeights3,
GLT_VEC4F,
draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
draco::DT_FLOAT32,
i/4);
gltf->AddAttributeArrayToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
}
}
// each channel present in the mesh always ends up a target in the primitive

View File

@ -154,6 +154,44 @@ struct AttributeDefinition {
glType(_glType),
dracoAttribute(draco::GeometryAttribute::INVALID),
dracoComponentType(draco::DataType::DT_INVALID) {}
AttributeDefinition(
const std::string gltfName,
const T RawVertex::*rawAttributeIx,
const GLType& _glType,
const draco::GeometryAttribute::Type dracoAttribute,
const draco::DataType dracoComponentType,
const int arrayOffset)
: gltfName(gltfName),
rawAttributeIx(rawAttributeIx),
glType(_glType),
dracoAttribute(dracoAttribute),
dracoComponentType(dracoComponentType),
arrayOffset(arrayOffset){}
};
template <class T>
struct AttributeArrayDefinition {
const std::string gltfName;
const T (RawVertex::*rawAttributeIx)[(RawModel::MAX_SUPPORTED_WEIGHTS -1 ) / 4 + 1];
const GLType glType;
const int arrayOffset;
const draco::GeometryAttribute::Type dracoAttribute;
const draco::DataType dracoComponentType;
AttributeArrayDefinition(
const std::string gltfName,
const T (RawVertex::*rawAttributeIx)[(RawModel::MAX_SUPPORTED_WEIGHTS - 1) / 4 + 1],
const GLType& _glType,
const draco::GeometryAttribute::Type dracoAttribute,
const draco::DataType dracoComponentType,
const int arrayOffset)
: gltfName(gltfName),
rawAttributeIx(rawAttributeIx),
glType(_glType),
dracoAttribute(dracoAttribute),
dracoComponentType(dracoComponentType),
arrayOffset(arrayOffset) {}
};
struct AccessorData;

View File

@ -62,6 +62,32 @@ struct PrimitiveData {
dracoAttributes[attribute.gltfName] = dracoAttId;
}
template <class T>
void AddDracoArrayAttrib(const AttributeArrayDefinition<T> attribute, const std::vector<T>& attribArr) {
draco::PointAttribute att;
int8_t componentCount = attribute.glType.count;
att.Init(
attribute.dracoAttribute,
nullptr,
componentCount,
attribute.dracoComponentType,
false,
componentCount * draco::DataTypeLength(attribute.dracoComponentType),
0);
const int dracoAttId = dracoMesh->AddAttribute(att, true, attribArr.size());
draco::PointAttribute* attPtr = dracoMesh->attribute(dracoAttId);
std::vector<uint8_t> buf(sizeof(T));
for (uint32_t ii = 0; ii < attribArr.size(); ii++) {
uint8_t* ptr = &buf[0];
attribute.glType.write(ptr, attribArr[ii]);
attPtr->SetAttributeValue(attPtr->mapped_index(draco::PointIndex(ii)), ptr);
}
dracoAttributes[attribute.gltfName] = dracoAttId;
}
void NoteDracoBuffer(const BufferViewData& data);
const int indices;

View File

@ -27,10 +27,7 @@ bool RawVertex::operator==(const RawVertex& other) const {
return (position == other.position) && (normal == other.normal) && (tangent == other.tangent) &&
(binormal == other.binormal) && (color == other.color) && (uv0 == other.uv0) &&
(uv1 == other.uv1) &&
(jointWeights0 == other.jointWeights0) && (jointWeights1 == other.jointWeights1) &&
(jointWeights2 == other.jointWeights2) && (jointWeights3 == other.jointWeights3) &&
(jointIndices0 == other.jointIndices0) && (jointIndices1 == other.jointIndices1) &&
(jointIndices2 == other.jointIndices2) && (jointIndices3 == other.jointIndices3) &&
(jointWeights == other.jointWeights) && (jointIndices == other.jointIndices) &&
(polarityUv0 == other.polarityUv0) &&
(blendSurfaceIx == other.blendSurfaceIx) && (blends == other.blends);
}
@ -59,17 +56,8 @@ size_t RawVertex::Difference(const RawVertex& other) const {
attributes |= RAW_VERTEX_ATTRIBUTE_UV1;
}
// Always need both or neither.
if (jointIndices0 != other.jointIndices0 || jointWeights0 != other.jointWeights0) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0;
}
if (jointIndices1 != other.jointIndices1 || jointWeights1 != other.jointWeights1) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1;
}
if (jointIndices2 != other.jointIndices2 || jointWeights2 != other.jointWeights2) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2;
}
if (jointIndices3 != other.jointIndices3 || jointWeights3 != other.jointWeights3) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3;
if (jointIndices != other.jointIndices || jointWeights != other.jointWeights) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
}
return attributes;
}
@ -409,7 +397,7 @@ void RawModel::Condense(const int maxSkinningWeights, const bool normalizeWeight
}
{
int globalMaxWeights = 0;
globalMaxWeights = 0;
for (auto& vertex: vertices) {
// Sort from largest to smallest weight.
@ -432,26 +420,15 @@ void RawModel::Condense(const int maxSkinningWeights, const bool normalizeWeight
}
if (globalMaxWeights > 0) {
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0);
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0);
}
if (globalMaxWeights > 1) {
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1);
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1);
}
if (globalMaxWeights > 2) {
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2);
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2);
}
if (globalMaxWeights > 3) {
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3);
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3);
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES);
AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS);
}
assert(globalMaxWeights <= RawModel::MAX_SUPPORTED_WEIGHTS);
// Copy to gltf friendly structure
for (auto& vertex : vertices) {
for (int i = 0; i < globalMaxWeights; i += 4) { // Ensure all vertices have the same number of streams
for (int i = 0; i < globalMaxWeights; i += 4) {
Vec4f weights{0.0};
Vec4i jointIds{0,0,0,0};
for (int j = i; j < i + 4 && j < vertex.skinningInfo.size(); j++) {
@ -459,26 +436,8 @@ void RawModel::Condense(const int maxSkinningWeights, const bool normalizeWeight
jointIds[j - i] = vertex.skinningInfo[j].jointIndex;
}
const int vertexStream = i / 4;
switch (vertexStream) {
case 0:
vertex.jointIndices0 = jointIds;
vertex.jointWeights0 = weights;
break;
case 1:
vertex.jointIndices1 = jointIds;
vertex.jointWeights1 = weights;
break;
case 2:
vertex.jointIndices2 = jointIds;
vertex.jointWeights2 = weights;
break;
case 3:
vertex.jointIndices3 = jointIds;
vertex.jointWeights3 = weights;
break;
default:
assert(0);
}
vertex.jointIndices[vertexStream] = jointIds;
vertex.jointWeights[vertexStream] = weights;
}
}
}
@ -631,6 +590,7 @@ void RawModel::CreateMaterialModels(
surfaces[sortedTriangles[i - 1].surfaceIndex].discrete))) {
materialModels.resize(materialModels.size() + 1);
model = &materialModels[materialModels.size() - 1];
model->globalMaxWeights = globalMaxWeights;
}
// FIXME: will have to unlink from the nodes, transform both surfaces into a
@ -658,8 +618,7 @@ void RawModel::CreateMaterialModels(
if (keepAttribs != -1) {
int keep = keepAttribs;
if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) {
keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 |
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3;
keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
}
if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) {
keep |= RAW_VERTEX_ATTRIBUTE_POSITION;
@ -700,29 +659,13 @@ void RawModel::CreateMaterialModels(
if ((keep & RAW_VERTEX_ATTRIBUTE_UV1) == 0) {
vertex.uv1 = defaultVertex.uv1;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0) == 0) {
vertex.jointIndices0 = defaultVertex.jointIndices0;
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) == 0) {
for (int i = 0; i < RawModel::MAX_SUPPORTED_WEIGHTS ; i++)
vertex.jointIndices[i] = defaultVertex.jointIndices[i];
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0) == 0) {
vertex.jointWeights0 = defaultVertex.jointWeights0;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) == 0) {
vertex.jointIndices1 = defaultVertex.jointIndices1;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) == 0) {
vertex.jointWeights1 = defaultVertex.jointWeights1;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2) == 0) {
vertex.jointIndices2 = defaultVertex.jointIndices2;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2) == 0) {
vertex.jointWeights2 = defaultVertex.jointWeights2;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3) == 0) {
vertex.jointIndices3 = defaultVertex.jointIndices3;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3) == 0) {
vertex.jointWeights3 = defaultVertex.jointWeights3;
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) == 0) {
for (int i = 0; i < RawModel::MAX_SUPPORTED_WEIGHTS; i++)
vertex.jointWeights[i] = defaultVertex.jointWeights[i];
}
}

View File

@ -15,6 +15,8 @@
#include "FBX2glTF.h"
#define MAX_SKINNING_WEIGHTS 32
enum RawVertexAttribute {
RAW_VERTEX_ATTRIBUTE_POSITION = 1 << 0,
RAW_VERTEX_ATTRIBUTE_NORMAL = 1 << 1,
@ -23,14 +25,8 @@ enum RawVertexAttribute {
RAW_VERTEX_ATTRIBUTE_COLOR = 1 << 4,
RAW_VERTEX_ATTRIBUTE_UV0 = 1 << 5,
RAW_VERTEX_ATTRIBUTE_UV1 = 1 << 6,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 = 1 << 7,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 = 1 << 8,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 = 1 << 9,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 = 1 << 10,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 = 1 << 11,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2 = 1 << 12,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 = 1 << 13,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3 = 1 << 14,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES = 1 << 7,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS = 1 << 8,
RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31
};
@ -65,14 +61,9 @@ struct RawVertex {
Vec4f color{0.0f};
Vec2f uv0{0.0f};
Vec2f uv1{0.0f};
Vec4i jointIndices0{0,0,0,0};
Vec4i jointIndices1{0,0,0,0};
Vec4i jointIndices2{0,0,0,0};
Vec4i jointIndices3{0,0,0,0};
Vec4f jointWeights0{0.0f};
Vec4f jointWeights1{0.0f};
Vec4f jointWeights2{0.0f};
Vec4f jointWeights3{0.0f};
Vec4i jointIndices[(MAX_SKINNING_WEIGHTS - 1) / 4 + 1];
Vec4f jointWeights[(MAX_SKINNING_WEIGHTS - 1) / 4 + 1];
std::vector<RawVertexSkinningInfo> skinningInfo;
// end of members that directly correspond to vertex attributes
@ -381,7 +372,7 @@ struct RawNode {
class RawModel {
public:
static const int MAX_SUPPORTED_WEIGHTS = 16;
static const int MAX_SUPPORTED_WEIGHTS = MAX_SKINNING_WEIGHTS;
RawModel();
@ -460,6 +451,11 @@ class RawModel {
int GetVertexCount() const {
return (int)vertices.size();
}
int GetGlobalWeightCount() const{
return globalMaxWeights;
}
const RawVertex& GetVertex(const int index) const {
return vertices[index];
}
@ -542,6 +538,14 @@ class RawModel {
void GetAttributeArray(std::vector<_attrib_type_>& out, const _attrib_type_ RawVertex::*ptr)
const;
// Create individual attribute arrays, with the source as an array.
// Returns true if the vertices store the particular attribute.
template <typename _attrib_type_>
void GetArrayAttributeArray(std::vector<_attrib_type_>& out,
const _attrib_type_ (RawVertex::*ptr)[(RawModel::MAX_SUPPORTED_WEIGHTS - 1) / 4 + 1],
const int arrayOffset)
const;
// Create an array with a raw model for each material.
// Multiple surfaces with the same material will turn into a single model.
// However, surfaces that are marked as 'discrete' will turn into separate models.
@ -556,6 +560,7 @@ class RawModel {
long rootNodeId;
int vertexAttributes;
int globalMaxWeights;
std::unordered_map<RawVertex, int, VertexHasher> vertexHash;
std::vector<RawVertex> vertices;
std::vector<RawTriangle> triangles;
@ -577,3 +582,14 @@ void RawModel::GetAttributeArray(
out[i] = vertices[i].*ptr;
}
}
template <typename _attrib_type_>
void RawModel::GetArrayAttributeArray(
std::vector<_attrib_type_>& out,
const _attrib_type_ (RawVertex::*ptr)[(RawModel::MAX_SUPPORTED_WEIGHTS - 1) / 4 + 1],
const int arrayOffset) const {
out.resize(vertices.size());
for (size_t i = 0; i < vertices.size(); i++) {
out[i] = (vertices[i].*ptr)[arrayOffset];
}
}