mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-13 08:09:39 +00:00
742 lines
23 KiB
C++
742 lines
23 KiB
C++
/****************************************************************************
|
|
* MeshLab o o *
|
|
* A versatile mesh processing toolbox o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2005-2021 \/)\/ *
|
|
* Visual Computing Lab /\/| *
|
|
* ISTI - Italian National Research Council | *
|
|
* \ *
|
|
* All rights reserved. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program 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 General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
|
* for more details. *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
// From tinygltf example:
|
|
// Define these only in *one* .cc file.
|
|
#define TINYGLTF_IMPLEMENTATION
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
|
|
#include "gltf_loader.h"
|
|
|
|
#include <regex>
|
|
#include <common/mlexception.h>
|
|
|
|
namespace gltf {
|
|
|
|
/**
|
|
* @brief returns the number of meshes referred by the nodes contained in the
|
|
* gltf file.
|
|
*
|
|
* Note: this number may differ from model.meshes.size().
|
|
* This is because some gltf file may duplicate a mesh in the scene, and place
|
|
* it in different positions using the node hierarchy.
|
|
*
|
|
* This function actually returns how many (referenced) nodes contain a mesh.
|
|
*
|
|
* @param model: the tinygltf content of the file
|
|
* @return
|
|
*/
|
|
unsigned int getNumberMeshes(
|
|
const tinygltf::Model& model)
|
|
{
|
|
unsigned int nMeshes = 0;
|
|
for (unsigned int s = 0; s < model.scenes.size(); ++s){
|
|
const tinygltf::Scene& scene = model.scenes[s];
|
|
for (unsigned int n = 0; n < scene.nodes.size(); ++n){
|
|
nMeshes += internal::getNumberMeshes(model, scene.nodes[n]);
|
|
}
|
|
}
|
|
return nMeshes;
|
|
}
|
|
|
|
/**
|
|
* @brief Loads all the meshes referred in the scene of the gltf file into
|
|
* the list of meshes.
|
|
*
|
|
* @param meshModelList
|
|
* @param maskList
|
|
* @param model
|
|
*/
|
|
void loadMeshes(
|
|
const std::list<MeshModel*>& meshModelList,
|
|
std::list<int>& maskList,
|
|
const tinygltf::Model& model,
|
|
bool loadInSingleLayer,
|
|
vcg::CallBackPos* cb)
|
|
{
|
|
CallBackProgress progress(100.0/meshModelList.size());
|
|
maskList.resize(meshModelList.size(), 0);
|
|
std::list<MeshModel*>::const_iterator meshit = meshModelList.begin();
|
|
std::list<int>::iterator maskit = maskList.begin();
|
|
for (unsigned int s = 0; s < model.scenes.size(); ++s){
|
|
const tinygltf::Scene& scene = model.scenes[s];
|
|
for (unsigned int n = 0; n < scene.nodes.size(); ++n){
|
|
internal::loadMeshesWhileTraversingNodes(
|
|
model,
|
|
meshit,
|
|
maskit,
|
|
Matrix44m::Identity(),
|
|
scene.nodes[n],
|
|
loadInSingleLayer,
|
|
cb,
|
|
progress);
|
|
}
|
|
}
|
|
if (cb)
|
|
cb(100, "GLTF File loaded");
|
|
}
|
|
|
|
namespace internal {
|
|
|
|
/**
|
|
* @brief Recursive function that returns the number of meshes contained
|
|
* in the current node (0 or 1) plus the number of meshes contained in the
|
|
* children of the node.
|
|
*
|
|
* Call this function from a root node to know how many meshes are referred
|
|
* in the scene.
|
|
*
|
|
* @param model
|
|
* @param node
|
|
* @return
|
|
*/
|
|
unsigned int getNumberMeshes(
|
|
const tinygltf::Model& model,
|
|
unsigned int node)
|
|
{
|
|
unsigned int nMeshes = 0;
|
|
if (model.nodes[node].mesh >= 0){
|
|
nMeshes = 1;
|
|
}
|
|
for (int c : model.nodes[node].children){
|
|
if (c>=0){
|
|
nMeshes += getNumberMeshes(model, c);
|
|
}
|
|
}
|
|
return nMeshes;
|
|
}
|
|
|
|
/**
|
|
* @brief Recursive function that loads a mesh if the current node contains one,
|
|
* and then calls itself on the children of the node.
|
|
*
|
|
* @param model
|
|
* @param currentMesh
|
|
* @param currentMask
|
|
* @param currentMatrix
|
|
* @param currentNode
|
|
*/
|
|
void loadMeshesWhileTraversingNodes(
|
|
const tinygltf::Model& model,
|
|
std::list<MeshModel*>::const_iterator& currentMesh,
|
|
std::list<int>::iterator& currentMask,
|
|
Matrix44m currentMatrix,
|
|
unsigned int currentNode,
|
|
bool loadInSingleLayer,
|
|
vcg::CallBackPos* cb,
|
|
CallBackProgress& progress)
|
|
{
|
|
currentMatrix = currentMatrix * getCurrentNodeTrMatrix(model, currentNode);
|
|
if (model.nodes[currentNode].mesh >= 0) {
|
|
|
|
int meshid = model.nodes[currentNode].mesh;
|
|
loadMesh(
|
|
**currentMesh,
|
|
*currentMask,
|
|
model.meshes[meshid],
|
|
model,
|
|
loadInSingleLayer,
|
|
currentMatrix,
|
|
cb,
|
|
progress);
|
|
if (!loadInSingleLayer) {
|
|
(*currentMesh)->cm.Tr = currentMatrix;
|
|
++currentMesh;
|
|
++currentMask;
|
|
}
|
|
}
|
|
|
|
//for each child
|
|
for (int c : model.nodes[currentNode].children){
|
|
if (c>=0){ //if it is valid
|
|
//visit child
|
|
loadMeshesWhileTraversingNodes(
|
|
model,
|
|
currentMesh,
|
|
currentMask,
|
|
currentMatrix,
|
|
c,
|
|
loadInSingleLayer,
|
|
cb,
|
|
progress);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the 4x4 transformation matrix contained in the node itself,
|
|
* without taking into account parent transformations of the node.
|
|
*
|
|
* @param model
|
|
* @param currentNode
|
|
* @return
|
|
*/
|
|
Matrix44m getCurrentNodeTrMatrix(
|
|
const tinygltf::Model& model,
|
|
unsigned int currentNode)
|
|
{
|
|
Matrix44m currentMatrix = Matrix44m::Identity();
|
|
//if the current node contains a 4x4 matrix
|
|
if (model.nodes[currentNode].matrix.size() == 16) {
|
|
vcg::Matrix44d curr(model.nodes[currentNode].matrix.data());
|
|
curr.transposeInPlace();
|
|
currentMatrix = Matrix44m::Construct(curr);
|
|
}
|
|
//if the current node contains rotation quaternion, scale vector or
|
|
// translation vector
|
|
else {
|
|
//note: if one or more of these are missing, identity is used.
|
|
//note: final matrix is computed as M = T * R * S, as specified by
|
|
//gltf docs: https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md
|
|
//4x4 matrices associated to rotation, translation and scale
|
|
vcg::Matrix44d rot; rot.SetIdentity();
|
|
vcg::Matrix44d scale; scale.SetIdentity();
|
|
vcg::Matrix44d trans; trans.SetIdentity();
|
|
|
|
//if node contains rotation quaternion
|
|
if (model.nodes[currentNode].rotation.size() == 4) {
|
|
vcg::Quaterniond qr(
|
|
model.nodes[currentNode].rotation[3],
|
|
model.nodes[currentNode].rotation[0],
|
|
model.nodes[currentNode].rotation[1],
|
|
model.nodes[currentNode].rotation[2]);
|
|
qr.ToMatrix(rot); //set 4x4 matrix quaternion
|
|
}
|
|
//if node contains scale
|
|
if (model.nodes[currentNode].scale.size() == 3) {
|
|
//set 4x4 matrix scale
|
|
scale.ElementAt(0,0) = model.nodes[currentNode].scale[0];
|
|
scale.ElementAt(1,1) = model.nodes[currentNode].scale[1];
|
|
scale.ElementAt(2,2) = model.nodes[currentNode].scale[2];
|
|
}
|
|
//if node contains translation
|
|
if (model.nodes[currentNode].translation.size() == 3) {
|
|
//set 4x4 matrix translation
|
|
trans.ElementAt(0,3) = model.nodes[currentNode].translation[0];
|
|
trans.ElementAt(1,3) = model.nodes[currentNode].translation[1];
|
|
trans.ElementAt(2,3) = model.nodes[currentNode].translation[2];
|
|
}
|
|
|
|
//M = T * R * S
|
|
vcg::Matrix44d curr = trans * rot * scale;
|
|
currentMatrix = Matrix44m::Construct(curr);
|
|
}
|
|
return currentMatrix;
|
|
}
|
|
|
|
/**
|
|
* @brief loads a mesh from gltf file.
|
|
* It merges all the primitives in the loaded mesh.
|
|
*
|
|
* @param m: the mesh that will contain the loaded mesh
|
|
* @param tm: tinygltf structure of the mesh to load
|
|
* @param model: tinygltf file
|
|
*/
|
|
void loadMesh(
|
|
MeshModel& m,
|
|
int& mask,
|
|
const tinygltf::Mesh& tm,
|
|
const tinygltf::Model& model,
|
|
bool loadInSingleLayer,
|
|
const Matrix44m& transf,
|
|
vcg::CallBackPos* cb,
|
|
CallBackProgress& progress)
|
|
{
|
|
if (!tm.name.empty())
|
|
m.setLabel(QString::fromStdString(tm.name));
|
|
|
|
double oldStep = progress.step();
|
|
progress.setStep(oldStep / tm.primitives.size());
|
|
|
|
//for each primitive, load it into the mesh
|
|
for (const tinygltf::Primitive& p : tm.primitives){
|
|
internal::loadMeshPrimitive(
|
|
m,
|
|
mask,
|
|
model,
|
|
p,
|
|
loadInSingleLayer,
|
|
transf,
|
|
cb,
|
|
progress);
|
|
}
|
|
if (cb)
|
|
cb(progress.progress(), "Loaded all primitives for current mesh.");
|
|
progress.setStep(oldStep);
|
|
}
|
|
|
|
/**
|
|
* @brief loads the given primitive into the mesh
|
|
* @param m
|
|
* @param model
|
|
* @param p
|
|
*/
|
|
void loadMeshPrimitive(
|
|
MeshModel& m,
|
|
int& mask,
|
|
const tinygltf::Model& model,
|
|
const tinygltf::Primitive& p,
|
|
bool loadInSingleLayer,
|
|
const Matrix44m& transf,
|
|
vcg::CallBackPos* cb,
|
|
CallBackProgress& progress)
|
|
{
|
|
double oldStep = progress.step();
|
|
progress.setStep(oldStep/GLTF_ATTR_STR.size()+1);
|
|
|
|
int textureImg = -1; //id of the texture associated to the material
|
|
bool vCol = false; // used if a material has a base color for all the primitive
|
|
bool vTex = false; // used if a material has a texture
|
|
vcg::Color4b col; //the base color, to be set to all the vertices
|
|
|
|
if (p.material >= 0) { //if the primitive has a material
|
|
const tinygltf::Material& mat = model.materials[p.material];
|
|
auto it = mat.values.find("baseColorTexture");
|
|
if (it != mat.values.end()){ //the material is a texture
|
|
auto it2 = it->second.json_double_value.find("index");
|
|
if (it2 != it->second.json_double_value.end()){
|
|
textureImg = it2->second; //get the id of the texture
|
|
}
|
|
}
|
|
it = mat.values.find("baseColorFactor");
|
|
if (it != mat.values.end()) { //vertex base color, the same for a primitive
|
|
vCol = true;
|
|
const std::vector<double>& vc = it->second.number_array;
|
|
for (unsigned int i = 0; i < 4; i++)
|
|
col[i] = vc[i] * 255.0;
|
|
}
|
|
}
|
|
if (textureImg != -1) { //if we found a texture
|
|
vTex = true;
|
|
const tinygltf::Image& img = model.images[model.textures[textureImg].source];
|
|
//add the path of the texture to the mesh
|
|
std::string uri = img.uri;
|
|
uri = std::regex_replace(uri, std::regex("\\%20"), " ");
|
|
|
|
bool textureAlreadyExists = false;
|
|
if (img.image.size() > 0) {
|
|
if (img.bits == 8 || img.component == 4) {
|
|
QImage qimg(img.image.data(), img.width, img.height, QImage::Format_RGBA8888);
|
|
if (!qimg.isNull()){
|
|
QImage copy = qimg.copy();
|
|
if (uri.empty()) {
|
|
uri = "texture_" + std::to_string(textureImg);
|
|
}
|
|
|
|
QImage existingTexture = m.getTexture(uri);
|
|
if (existingTexture.isNull()) {
|
|
m.addTexture(uri, copy);
|
|
}
|
|
else {
|
|
//use the existing texture index
|
|
auto it = std::find(m.cm.textures.begin(), m.cm.textures.end(), uri);
|
|
textureAlreadyExists = it != m.cm.textures.end();
|
|
if (textureAlreadyExists) {
|
|
textureImg = it - m.cm.textures.begin();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
m.cm.textures.push_back(uri);
|
|
}
|
|
}
|
|
else {
|
|
m.cm.textures.push_back(uri);
|
|
}
|
|
}
|
|
else {
|
|
//set to load later (could be format non-supported by tinygltf)
|
|
m.cm.textures.push_back(uri);
|
|
}
|
|
|
|
if (!textureAlreadyExists)
|
|
{
|
|
//set the id of the texture: we need it when set uv coords
|
|
textureImg = m.cm.textures.size() - 1;
|
|
}
|
|
}
|
|
//vector of vertex pointers added to the mesh
|
|
//this vector is modified only when adding vertex position attributes
|
|
std::vector<CMeshO::VertexPointer> ivp;
|
|
|
|
//load vertex position attribute, sets also the ivp vector
|
|
if (cb)
|
|
cb(progress.progress(), "Loading vertex coordinates");
|
|
loadAttribute(m, ivp, model, p, POSITION);
|
|
progress.increment();
|
|
|
|
//if all the meshes are loaded in a single layer, I need to apply
|
|
//the transformation matrix to the loaded coordinates
|
|
if (loadInSingleLayer){
|
|
for (CMeshO::VertexPointer p : ivp){
|
|
p->P() = transf * p->P();
|
|
}
|
|
}
|
|
|
|
//if the mesh has a base color, set it to vertex colors
|
|
if (vCol) {
|
|
mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
|
|
m.updateDataMask(MeshModel::MM_VERTCOLOR);
|
|
for (auto v : ivp)
|
|
v->C() = col;
|
|
}
|
|
|
|
// if the mesh has a texture or texcoords, enable texcoords to the mesh
|
|
if (vTex || p.attributes.find(GLTF_ATTR_STR[TEXCOORD_0]) != p.attributes.end()) {
|
|
m.updateDataMask(MeshModel::MM_VERTTEXCOORD);
|
|
m.updateDataMask(MeshModel::MM_WEDGTEXCOORD);
|
|
}
|
|
|
|
if (cb)
|
|
cb(progress.progress(), "Loading vertex normals");
|
|
//load all the other vertex attributes (ivp is not modified by these calls)
|
|
bool res = loadAttribute(m, ivp, model, p, NORMAL);
|
|
if (res) {
|
|
mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;
|
|
//if all the meshes are loaded in a single layer, I need to apply
|
|
//the transformation matrix to the loaded coordinates
|
|
if (loadInSingleLayer){
|
|
Matrix33m mat33(transf,3);
|
|
for (CMeshO::VertexPointer p : ivp){
|
|
p->N() = mat33 * p->N();
|
|
}
|
|
}
|
|
}
|
|
progress.increment();
|
|
|
|
if (cb)
|
|
cb(progress.progress(), "Loading vertex colors");
|
|
res = loadAttribute(m, ivp, model, p, COLOR_0);
|
|
if (res) {
|
|
mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
|
|
}
|
|
progress.increment();
|
|
if (cb)
|
|
cb(progress.progress(), "Loading vertex texcoords");
|
|
res = loadAttribute(m, ivp, model, p, TEXCOORD_0, textureImg);
|
|
if (res) {
|
|
mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
|
|
}
|
|
progress.increment();
|
|
|
|
|
|
//load triangles
|
|
if (cb)
|
|
cb(progress.progress(), "Loading triangle indices");
|
|
loadAttribute(m, ivp, model, p, INDICES);
|
|
progress.increment();
|
|
|
|
// if vTex was true, it means that we loaded texcoords, that have been transferred from vertex to
|
|
// wedges when loading triangle indices. Therefore, we can remove vertex texcoords and leave
|
|
// only wedges, which are the only that can be rendered with multiple textures in meshlab
|
|
// TODO: remove this mechanism whenever vertex texcoords allow to render multiple textures in
|
|
// vcg.
|
|
if (vTex) {
|
|
m.clearDataMask(MeshModel::MM_VERTTEXCOORD);
|
|
}
|
|
|
|
if (cb)
|
|
cb(progress.progress(), "Loaded all attributes of current mesh");
|
|
|
|
progress.setStep(oldStep);
|
|
}
|
|
|
|
/**
|
|
* @brief loads the attribute attr from the primitive p contained in the
|
|
* gltf model. If the attribute is vertex position, sets also vertex pointers
|
|
* vector ivp. For all the other parameters, ivp is a const input.
|
|
*
|
|
* If the primitive does not contain the attribute attr, nothing is done.
|
|
* However, if the attribute is POSITION, then a MLException will be thrown.
|
|
*
|
|
*
|
|
* @param m
|
|
* @param ivp
|
|
* @param model
|
|
* @param p
|
|
* @param attr
|
|
* @param textID: id of the texture in case of the attr is TEXCOORD_0
|
|
* @return true if the attribute has been loaded
|
|
*/
|
|
bool loadAttribute(
|
|
MeshModel& m,
|
|
std::vector<CMeshO::VertexPointer>& ivp,
|
|
const tinygltf::Model& model,
|
|
const tinygltf::Primitive& p,
|
|
GLTF_ATTR_TYPE attr,
|
|
int textID)
|
|
{
|
|
bool attrLoaded = false;
|
|
const tinygltf::Accessor* accessor = nullptr;
|
|
|
|
//get the accessor associated to the attribute
|
|
if (attr != INDICES) {
|
|
auto it = p.attributes.find(GLTF_ATTR_STR[attr]);
|
|
|
|
if (it != p.attributes.end()) { //accessor found
|
|
accessor = &model.accessors[it->second];
|
|
}
|
|
else if (attr == POSITION) { //if we were looking for POSITION and didn't find any
|
|
throw MLException("File has not 'Position' attribute");
|
|
}
|
|
}
|
|
else { //if the attribute is triangle indices
|
|
|
|
//if the mode is GL_TRIANGLES and we have triangle indices
|
|
if (p.mode == 4 && p.indices >= 0 &&
|
|
(unsigned int)p.indices < model.accessors.size()) {
|
|
accessor = &model.accessors[p.indices];
|
|
}
|
|
}
|
|
|
|
//if we found an accessor of the attribute
|
|
if (accessor) {
|
|
//bufferview: contains infos on how to access buffer with the accessor
|
|
const tinygltf::BufferView& posbw = model.bufferViews[accessor->bufferView];
|
|
|
|
//data of the whole buffer (vector of bytes);
|
|
//may contain also other data not associated to our attribute
|
|
const std::vector<unsigned char>& posdata = model.buffers[posbw.buffer].data;
|
|
|
|
//offset where the data of the attribute starts
|
|
unsigned int posOffset = posbw.byteOffset + accessor->byteOffset;
|
|
//hack:
|
|
//if the attribute is a color, textid is used to tell the size of the
|
|
//color (3 or 4 components)
|
|
if (attr == COLOR_0){
|
|
if (accessor->type == TINYGLTF_TYPE_VEC3)
|
|
textID = 3;
|
|
else if (accessor->type == TINYGLTF_TYPE_VEC4)
|
|
textID = 4;
|
|
}
|
|
|
|
const unsigned int elementSize =
|
|
tinygltf::GetNumComponentsInType(accessor->type) *
|
|
tinygltf::GetComponentSizeInBytes(accessor->componentType);
|
|
const unsigned int stride =
|
|
(posbw.byteStride > elementSize) ? posbw.byteStride : elementSize;
|
|
|
|
//if data is float
|
|
if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
|
//get the starting point of the data as float pointer
|
|
const float* posArray = (const float*) (posdata.data() + posOffset);
|
|
populateAttr(attr, m, ivp, posArray, stride, accessor->count, textID);
|
|
attrLoaded = true;
|
|
}
|
|
//if data is double
|
|
else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
|
|
//get the starting point of the data as double pointer
|
|
const double* posArray = (const double*) (posdata.data() + posOffset);
|
|
populateAttr(attr, m, ivp, posArray, stride, accessor->count, textID);
|
|
attrLoaded = true;
|
|
}
|
|
//if data is ubyte
|
|
else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
|
//get the starting point of the data as uchar pointer
|
|
const unsigned char* triArray = (const unsigned char*) (posdata.data() + posOffset);
|
|
populateAttr(attr, m, ivp, triArray, stride, accessor->count, textID);
|
|
attrLoaded = true;
|
|
}
|
|
//if data is ushort
|
|
else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
|
//get the starting point of the data as ushort pointer
|
|
const unsigned short* triArray = (const unsigned short*) (posdata.data() + posOffset);
|
|
populateAttr(attr, m, ivp, triArray, stride, accessor->count, textID);
|
|
attrLoaded = true;
|
|
}
|
|
//if data is uint
|
|
else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
|
|
//get the starting point of the data as uint pointer
|
|
const unsigned int* triArray = (const unsigned int*) (posdata.data() + posOffset);
|
|
populateAttr(attr, m, ivp, triArray, stride, accessor->count, textID);
|
|
attrLoaded = true;
|
|
}
|
|
}
|
|
//if accessor not found and attribute is indices, it means that
|
|
//the mesh is not indexed, and triplets of contiguous vertices
|
|
//generate triangles
|
|
else if (attr == INDICES) {
|
|
//this case is managed when passing nullptr as data
|
|
populateAttr<unsigned char>(attr, m, ivp, nullptr, 0, 0);
|
|
attrLoaded = true;
|
|
}
|
|
return attrLoaded;
|
|
}
|
|
|
|
/**
|
|
* @brief given the attribute and the pointer to the data,
|
|
* it calls the appropriate functions that put the data into the mesh
|
|
* appropriately
|
|
* @param attr
|
|
* @param m
|
|
* @param ivp: modified only if attr
|
|
* @param array: vector containing the data
|
|
* @param stride:
|
|
* number of bytes between consecutive elements in the vector
|
|
* (only applies to vertex attributes; indices are always tightly packed)
|
|
* @param number: number of elements contained in the data
|
|
* @param textID:
|
|
* if attr is texcoord, it is the texture id
|
|
* if attr is color, tells if color has 3 or 4 components
|
|
*/
|
|
template <typename Scalar>
|
|
void populateAttr(
|
|
GLTF_ATTR_TYPE attr,
|
|
MeshModel&m,
|
|
std::vector<CMeshO::VertexPointer>& ivp,
|
|
const Scalar* array,
|
|
unsigned int stride,
|
|
unsigned int number,
|
|
int textID)
|
|
{
|
|
switch (attr) {
|
|
case POSITION:
|
|
populateVertices(m, ivp, array, stride, number); break;
|
|
case NORMAL:
|
|
populateVNormals(ivp, array, stride, number); break;
|
|
case COLOR_0:
|
|
populateVColors(ivp, array, stride, number, textID); break;
|
|
case TEXCOORD_0:
|
|
populateVTextCoords(ivp, array, stride, number, textID); break;
|
|
case INDICES:
|
|
populateTriangles(m, ivp, array, number/3); break;
|
|
}
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void populateVertices(
|
|
MeshModel&m,
|
|
std::vector<CMeshO::VertexPointer>& ivp,
|
|
const Scalar* posArray,
|
|
unsigned int stride,
|
|
unsigned int vertNumber)
|
|
{
|
|
ivp.clear();
|
|
ivp.resize(vertNumber);
|
|
CMeshO::VertexIterator vi =
|
|
vcg::tri::Allocator<CMeshO>::AddVertices(m.cm, vertNumber);
|
|
for (unsigned int i = 0; i < vertNumber*3; i+= 3, ++vi){
|
|
ivp[i/3] = &*vi;
|
|
const Scalar* posBase =
|
|
reinterpret_cast<const Scalar*>(reinterpret_cast<const char*>(posArray) + (i/3) * stride);
|
|
vi->P() = CMeshO::CoordType(posBase[0], posBase[1], posBase[2]);
|
|
}
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void populateVNormals(
|
|
const std::vector<CMeshO::VertexPointer>& ivp,
|
|
const Scalar* normArray,
|
|
unsigned int stride,
|
|
unsigned int vertNumber)
|
|
{
|
|
for (unsigned int i = 0; i < vertNumber*3; i+= 3) {
|
|
const Scalar* normBase =
|
|
reinterpret_cast<const Scalar*>(reinterpret_cast<const char*>(normArray) + (i/3) * stride);
|
|
ivp[i/3]->N() = CMeshO::CoordType(normBase[0], normBase[1], normBase[2]);
|
|
}
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void populateVColors(
|
|
const std::vector<CMeshO::VertexPointer>& ivp,
|
|
const Scalar* colorArray,
|
|
unsigned int stride,
|
|
unsigned int vertNumber,
|
|
int nElemns)
|
|
{
|
|
for (unsigned int i = 0; i < vertNumber*nElemns; i+= nElemns) {
|
|
const Scalar* colorBase =
|
|
reinterpret_cast<const Scalar*>(reinterpret_cast<const char*>(colorArray) + (i/nElemns) * stride);
|
|
if (!std::is_floating_point<Scalar>::value) {
|
|
int alpha = nElemns == 4 ? colorBase[3] : 255;
|
|
ivp[i/nElemns]->C() = vcg::Color4b(colorBase[0], colorBase[1], colorBase[2], alpha);
|
|
}
|
|
else {
|
|
int alpha = nElemns == 4 ? colorBase[3] * 255 : 255;
|
|
ivp[i/nElemns]->C() = vcg::Color4b(colorBase[0] * 255, colorBase[1]*255, colorBase[2]*255, alpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void populateVTextCoords(
|
|
const std::vector<CMeshO::VertexPointer>& ivp,
|
|
const Scalar* textCoordArray,
|
|
unsigned int stride,
|
|
unsigned int vertNumber,
|
|
int textID)
|
|
{
|
|
for (unsigned int i = 0; i < vertNumber*2; i+= 2) {
|
|
const Scalar* textCoordBase =
|
|
reinterpret_cast<const Scalar*>(reinterpret_cast<const char*>(textCoordArray) + (i/2) * stride);
|
|
ivp[i/2]->T() = CMeshO::VertexType::TexCoordType(textCoordBase[0], 1-textCoordBase[1]);
|
|
ivp[i/2]->T().N() = textID;
|
|
}
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void populateTriangles(
|
|
MeshModel&m,
|
|
const std::vector<CMeshO::VertexPointer>& ivp,
|
|
const Scalar* triArray,
|
|
unsigned int triNumber)
|
|
{
|
|
const bool hasTexcoord = m.hasPerVertexTexCoord();
|
|
|
|
if (triArray != nullptr) {
|
|
CMeshO::FaceIterator fi =
|
|
vcg::tri::Allocator<CMeshO>::AddFaces(m.cm, triNumber);
|
|
for (unsigned int i = 0; i < triNumber*3; i+=3, ++fi) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
fi->V(j) = ivp[triArray[i+j]];
|
|
|
|
if (hasTexcoord) {
|
|
fi->WT(j).u() = fi->V(j)->T().u();
|
|
fi->WT(j).v() = fi->V(j)->T().v();
|
|
fi->WT(j).n() = fi->V(j)->T().N();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
CMeshO::FaceIterator fi =
|
|
vcg::tri::Allocator<CMeshO>::AddFaces(m.cm, ivp.size()/3);
|
|
for (unsigned int i = 0; i < ivp.size(); i+=3, ++fi) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
fi->V(j) = ivp[i+j];
|
|
|
|
if (hasTexcoord) {
|
|
fi->WT(j).u() = fi->V(j)->T().u();
|
|
fi->WT(j).v() = fi->V(j)->T().v();
|
|
fi->WT(j).n() = fi->V(j)->T().N();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} //namespace gltf::internal
|
|
} //namespace gltf
|