Merge branch 'master' into 3dsMax2021PbrMaterials
This commit is contained in:
@@ -9,8 +9,10 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
|
||||
const int CHARS_PER_LINE = 72;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#endif // _MSC_VER
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
@@ -33,9 +35,9 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
@@ -74,7 +76,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4)
|
||||
{
|
||||
@@ -90,7 +92,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
@@ -106,8 +108,10 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
break;
|
||||
}
|
||||
*codechar++ = '\n';
|
||||
|
||||
|
||||
return (int)(codechar - code_out);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif // _MSC_VER
|
||||
|
||||
@@ -185,6 +185,17 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
|
||||
return unique_name;
|
||||
}
|
||||
|
||||
/// This struct manages nodes which may or may not end up in the node hierarchy.
|
||||
/// When a node becomes a child of another node, that node becomes its owner and mOwnership should be released.
|
||||
struct FBXConverter::PotentialNode
|
||||
{
|
||||
PotentialNode() : mOwnership(new aiNode), mNode(mOwnership.get()) {}
|
||||
PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {}
|
||||
aiNode* operator->() { return mNode; }
|
||||
std::unique_ptr<aiNode> mOwnership;
|
||||
aiNode* mNode;
|
||||
};
|
||||
|
||||
/// todo: pre-build node hierarchy
|
||||
/// todo: get bone from stack
|
||||
/// todo: make map of aiBone* to aiNode*
|
||||
@@ -192,137 +203,129 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
|
||||
void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
|
||||
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
|
||||
|
||||
std::vector<aiNode *> nodes;
|
||||
std::vector<PotentialNode> nodes;
|
||||
nodes.reserve(conns.size());
|
||||
|
||||
std::vector<aiNode *> nodes_chain;
|
||||
std::vector<aiNode *> post_nodes_chain;
|
||||
std::vector<PotentialNode> nodes_chain;
|
||||
std::vector<PotentialNode> post_nodes_chain;
|
||||
|
||||
try {
|
||||
for (const Connection *con : conns) {
|
||||
// ignore object-property links
|
||||
if (con->PropertyName().length()) {
|
||||
// really important we document why this is ignored.
|
||||
FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
|
||||
continue; //?
|
||||
for (const Connection *con : conns) {
|
||||
// ignore object-property links
|
||||
if (con->PropertyName().length()) {
|
||||
// really important we document why this is ignored.
|
||||
FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
|
||||
continue; //?
|
||||
}
|
||||
|
||||
// convert connection source object into Object base class
|
||||
const Object *const object = con->SourceObject();
|
||||
if (nullptr == object) {
|
||||
FBXImporter::LogError("failed to convert source object for Model link");
|
||||
continue;
|
||||
}
|
||||
|
||||
// FBX Model::Cube, Model::Bone001, etc elements
|
||||
// This detects if we can cast the object into this model structure.
|
||||
const Model *const model = dynamic_cast<const Model *>(object);
|
||||
|
||||
if (nullptr != model) {
|
||||
nodes_chain.clear();
|
||||
post_nodes_chain.clear();
|
||||
|
||||
aiMatrix4x4 new_abs_transform = parent->mTransformation;
|
||||
std::string node_name = FixNodeName(model->Name());
|
||||
// even though there is only a single input node, the design of
|
||||
// assimp (or rather: the complicated transformation chain that
|
||||
// is employed by fbx) means that we may need multiple aiNode's
|
||||
// to represent a fbx node's transformation.
|
||||
|
||||
// generate node transforms - this includes pivot data
|
||||
// if need_additional_node is true then you t
|
||||
const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
|
||||
|
||||
// assert that for the current node we must have at least a single transform
|
||||
ai_assert(nodes_chain.size());
|
||||
|
||||
if (need_additional_node) {
|
||||
nodes_chain.emplace_back(PotentialNode(node_name));
|
||||
}
|
||||
|
||||
// convert connection source object into Object base class
|
||||
const Object *const object = con->SourceObject();
|
||||
if (nullptr == object) {
|
||||
FBXImporter::LogError("failed to convert source object for Model link");
|
||||
continue;
|
||||
}
|
||||
//setup metadata on newest node
|
||||
SetupNodeMetadata(*model, *nodes_chain.back().mNode);
|
||||
|
||||
// FBX Model::Cube, Model::Bone001, etc elements
|
||||
// This detects if we can cast the object into this model structure.
|
||||
const Model *const model = dynamic_cast<const Model *>(object);
|
||||
// link all nodes in a row
|
||||
aiNode *last_parent = parent;
|
||||
for (PotentialNode& child : nodes_chain) {
|
||||
ai_assert(child.mNode);
|
||||
|
||||
if (nullptr != model) {
|
||||
nodes_chain.clear();
|
||||
post_nodes_chain.clear();
|
||||
|
||||
aiMatrix4x4 new_abs_transform = parent->mTransformation;
|
||||
std::string node_name = FixNodeName(model->Name());
|
||||
// even though there is only a single input node, the design of
|
||||
// assimp (or rather: the complicated transformation chain that
|
||||
// is employed by fbx) means that we may need multiple aiNode's
|
||||
// to represent a fbx node's transformation.
|
||||
|
||||
// generate node transforms - this includes pivot data
|
||||
// if need_additional_node is true then you t
|
||||
const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
|
||||
|
||||
// assert that for the current node we must have at least a single transform
|
||||
ai_assert(nodes_chain.size());
|
||||
|
||||
if (need_additional_node) {
|
||||
nodes_chain.push_back(new aiNode(node_name));
|
||||
if (last_parent != parent) {
|
||||
last_parent->mNumChildren = 1;
|
||||
last_parent->mChildren = new aiNode *[1];
|
||||
last_parent->mChildren[0] = child.mOwnership.release();
|
||||
}
|
||||
|
||||
//setup metadata on newest node
|
||||
SetupNodeMetadata(*model, *nodes_chain.back());
|
||||
child->mParent = last_parent;
|
||||
last_parent = child.mNode;
|
||||
|
||||
// link all nodes in a row
|
||||
aiNode *last_parent = parent;
|
||||
for (aiNode *child : nodes_chain) {
|
||||
ai_assert(child);
|
||||
new_abs_transform *= child->mTransformation;
|
||||
}
|
||||
|
||||
// attach geometry
|
||||
ConvertModel(*model, nodes_chain.back().mNode, root_node, new_abs_transform);
|
||||
|
||||
// check if there will be any child nodes
|
||||
const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
|
||||
|
||||
// if so, link the geometric transform inverse nodes
|
||||
// before we attach any child nodes
|
||||
if (child_conns.size()) {
|
||||
for (PotentialNode& postnode : post_nodes_chain) {
|
||||
ai_assert(postnode.mNode);
|
||||
|
||||
if (last_parent != parent) {
|
||||
last_parent->mNumChildren = 1;
|
||||
last_parent->mChildren = new aiNode *[1];
|
||||
last_parent->mChildren[0] = child;
|
||||
last_parent->mChildren[0] = postnode.mOwnership.release();
|
||||
}
|
||||
|
||||
child->mParent = last_parent;
|
||||
last_parent = child;
|
||||
postnode->mParent = last_parent;
|
||||
last_parent = postnode.mNode;
|
||||
|
||||
new_abs_transform *= child->mTransformation;
|
||||
new_abs_transform *= postnode->mTransformation;
|
||||
}
|
||||
|
||||
// attach geometry
|
||||
ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform);
|
||||
|
||||
// check if there will be any child nodes
|
||||
const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
|
||||
|
||||
// if so, link the geometric transform inverse nodes
|
||||
// before we attach any child nodes
|
||||
if (child_conns.size()) {
|
||||
for (aiNode *postnode : post_nodes_chain) {
|
||||
ai_assert(postnode);
|
||||
|
||||
if (last_parent != parent) {
|
||||
last_parent->mNumChildren = 1;
|
||||
last_parent->mChildren = new aiNode *[1];
|
||||
last_parent->mChildren[0] = postnode;
|
||||
}
|
||||
|
||||
postnode->mParent = last_parent;
|
||||
last_parent = postnode;
|
||||
|
||||
new_abs_transform *= postnode->mTransformation;
|
||||
}
|
||||
} else {
|
||||
// free the nodes we allocated as we don't need them
|
||||
Util::delete_fun<aiNode> deleter;
|
||||
std::for_each(
|
||||
post_nodes_chain.begin(),
|
||||
post_nodes_chain.end(),
|
||||
deleter);
|
||||
}
|
||||
|
||||
// recursion call - child nodes
|
||||
ConvertNodes(model->ID(), last_parent, root_node);
|
||||
|
||||
if (doc.Settings().readLights) {
|
||||
ConvertLights(*model, node_name);
|
||||
}
|
||||
|
||||
if (doc.Settings().readCameras) {
|
||||
ConvertCameras(*model, node_name);
|
||||
}
|
||||
|
||||
nodes.push_back(nodes_chain.front());
|
||||
nodes_chain.clear();
|
||||
} else {
|
||||
// free the nodes we allocated as we don't need them
|
||||
post_nodes_chain.clear();
|
||||
}
|
||||
|
||||
// recursion call - child nodes
|
||||
ConvertNodes(model->ID(), last_parent, root_node);
|
||||
|
||||
if (doc.Settings().readLights) {
|
||||
ConvertLights(*model, node_name);
|
||||
}
|
||||
|
||||
if (doc.Settings().readCameras) {
|
||||
ConvertCameras(*model, node_name);
|
||||
}
|
||||
|
||||
nodes.push_back(std::move(nodes_chain.front()));
|
||||
nodes_chain.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.size()) {
|
||||
parent->mChildren = new aiNode *[nodes.size()]();
|
||||
parent->mNumChildren = static_cast<unsigned int>(nodes.size());
|
||||
if (nodes.size()) {
|
||||
parent->mChildren = new aiNode *[nodes.size()]();
|
||||
parent->mNumChildren = static_cast<unsigned int>(nodes.size());
|
||||
|
||||
std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren);
|
||||
} else {
|
||||
parent->mNumChildren = 0;
|
||||
parent->mChildren = nullptr;
|
||||
for (unsigned int i = 0; i < nodes.size(); ++i)
|
||||
{
|
||||
parent->mChildren[i] = nodes[i].mOwnership.release();
|
||||
}
|
||||
|
||||
} catch (std::exception &) {
|
||||
Util::delete_fun<aiNode> deleter;
|
||||
std::for_each(nodes.begin(), nodes.end(), deleter);
|
||||
std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter);
|
||||
std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter);
|
||||
nodes.clear();
|
||||
} else {
|
||||
parent->mNumChildren = 0;
|
||||
parent->mChildren = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,8 +684,8 @@ std::string FBXConverter::NameTransformationChainNode(const std::string &name, T
|
||||
return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
|
||||
}
|
||||
|
||||
bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector<aiNode *> &output_nodes,
|
||||
std::vector<aiNode *> &post_output_nodes) {
|
||||
bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector<PotentialNode> &output_nodes,
|
||||
std::vector<PotentialNode> &post_output_nodes) {
|
||||
const PropertyTable &props = model.Props();
|
||||
const Model::RotOrder rot = model.RotationOrder();
|
||||
|
||||
@@ -828,7 +831,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
|
||||
chain[i] = chain[i].Inverse();
|
||||
}
|
||||
|
||||
aiNode *nd = new aiNode();
|
||||
PotentialNode nd;
|
||||
nd->mName.Set(NameTransformationChainNode(name, comp));
|
||||
nd->mTransformation = chain[i];
|
||||
|
||||
@@ -836,9 +839,9 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
|
||||
if (comp == TransformationComp_GeometricScalingInverse ||
|
||||
comp == TransformationComp_GeometricRotationInverse ||
|
||||
comp == TransformationComp_GeometricTranslationInverse) {
|
||||
post_output_nodes.push_back(nd);
|
||||
post_output_nodes.emplace_back(std::move(nd));
|
||||
} else {
|
||||
output_nodes.push_back(nd);
|
||||
output_nodes.emplace_back(std::move(nd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,8 +850,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
|
||||
}
|
||||
|
||||
// else, we can just multiply the matrices together
|
||||
aiNode *nd = new aiNode();
|
||||
output_nodes.push_back(nd);
|
||||
PotentialNode nd;
|
||||
|
||||
// name passed to the method is already unique
|
||||
nd->mName.Set(name);
|
||||
@@ -857,6 +859,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
|
||||
for (unsigned int i = TransformationComp_Translation; i < TransformationComp_MAXIMUM; i++) {
|
||||
nd->mTransformation = nd->mTransformation * chain[i];
|
||||
}
|
||||
output_nodes.push_back(std::move(nd));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,9 +171,10 @@ private:
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* note: memory for output_nodes will be managed by the caller
|
||||
* note: memory for output_nodes is managed by the caller, via the PotentialNode struct.
|
||||
*/
|
||||
bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
|
||||
struct PotentialNode;
|
||||
bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<PotentialNode>& output_nodes, std::vector<PotentialNode>& post_output_nodes);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SetupNodeMetadata(const Model& model, aiNode& nd);
|
||||
|
||||
@@ -49,7 +49,9 @@ using namespace glTFCommon::Util;
|
||||
namespace Util {
|
||||
|
||||
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out) {
|
||||
ai_assert(inLength % 4 == 0);
|
||||
if (inLength % 4 != 0) {
|
||||
throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength);
|
||||
}
|
||||
|
||||
if (inLength < 4) {
|
||||
out = 0;
|
||||
|
||||
@@ -249,7 +249,10 @@ inline char EncodeCharBase64(uint8_t b) {
|
||||
}
|
||||
|
||||
inline uint8_t DecodeCharBase64(char c) {
|
||||
return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
|
||||
if (c & 0x80) {
|
||||
throw DeadlyImportError("Invalid base64 char value: ", size_t(c));
|
||||
}
|
||||
return DATA<true>::tableDecodeBase64[size_t(c & 0x7F)]; // TODO faster with lookup table or ifs?
|
||||
}
|
||||
|
||||
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out);
|
||||
|
||||
@@ -1124,6 +1124,14 @@ private:
|
||||
IOStream *OpenFile(std::string path, const char *mode, bool absolute = false);
|
||||
};
|
||||
|
||||
inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) {
|
||||
std::string context = id;
|
||||
if (!name.empty()) {
|
||||
context += " (\"" + name + "\")";
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
} // namespace glTF2
|
||||
|
||||
// Include the implementation of the methods
|
||||
|
||||
@@ -273,17 +273,21 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
|
||||
}
|
||||
|
||||
if (!mDict->IsArray()) {
|
||||
throw DeadlyImportError("GLTF: Field is not an array \"", mDictId, "\"");
|
||||
throw DeadlyImportError("GLTF: Field \"", mDictId, "\" is not an array");
|
||||
}
|
||||
|
||||
if (i >= mDict->Size()) {
|
||||
throw DeadlyImportError("GLTF: Array index ", i, " is out of bounds (", mDict->Size(), ") for \"", mDictId, "\"");
|
||||
}
|
||||
|
||||
Value &obj = (*mDict)[i];
|
||||
|
||||
if (!obj.IsObject()) {
|
||||
throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" is not a JSON object");
|
||||
throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" is not a JSON object");
|
||||
}
|
||||
|
||||
if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
|
||||
throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" has recursive reference to itself");
|
||||
throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" has recursive reference to itself");
|
||||
}
|
||||
mRecursiveReferenceCheck.insert(i);
|
||||
|
||||
@@ -741,14 +745,6 @@ inline void CopyData(size_t count,
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string getContextForErrorMessages(const std::string& id, const std::string& name) {
|
||||
std::string context = id;
|
||||
if (!name.empty()) {
|
||||
context += " (\"" + name + "\")";
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <class T>
|
||||
@@ -766,11 +762,12 @@ void Accessor::ExtractData(T *&outData) {
|
||||
const size_t targetElemSize = sizeof(T);
|
||||
|
||||
if (elemSize > targetElemSize) {
|
||||
throw DeadlyImportError("GLTF: elemSize > targetElemSize");
|
||||
throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
|
||||
}
|
||||
|
||||
if (count*stride > (bufferView ? bufferView->byteLength : sparse->data.size())) {
|
||||
throw DeadlyImportError("GLTF: count*stride out of range");
|
||||
const size_t maxSize = (bufferView ? bufferView->byteLength : sparse->data.size());
|
||||
if (count*stride > maxSize) {
|
||||
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
|
||||
}
|
||||
|
||||
outData = new T[count];
|
||||
|
||||
@@ -1126,7 +1126,7 @@ void glTF2Exporter::MergeMeshes()
|
||||
unsigned int meshIndex = meshRef.GetIndex();
|
||||
|
||||
if (meshIndex == removedIndex) {
|
||||
node->meshes.erase(curNode->meshes.begin() + mm);
|
||||
curNode->meshes.erase(curNode->meshes.begin() + mm);
|
||||
} else if (meshIndex > removedIndex) {
|
||||
Ref<Mesh> newMeshRef = mAsset->meshes.Get(meshIndex - 1);
|
||||
|
||||
|
||||
@@ -918,7 +918,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
|
||||
|
||||
if (!node.meshes.empty()) {
|
||||
// GLTF files contain at most 1 mesh per node.
|
||||
assert(node.meshes.size() == 1);
|
||||
if (node.meshes.size() > 1)
|
||||
{
|
||||
throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(), " meshes in ", getContextForErrorMessages(node.id, node.name), ", but only 1 mesh per node allowed.");
|
||||
}
|
||||
int mesh_idx = node.meshes[0].GetIndex();
|
||||
int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user