Merge branch 'master' into master
commit
02812f22af
|
@ -34,15 +34,6 @@ namespace Assimp {
|
||||||
|
|
||||||
void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
|
void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
|
||||||
|
|
||||||
Exporter::ExportFormatEntry Assimp2Json_desc = Assimp::Exporter::ExportFormatEntry(
|
|
||||||
"json",
|
|
||||||
"Plain JSON representation of the Assimp scene data structure",
|
|
||||||
"json",
|
|
||||||
&ExportAssimp2Json,
|
|
||||||
0u
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// small utility class to simplify serializing the aiScene to Json
|
// small utility class to simplify serializing the aiScene to Json
|
||||||
class JSONWriter {
|
class JSONWriter {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -810,7 +810,7 @@ ADD_ASSIMP_IMPORTER( MMD
|
||||||
MMD/MMDVmdParser.h
|
MMD/MMDVmdParser.h
|
||||||
)
|
)
|
||||||
|
|
||||||
ADD_ASSIMP_EXPORTER( Assjson
|
ADD_ASSIMP_EXPORTER( ASSJSON
|
||||||
Assjson/cencode.c
|
Assjson/cencode.c
|
||||||
Assjson/cencode.h
|
Assjson/cencode.h
|
||||||
Assjson/json_exporter.cpp
|
Assjson/json_exporter.cpp
|
||||||
|
|
|
@ -163,11 +163,11 @@ Exporter::ExportFormatEntry gExporters[] =
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
|
||||||
Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ),
|
Exporter::ExportFormatEntry( "assbin", "Assimp Binary File", "assbin" , &ExportSceneAssbin, 0 ),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
|
||||||
Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ),
|
Exporter::ExportFormatEntry( "assxml", "Assimp XML Document", "assxml" , &ExportSceneAssxml, 0 ),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
|
||||||
|
@ -183,8 +183,8 @@ Exporter::ExportFormatEntry gExporters[] =
|
||||||
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ),
|
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_Assjson_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
||||||
Exporter::ExportFormatEntry("json", "Plain JSON representation of the Assimp scene data structure", "json", &ExportAssimp2Json, 0)
|
Exporter::ExportFormatEntry( "assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,6 @@ namespace Assimp {
|
||||||
, anim_fps()
|
, anim_fps()
|
||||||
, out(out)
|
, out(out)
|
||||||
, doc(doc)
|
, doc(doc)
|
||||||
, mRemoveEmptyBones( removeEmptyBones )
|
|
||||||
, mCurrentUnit(FbxUnit::cm) {
|
, mCurrentUnit(FbxUnit::cm) {
|
||||||
// animations need to be converted first since this will
|
// animations need to be converted first since this will
|
||||||
// populate the node_anim_chain_bits map, which is needed
|
// populate the node_anim_chain_bits map, which is needed
|
||||||
|
@ -1462,14 +1461,8 @@ namespace Assimp {
|
||||||
|
|
||||||
const WeightIndexArray& indices = cluster->GetIndices();
|
const WeightIndexArray& indices = cluster->GetIndices();
|
||||||
|
|
||||||
if (indices.empty() && mRemoveEmptyBones ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MatIndexArray& mats = geo.GetMaterialIndices();
|
const MatIndexArray& mats = geo.GetMaterialIndices();
|
||||||
|
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
|
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
count_out_indices.clear();
|
count_out_indices.clear();
|
||||||
|
@ -1509,8 +1502,7 @@ namespace Assimp {
|
||||||
out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
|
out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
|
||||||
}
|
}
|
||||||
|
|
||||||
++count_out_indices.back();
|
++count_out_indices.back();
|
||||||
ok = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1518,10 +1510,8 @@ namespace Assimp {
|
||||||
// if we found at least one, generate the output bones
|
// if we found at least one, generate the output bones
|
||||||
// XXX this could be heavily simplified by collecting the bone
|
// XXX this could be heavily simplified by collecting the bone
|
||||||
// data in a single step.
|
// data in a single step.
|
||||||
if (ok && mRemoveEmptyBones) {
|
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
|
||||||
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
|
|
||||||
count_out_indices, node_global_transform);
|
count_out_indices, node_global_transform);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception&) {
|
catch (std::exception&) {
|
||||||
|
|
|
@ -470,9 +470,6 @@ private:
|
||||||
|
|
||||||
aiScene* const out;
|
aiScene* const out;
|
||||||
const FBX::Document& doc;
|
const FBX::Document& doc;
|
||||||
|
|
||||||
bool mRemoveEmptyBones;
|
|
||||||
|
|
||||||
FbxUnit mCurrentUnit;
|
FbxUnit mCurrentUnit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -90,14 +90,6 @@ const Object* LazyObject::Get(bool dieOnError)
|
||||||
return object.get();
|
return object.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is the root object, we return a dummy since there
|
|
||||||
// is no root object int he fbx file - it is just referenced
|
|
||||||
// with id 0.
|
|
||||||
if(id == 0L) {
|
|
||||||
object.reset(new Object(id, element, "Model::RootNode"));
|
|
||||||
return object.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Token& key = element.KeyToken();
|
const Token& key = element.KeyToken();
|
||||||
const TokenList& tokens = element.Tokens();
|
const TokenList& tokens = element.Tokens();
|
||||||
|
|
||||||
|
|
|
@ -1219,6 +1219,16 @@ void FBXExporter::WriteObjects ()
|
||||||
layer.AddChild(le);
|
layer.AddChild(le);
|
||||||
layer.Dump(outstream, binary, indent);
|
layer.Dump(outstream, binary, indent);
|
||||||
|
|
||||||
|
for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr)
|
||||||
|
{
|
||||||
|
FBX::Node layerExtra("Layer", int32_t(1));
|
||||||
|
layerExtra.AddChild("Version", int32_t(100));
|
||||||
|
FBX::Node leExtra("LayerElement");
|
||||||
|
leExtra.AddChild("Type", "LayerElementUV");
|
||||||
|
leExtra.AddChild("TypedIndex", int32_t(lr));
|
||||||
|
layerExtra.AddChild(leExtra);
|
||||||
|
layerExtra.Dump(outstream, binary, indent);
|
||||||
|
}
|
||||||
// finish the node record
|
// finish the node record
|
||||||
indent = 1;
|
indent = 1;
|
||||||
n.End(outstream, binary, indent, true);
|
n.End(outstream, binary, indent, true);
|
||||||
|
@ -1696,8 +1706,7 @@ void FBXExporter::WriteObjects ()
|
||||||
}
|
}
|
||||||
if (end) { break; }
|
if (end) { break; }
|
||||||
}
|
}
|
||||||
limbnodes.insert(parent);
|
|
||||||
skeleton.insert(parent);
|
|
||||||
// if it was the skeleton root we can finish here
|
// if it was the skeleton root we can finish here
|
||||||
if (end) { break; }
|
if (end) { break; }
|
||||||
}
|
}
|
||||||
|
@ -1838,44 +1847,10 @@ void FBXExporter::WriteObjects ()
|
||||||
inverse_bone_xform.Inverse();
|
inverse_bone_xform.Inverse();
|
||||||
aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
|
aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
|
||||||
|
|
||||||
// this should be the same as the bone's mOffsetMatrix.
|
sdnode.AddChild("Transform", tr);
|
||||||
// if it's not the same, the skeleton isn't in the bind pose.
|
|
||||||
float epsilon = 1e-4f; // some error is to be expected
|
|
||||||
float epsilon_custom = mProperties->GetPropertyFloat("BINDPOSE_EPSILON", -1);
|
|
||||||
if(epsilon_custom > 0)
|
|
||||||
epsilon = epsilon_custom;
|
|
||||||
bool bone_xform_okay = true;
|
|
||||||
if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
|
|
||||||
not_in_bind_pose.insert(b);
|
|
||||||
bone_xform_okay = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have a bone we should use the mOffsetMatrix,
|
|
||||||
// otherwise try to just use the calculated transform.
|
|
||||||
if (b) {
|
|
||||||
sdnode.AddChild("Transform", b->mOffsetMatrix);
|
|
||||||
} else {
|
|
||||||
sdnode.AddChild("Transform", tr);
|
|
||||||
}
|
|
||||||
// note: it doesn't matter if we mix these,
|
|
||||||
// because if they disagree we'll throw an exception later.
|
|
||||||
// it could be that the skeleton is not in the bone pose
|
|
||||||
// but all bones are still defined,
|
|
||||||
// in which case this would use the mOffsetMatrix for everything
|
|
||||||
// and a correct skeleton would still be output.
|
|
||||||
|
|
||||||
// transformlink should be the position of the bone in world space.
|
sdnode.AddChild("TransformLink", bone_xform);
|
||||||
// if the bone is in the bind pose (or nonexistent),
|
|
||||||
// we can just use the matrix we already calculated
|
|
||||||
if (bone_xform_okay) {
|
|
||||||
sdnode.AddChild("TransformLink", bone_xform);
|
|
||||||
// otherwise we can only work it out using the mesh position.
|
|
||||||
} else {
|
|
||||||
aiMatrix4x4 trl = b->mOffsetMatrix;
|
|
||||||
trl.Inverse();
|
|
||||||
trl *= mesh_xform;
|
|
||||||
sdnode.AddChild("TransformLink", trl);
|
|
||||||
}
|
|
||||||
// note: this means we ALWAYS rely on the mesh node transform
|
// note: this means we ALWAYS rely on the mesh node transform
|
||||||
// being unchanged from the time the skeleton was bound.
|
// being unchanged from the time the skeleton was bound.
|
||||||
// there's not really any way around this at the moment.
|
// there's not really any way around this at the moment.
|
||||||
|
|
|
@ -115,7 +115,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
|
||||||
|
|
||||||
if(tempVerts.empty()) {
|
if(tempVerts.empty()) {
|
||||||
FBXImporter::LogWarn("encountered mesh with no vertices");
|
FBXImporter::LogWarn("encountered mesh with no vertices");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> tempFaces;
|
std::vector<int> tempFaces;
|
||||||
|
@ -123,7 +122,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
|
||||||
|
|
||||||
if(tempFaces.empty()) {
|
if(tempFaces.empty()) {
|
||||||
FBXImporter::LogWarn("encountered mesh with no faces");
|
FBXImporter::LogWarn("encountered mesh with no faces");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_vertices.reserve(tempFaces.size());
|
m_vertices.reserve(tempFaces.size());
|
||||||
|
|
|
@ -225,7 +225,7 @@ void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
|
||||||
}
|
}
|
||||||
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
|
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
|
||||||
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
|
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
|
||||||
clrDiffuse = aiColor4D( ai_real(1.0), ai_real(1.0), ai_real(1.0), ai_real(1.0));
|
clrDiffuse = aiColor4D( ai_real(0.05), ai_real(0.05), ai_real(0.05), ai_real(1.0));
|
||||||
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
|
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
|
||||||
|
|
||||||
pScene->mNumMaterials = 1;
|
pScene->mNumMaterials = 1;
|
||||||
|
|
|
@ -80,7 +80,13 @@ const aiImporterDesc X3DImporter::Description = {
|
||||||
//const std::regex X3DImporter::pattern_nws(R"([^, \t\r\n]+)");
|
//const std::regex X3DImporter::pattern_nws(R"([^, \t\r\n]+)");
|
||||||
//const std::regex X3DImporter::pattern_true(R"(^\s*(?:true|1)\s*$)", std::regex::icase);
|
//const std::regex X3DImporter::pattern_true(R"(^\s*(?:true|1)\s*$)", std::regex::icase);
|
||||||
|
|
||||||
struct WordIterator: public std::iterator<std::input_iterator_tag, const char*> {
|
struct WordIterator {
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using value_type = const char*;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using reference = value_type&;
|
||||||
|
|
||||||
static const char *whitespace;
|
static const char *whitespace;
|
||||||
const char *start_, *end_;
|
const char *start_, *end_;
|
||||||
WordIterator(const char *start, const char *end): start_(start), end_(end) {
|
WordIterator(const char *start, const char *end): start_(start), end_(end) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* glTF Extensions Support:
|
* glTF Extensions Support:
|
||||||
* KHR_materials_pbrSpecularGlossiness full
|
* KHR_materials_pbrSpecularGlossiness full
|
||||||
* KHR_materials_unlit full
|
* KHR_materials_unlit full
|
||||||
|
* KHR_lights_punctual full
|
||||||
*/
|
*/
|
||||||
#ifndef GLTF2ASSET_H_INC
|
#ifndef GLTF2ASSET_H_INC
|
||||||
#define GLTF2ASSET_H_INC
|
#define GLTF2ASSET_H_INC
|
||||||
|
@ -668,6 +669,28 @@ namespace glTF2
|
||||||
void Read(Value& obj, Asset& r);
|
void Read(Value& obj, Asset& r);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! A light (from KHR_lights_punctual extension)
|
||||||
|
struct Light : public Object
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
Directional,
|
||||||
|
Point,
|
||||||
|
Spot
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
Nullable<float> range;
|
||||||
|
|
||||||
|
float innerConeAngle;
|
||||||
|
float outerConeAngle;
|
||||||
|
|
||||||
|
Light() {}
|
||||||
|
void Read(Value& obj, Asset& r);
|
||||||
|
};
|
||||||
|
|
||||||
//! Image data used to create a texture.
|
//! Image data used to create a texture.
|
||||||
struct Image : public Object
|
struct Image : public Object
|
||||||
|
@ -819,6 +842,7 @@ namespace glTF2
|
||||||
Nullable<vec3> scale;
|
Nullable<vec3> scale;
|
||||||
|
|
||||||
Ref<Camera> camera;
|
Ref<Camera> camera;
|
||||||
|
Ref<Light> light;
|
||||||
|
|
||||||
std::vector< Ref<Node> > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy.
|
std::vector< Ref<Node> > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy.
|
||||||
Ref<Skin> skin; //!< The ID of the skin referenced by this node.
|
Ref<Skin> skin; //!< The ID of the skin referenced by this node.
|
||||||
|
@ -1050,6 +1074,7 @@ namespace glTF2
|
||||||
{
|
{
|
||||||
bool KHR_materials_pbrSpecularGlossiness;
|
bool KHR_materials_pbrSpecularGlossiness;
|
||||||
bool KHR_materials_unlit;
|
bool KHR_materials_unlit;
|
||||||
|
bool KHR_lights_punctual;
|
||||||
|
|
||||||
} extensionsUsed;
|
} extensionsUsed;
|
||||||
|
|
||||||
|
@ -1063,6 +1088,7 @@ namespace glTF2
|
||||||
LazyDict<Buffer> buffers;
|
LazyDict<Buffer> buffers;
|
||||||
LazyDict<BufferView> bufferViews;
|
LazyDict<BufferView> bufferViews;
|
||||||
LazyDict<Camera> cameras;
|
LazyDict<Camera> cameras;
|
||||||
|
LazyDict<Light> lights;
|
||||||
LazyDict<Image> images;
|
LazyDict<Image> images;
|
||||||
LazyDict<Material> materials;
|
LazyDict<Material> materials;
|
||||||
LazyDict<Mesh> meshes;
|
LazyDict<Mesh> meshes;
|
||||||
|
@ -1083,6 +1109,7 @@ namespace glTF2
|
||||||
, buffers (*this, "buffers")
|
, buffers (*this, "buffers")
|
||||||
, bufferViews (*this, "bufferViews")
|
, bufferViews (*this, "bufferViews")
|
||||||
, cameras (*this, "cameras")
|
, cameras (*this, "cameras")
|
||||||
|
, lights (*this, "lights", "KHR_lights_punctual")
|
||||||
, images (*this, "images")
|
, images (*this, "images")
|
||||||
, materials (*this, "materials")
|
, materials (*this, "materials")
|
||||||
, meshes (*this, "meshes")
|
, meshes (*this, "meshes")
|
||||||
|
|
|
@ -1067,6 +1067,39 @@ inline void Camera::Read(Value& obj, Asset& /*r*/)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Light::Read(Value& obj, Asset& /*r*/)
|
||||||
|
{
|
||||||
|
#ifndef M_PI
|
||||||
|
const float M_PI = 3.14159265358979323846f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string type_string;
|
||||||
|
ReadMember(obj, "type", type_string);
|
||||||
|
if (type_string == "directional")
|
||||||
|
type = Light::Directional;
|
||||||
|
else if (type_string == "point")
|
||||||
|
type = Light::Point;
|
||||||
|
else
|
||||||
|
type = Light::Spot;
|
||||||
|
|
||||||
|
name = MemberOrDefault(obj, "name", "");
|
||||||
|
|
||||||
|
SetVector(color, vec3{ 1.0f, 1.0f, 1.0f });
|
||||||
|
ReadMember(obj, "color", color);
|
||||||
|
|
||||||
|
intensity = MemberOrDefault(obj, "intensity", 1.0f);
|
||||||
|
|
||||||
|
ReadMember(obj, "range", range);
|
||||||
|
|
||||||
|
if (type == Light::Spot)
|
||||||
|
{
|
||||||
|
Value* spot = FindObject(obj, "spot");
|
||||||
|
if (!spot) throw DeadlyImportError("GLTF: Light missing its spot parameters");
|
||||||
|
innerConeAngle = MemberOrDefault(*spot, "innerConeAngle", 0.0f);
|
||||||
|
outerConeAngle = MemberOrDefault(*spot, "outerConeAngle", M_PI / 4.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void Node::Read(Value& obj, Asset& r)
|
inline void Node::Read(Value& obj, Asset& r)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1110,6 +1143,19 @@ inline void Node::Read(Value& obj, Asset& r)
|
||||||
if (this->camera)
|
if (this->camera)
|
||||||
this->camera->id = this->id;
|
this->camera->id = this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Value* extensions = FindObject(obj, "extensions")) {
|
||||||
|
if (r.extensionsUsed.KHR_lights_punctual) {
|
||||||
|
|
||||||
|
if (Value* ext = FindObject(*extensions, "KHR_lights_punctual")) {
|
||||||
|
if (Value* light = FindUInt(*ext, "light")) {
|
||||||
|
this->light = r.lights.Retrieve(light->GetUint());
|
||||||
|
if (this->light)
|
||||||
|
this->light->id = this->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Scene::Read(Value& obj, Asset& r)
|
inline void Scene::Read(Value& obj, Asset& r)
|
||||||
|
@ -1421,6 +1467,7 @@ inline void Asset::ReadExtensionsUsed(Document& doc)
|
||||||
|
|
||||||
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
|
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
|
||||||
CHECK_EXT(KHR_materials_unlit);
|
CHECK_EXT(KHR_materials_unlit);
|
||||||
|
CHECK_EXT(KHR_lights_punctual);
|
||||||
|
|
||||||
#undef CHECK_EXT
|
#undef CHECK_EXT
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,11 @@ namespace glTF2 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Write(Value& /*obj*/, Light& /*c*/, AssetWriter& /*w*/)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
inline void Write(Value& obj, Image& img, AssetWriter& w)
|
inline void Write(Value& obj, Image& img, AssetWriter& w)
|
||||||
{
|
{
|
||||||
if (img.bufferView) {
|
if (img.bufferView) {
|
||||||
|
|
|
@ -140,10 +140,10 @@ static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
|
static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
|
||||||
//{
|
{
|
||||||
// out.r = v[0]; out.g = v[1]; out.b = v[2];
|
out.r = v[0]; out.g = v[1]; out.b = v[2];
|
||||||
//}
|
}
|
||||||
|
|
||||||
static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
|
static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
|
||||||
{
|
{
|
||||||
|
@ -710,6 +710,69 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void glTF2Importer::ImportLights(glTF2::Asset& r)
|
||||||
|
{
|
||||||
|
if (!r.lights.Size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mScene->mNumLights = r.lights.Size();
|
||||||
|
mScene->mLights = new aiLight*[r.lights.Size()];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < r.lights.Size(); ++i) {
|
||||||
|
Light& light = r.lights[i];
|
||||||
|
|
||||||
|
aiLight* ail = mScene->mLights[i] = new aiLight();
|
||||||
|
|
||||||
|
switch (light.type)
|
||||||
|
{
|
||||||
|
case Light::Directional:
|
||||||
|
ail->mType = aiLightSource_DIRECTIONAL; break;
|
||||||
|
case Light::Point:
|
||||||
|
ail->mType = aiLightSource_POINT; break;
|
||||||
|
case Light::Spot:
|
||||||
|
ail->mType = aiLightSource_SPOT; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ail->mType != aiLightSource_POINT)
|
||||||
|
{
|
||||||
|
ail->mDirection = aiVector3D(0.0f, 0.0f, -1.0f);
|
||||||
|
ail->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 colorWithIntensity = { light.color[0] * light.intensity, light.color[1] * light.intensity, light.color[2] * light.intensity };
|
||||||
|
CopyValue(colorWithIntensity, ail->mColorAmbient);
|
||||||
|
CopyValue(colorWithIntensity, ail->mColorDiffuse);
|
||||||
|
CopyValue(colorWithIntensity, ail->mColorSpecular);
|
||||||
|
|
||||||
|
if (ail->mType == aiLightSource_DIRECTIONAL)
|
||||||
|
{
|
||||||
|
ail->mAttenuationConstant = 1.0;
|
||||||
|
ail->mAttenuationLinear = 0.0;
|
||||||
|
ail->mAttenuationQuadratic = 0.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//in PBR attenuation is calculated using inverse square law which can be expressed
|
||||||
|
//using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters
|
||||||
|
//this is correct equation for the case when range (see
|
||||||
|
//https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual)
|
||||||
|
//is not present. When range is not present it is assumed that it is infinite and so numerator is 1.
|
||||||
|
//When range is present then numerator might be any value in range [0,1] and then assimps equation
|
||||||
|
//will not suffice. In this case range is added into metadata in ImportNode function
|
||||||
|
//and its up to implementation to read it when it wants to
|
||||||
|
ail->mAttenuationConstant = 0.0;
|
||||||
|
ail->mAttenuationLinear = 0.0;
|
||||||
|
ail->mAttenuationQuadratic = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ail->mType == aiLightSource_SPOT)
|
||||||
|
{
|
||||||
|
ail->mAngleInnerCone = light.innerConeAngle;
|
||||||
|
ail->mAngleOuterCone = light.outerConeAngle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) {
|
static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) {
|
||||||
if (node.matrix.isPresent) {
|
if (node.matrix.isPresent) {
|
||||||
CopyValue(node.matrix.value, matrix);
|
CopyValue(node.matrix.value, matrix);
|
||||||
|
@ -881,6 +944,18 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
|
||||||
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
|
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.light) {
|
||||||
|
pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
|
||||||
|
|
||||||
|
//range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
|
||||||
|
//it is added to meta data of parent node, because there is no other place to put it
|
||||||
|
if (node.light->range.isPresent)
|
||||||
|
{
|
||||||
|
ainode->mMetaData = aiMetadata::Alloc(1);
|
||||||
|
ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ainode;
|
return ainode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1150,6 +1225,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
|
||||||
ImportMeshes(asset);
|
ImportMeshes(asset);
|
||||||
|
|
||||||
ImportCameras(asset);
|
ImportCameras(asset);
|
||||||
|
ImportLights(asset);
|
||||||
|
|
||||||
ImportNodes(asset);
|
ImportNodes(asset);
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
/** @brief Specifies the maximum angle that may be between two vertex tangents
|
/** @brief Specifies the maximum angle that may be between two vertex tangents
|
||||||
* that their tangents and bi-tangents are smoothed.
|
* that their tangents and bi-tangents are smoothed.
|
||||||
*
|
*
|
||||||
* This applies to the CalcTangentSpace-Step. TFvhe angle is specified
|
* This applies to the CalcTangentSpace-Step. The angle is specified
|
||||||
* in degrees. The maximum value is 175.
|
* in degrees. The maximum value is 175.
|
||||||
* Property type: float. Default value: 45 degrees
|
* Property type: float. Default value: 45 degrees
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Ignore Unit Test Output files
|
||||||
|
|
||||||
|
*_out.*
|
|
@ -1,24 +0,0 @@
|
||||||
ply
|
|
||||||
format ascii 1.0
|
|
||||||
comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.993695325)
|
|
||||||
element vertex 8
|
|
||||||
property float x
|
|
||||||
property float y
|
|
||||||
property float z
|
|
||||||
element face 6
|
|
||||||
property list uchar int vertex_index
|
|
||||||
end_header
|
|
||||||
0 0 0
|
|
||||||
0 0 1
|
|
||||||
0 1 1
|
|
||||||
0 1 0
|
|
||||||
1 0 0
|
|
||||||
1 0 1
|
|
||||||
1 1 1
|
|
||||||
1 1 0
|
|
||||||
4 0 1 2 3
|
|
||||||
4 7 6 5 4
|
|
||||||
4 0 4 5 1
|
|
||||||
4 1 5 6 2
|
|
||||||
4 2 6 7 3
|
|
||||||
4 3 7 4 0
|
|
|
@ -57,7 +57,7 @@ public:
|
||||||
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure);
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure);
|
||||||
|
|
||||||
Exporter exporter;
|
Exporter exporter;
|
||||||
aiReturn res = exporter.Export(scene, "json", "./spider_test.json");
|
aiReturn res = exporter.Export(scene, "assjson", "./spider_test.json");
|
||||||
return aiReturn_SUCCESS == res;
|
return aiReturn_SUCCESS == res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,3 +71,32 @@ TEST_F(ExporterTest, ProgressHandlerTest) {
|
||||||
TestProgressHandler *ph(new TestProgressHandler);
|
TestProgressHandler *ph(new TestProgressHandler);
|
||||||
exporter.SetProgressHandler(ph);
|
exporter.SetProgressHandler(ph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all the registered exporters have useful descriptions
|
||||||
|
TEST_F(ExporterTest, ExporterIdTest) {
|
||||||
|
Exporter exporter;
|
||||||
|
size_t exportFormatCount = exporter.GetExportFormatCount();
|
||||||
|
EXPECT_NE(0u, exportFormatCount) << "No registered exporters";
|
||||||
|
typedef std::map<std::string, const aiExportFormatDesc*> ExportIdMap;
|
||||||
|
ExportIdMap exporterMap;
|
||||||
|
for (size_t i = 0; i < exportFormatCount; ++i)
|
||||||
|
{
|
||||||
|
// Check that the exporter description exists and makes sense
|
||||||
|
const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i);
|
||||||
|
ASSERT_NE(nullptr, desc) << "Missing aiExportFormatDesc at index " << i;
|
||||||
|
EXPECT_NE(nullptr, desc->id) << "Null exporter ID at index " << i;
|
||||||
|
EXPECT_STRNE("", desc->id) << "Empty exporter ID at index " << i;
|
||||||
|
EXPECT_NE(nullptr, desc->description) << "Null exporter description at index " << i;
|
||||||
|
EXPECT_STRNE("", desc->description) << "Empty exporter description at index " << i;
|
||||||
|
EXPECT_NE(nullptr, desc->fileExtension) << "Null exporter file extension at index " << i;
|
||||||
|
EXPECT_STRNE("", desc->fileExtension) << "Empty exporter file extension at index " << i;
|
||||||
|
|
||||||
|
// Check the ID is unique
|
||||||
|
std::string key(desc->id);
|
||||||
|
std::pair<ExportIdMap::iterator, bool> result = exporterMap.emplace(key, desc);
|
||||||
|
EXPECT_TRUE(result.second) << "Duplicate exported id: '" << key << "' " << desc->description << " *." << desc->fileExtension << " at index " << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(exportFormatCount);
|
||||||
|
EXPECT_EQ(nullptr, desc) << "More exporters than claimed";
|
||||||
|
}
|
||||||
|
|
|
@ -56,8 +56,8 @@ public:
|
||||||
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
|
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
|
||||||
|
|
||||||
Exporter exporter;
|
Exporter exporter;
|
||||||
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "assbin", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_test.assbin" ) );
|
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "assbin", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_out.assbin" ) );
|
||||||
const aiScene *newScene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider_test.assbin", aiProcess_ValidateDataStructure );
|
const aiScene *newScene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider_out.assbin", aiProcess_ValidateDataStructure );
|
||||||
|
|
||||||
return newScene != nullptr;
|
return newScene != nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,8 +205,8 @@ protected:
|
||||||
::Assimp::Exporter exporter;
|
::Assimp::Exporter exporter;
|
||||||
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
|
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
|
||||||
EXPECT_NE( nullptr, scene );
|
EXPECT_NE( nullptr, scene );
|
||||||
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_test.obj" ) );
|
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_out.obj" ) );
|
||||||
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "objnomtl", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_nomtl_test.obj" ) );
|
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "objnomtl", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_nomtl_out.obj" ) );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,7 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) {
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_EXPORT
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
::Assimp::Exporter exporter;
|
::Assimp::Exporter exporter;
|
||||||
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/test.obj" ) );
|
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/test_out.obj" ) );
|
||||||
#endif // ASSIMP_BUILD_NO_EXPORT
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ public:
|
||||||
Exporter exporter;
|
Exporter exporter;
|
||||||
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure);
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure);
|
||||||
EXPECT_NE(nullptr, scene);
|
EXPECT_NE(nullptr, scene);
|
||||||
EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "ply", ASSIMP_TEST_MODELS_DIR "/PLY/cube_test.ply"));
|
EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "ply", ASSIMP_TEST_MODELS_DIR "/PLY/cube_out.ply"));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue