pull/1302/head
Kim Kulling 2017-05-25 22:40:36 +02:00
commit 1b4cbcc6ad
40 changed files with 3744 additions and 112 deletions

View File

@ -0,0 +1,72 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#.rst:
# FindDevIL
# ---------
#
#
#
# This module locates the developer's image library.
# http://openil.sourceforge.net/
#
# This module sets:
#
# ::
#
# IL_LIBRARIES - the name of the IL library. These include the full path to
# the core DevIL library. This one has to be linked into the
# application.
# ILU_LIBRARIES - the name of the ILU library. Again, the full path. This
# library is for filters and effects, not actual loading. It
# doesn't have to be linked if the functionality it provides
# is not used.
# ILUT_LIBRARIES - the name of the ILUT library. Full path. This part of the
# library interfaces with OpenGL. It is not strictly needed
# in applications.
# IL_INCLUDE_DIR - where to find the il.h, ilu.h and ilut.h files.
# IL_FOUND - this is set to TRUE if all the above variables were set.
# This will be set to false if ILU or ILUT are not found,
# even if they are not needed. In most systems, if one
# library is found all the others are as well. That's the
# way the DevIL developers release it.
# TODO: Add version support.
# Tested under Linux and Windows (MSVC)
#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
include(FindPackageHandleStandardArgs)
find_path(IL_INCLUDE_DIR il.h
PATH_SUFFIXES include IL
DOC "The path to the directory that contains il.h"
)
#message("IL_INCLUDE_DIR is ${IL_INCLUDE_DIR}")
find_library(IL_LIBRARIES
NAMES IL DEVIL
PATH_SUFFIXES lib64 lib lib32
DOC "The file that corresponds to the base il library."
)
#message("IL_LIBRARIES is ${IL_LIBRARIES}")
find_library(ILUT_LIBRARIES
NAMES ILUT
PATH_SUFFIXES lib64 lib lib32
DOC "The file that corresponds to the il (system?) utility library."
)
#message("ILUT_LIBRARIES is ${ILUT_LIBRARIES}")
find_library(ILU_LIBRARIES
NAMES ILU
PATH_SUFFIXES lib64 lib lib32
DOC "The file that corresponds to the il utility library."
)
#message("ILU_LIBRARIES is ${ILU_LIBRARIES}")
FIND_PACKAGE_HANDLE_STANDARD_ARGS(IL DEFAULT_MSG
IL_LIBRARIES IL_INCLUDE_DIR)

View File

@ -198,7 +198,7 @@ template <typename T> void ReadBounds( IOStream * stream, T* /*p*/, unsigned int
stream->Seek( sizeof(T) * n, aiOrigin_CUR ); stream->Seek( sizeof(T) * n, aiOrigin_CUR );
} }
void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node ) void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* parent )
{ {
uint32_t chunkID = Read<uint32_t>(stream); uint32_t chunkID = Read<uint32_t>(stream);
ai_assert(chunkID == ASSBIN_CHUNK_AINODE); ai_assert(chunkID == ASSBIN_CHUNK_AINODE);
@ -210,6 +210,10 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node )
(*node)->mTransformation = Read<aiMatrix4x4>(stream); (*node)->mTransformation = Read<aiMatrix4x4>(stream);
(*node)->mNumChildren = Read<unsigned int>(stream); (*node)->mNumChildren = Read<unsigned int>(stream);
(*node)->mNumMeshes = Read<unsigned int>(stream); (*node)->mNumMeshes = Read<unsigned int>(stream);
if(parent)
{
(*node)->mParent = parent;
}
if ((*node)->mNumMeshes) if ((*node)->mNumMeshes)
{ {
@ -223,7 +227,7 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node )
{ {
(*node)->mChildren = new aiNode*[(*node)->mNumChildren]; (*node)->mChildren = new aiNode*[(*node)->mNumChildren];
for (unsigned int i = 0; i < (*node)->mNumChildren; ++i) { for (unsigned int i = 0; i < (*node)->mNumChildren; ++i) {
ReadBinaryNode( stream, &(*node)->mChildren[i] ); ReadBinaryNode( stream, &(*node)->mChildren[i], *node );
} }
} }
@ -571,7 +575,7 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
// Read node graph // Read node graph
scene->mRootNode = new aiNode[1]; scene->mRootNode = new aiNode[1];
ReadBinaryNode( stream, &scene->mRootNode ); ReadBinaryNode( stream, &scene->mRootNode, (aiNode*)NULL );
// Read all meshes // Read all meshes
if (scene->mNumMeshes) if (scene->mNumMeshes)

View File

@ -86,7 +86,7 @@ public:
IOSystem* pIOHandler IOSystem* pIOHandler
); );
void ReadBinaryScene( IOStream * stream, aiScene* pScene ); void ReadBinaryScene( IOStream * stream, aiScene* pScene );
void ReadBinaryNode( IOStream * stream, aiNode** mRootNode ); void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent );
void ReadBinaryMesh( IOStream * stream, aiMesh* mesh ); void ReadBinaryMesh( IOStream * stream, aiMesh* mesh );
void ReadBinaryBone( IOStream * stream, aiBone* bone ); void ReadBinaryBone( IOStream * stream, aiBone* bone );
void ReadBinaryMaterial(IOStream * stream, aiMaterial* mat); void ReadBinaryMaterial(IOStream * stream, aiMaterial* mat);

View File

@ -110,6 +110,7 @@ BlenderImporter::~BlenderImporter()
} }
static const char* Tokens[] = { "BLENDER" }; static const char* Tokens[] = { "BLENDER" };
static const char* TokensForSearch[] = { "blender" };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
@ -122,7 +123,7 @@ bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, b
else if ((!extension.length() || checkSig) && pIOHandler) { else if ((!extension.length() || checkSig) && pIOHandler) {
// note: this won't catch compressed files // note: this won't catch compressed files
return SearchFileHeaderForToken(pIOHandler,pFile, Tokens,1); return SearchFileHeaderForToken(pIOHandler,pFile, TokensForSearch,1);
} }
return false; return false;
} }

View File

@ -667,6 +667,16 @@ ADD_ASSIMP_IMPORTER( 3MF
D3MFOpcPackage.cpp D3MFOpcPackage.cpp
) )
ADD_ASSIMP_IMPORTER( MMD
MMDCpp14.h
MMDImporter.cpp
MMDImporter.h
MMDPmdParser.h
MMDPmxParser.h
MMDPmxParser.cpp
MMDVmdParser.h
)
SET( Step_SRCS SET( Step_SRCS
StepExporter.h StepExporter.h
StepExporter.cpp StepExporter.cpp

View File

@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory> #include <memory>
#include <ctime> #include <ctime>
#include <set> #include <set>
#include <vector>
#include <iostream>
using namespace Assimp; using namespace Assimp;
@ -132,6 +134,7 @@ void ColladaExporter::WriteFile()
WriteLightsLibrary(); WriteLightsLibrary();
WriteMaterials(); WriteMaterials();
WriteGeometryLibrary(); WriteGeometryLibrary();
WriteControllerLibrary();
WriteSceneLibrary(); WriteSceneLibrary();
@ -785,6 +788,177 @@ void ColladaExporter::WriteMaterials()
} }
} }
// ------------------------------------------------------------------------------------------------
// Writes the controller library
void ColladaExporter::WriteControllerLibrary()
{
mOutput << startstr << "<library_controllers>" << endstr;
PushTag();
for( size_t a = 0; a < mScene->mNumMeshes; ++a)
WriteController( a);
PopTag();
mOutput << startstr << "</library_controllers>" << endstr;
}
// ------------------------------------------------------------------------------------------------
// Writes a skin controller of the given mesh
void ColladaExporter::WriteController( size_t pIndex)
{
const aiMesh* mesh = mScene->mMeshes[pIndex];
const std::string idstr = GetMeshId( pIndex);
const std::string idstrEscaped = XMLEscape(idstr);
if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
return;
if ( mesh->mNumBones == 0 )
return;
mOutput << startstr << "<controller id=\"" << idstrEscaped << "-skin\" ";
mOutput << "name=\"skinCluster" << pIndex << "\">"<< endstr;
PushTag();
mOutput << startstr << "<skin source=\"#" << idstrEscaped << "\">" << endstr;
PushTag();
// bind pose matrix
mOutput << startstr << "<bind_shape_matrix>" << endstr;
PushTag();
// I think it is identity in general cases.
aiMatrix4x4 mat;
mOutput << startstr << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << endstr;
mOutput << startstr << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << endstr;
mOutput << startstr << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << endstr;
mOutput << startstr << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4 << endstr;
PopTag();
mOutput << startstr << "</bind_shape_matrix>" << endstr;
mOutput << startstr << "<source id=\"" << idstrEscaped << "-skin-joints\" name=\"" << idstrEscaped << "-skin-joints\">" << endstr;
PushTag();
mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
for( size_t i = 0; i < mesh->mNumBones; ++i )
mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " ";
mOutput << "</Name_array>" << endstr;
mOutput << startstr << "<technique_common>" << endstr;
PushTag();
mOutput << startstr << "<accessor source=\"#" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\" stride=\"" << 1 << "\">" << endstr;
PushTag();
mOutput << startstr << "<param name=\"JOINT\" type=\"Name\"></param>" << endstr;
PopTag();
mOutput << startstr << "</accessor>" << endstr;
PopTag();
mOutput << startstr << "</technique_common>" << endstr;
PopTag();
mOutput << startstr << "</source>" << endstr;
std::vector<ai_real> bind_poses;
bind_poses.reserve(mesh->mNumBones * 16);
for( size_t i = 0; i < mesh->mNumBones; ++i)
for( size_t j = 0; j < 4; ++j)
bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
WriteFloatArray( idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real*) bind_poses.data(), bind_poses.size() / 16);
bind_poses.clear();
std::vector<ai_real> skin_weights;
skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones);
for( size_t i = 0; i < mesh->mNumBones; ++i)
for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight);
WriteFloatArray( idstr + "-skin-weights", FloatType_Weight, (const ai_real*) skin_weights.data(), skin_weights.size());
skin_weights.clear();
mOutput << startstr << "<joints>" << endstr;
PushTag();
mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstrEscaped << "-skin-joints\"></input>" << endstr;
mOutput << startstr << "<input semantic=\"INV_BIND_MATRIX\" source=\"#" << idstrEscaped << "-skin-bind_poses\"></input>" << endstr;
PopTag();
mOutput << startstr << "</joints>" << endstr;
mOutput << startstr << "<vertex_weights count=\"" << mesh->mNumVertices << "\">" << endstr;
PushTag();
mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstrEscaped << "-skin-joints\" offset=\"0\"></input>" << endstr;
mOutput << startstr << "<input semantic=\"WEIGHT\" source=\"#" << idstrEscaped << "-skin-weights\" offset=\"1\"></input>" << endstr;
mOutput << startstr << "<vcount>";
std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0);
for( size_t i = 0; i < mesh->mNumBones; ++i)
for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
++num_influences[mesh->mBones[i]->mWeights[j].mVertexId];
for( size_t i = 0; i < mesh->mNumVertices; ++i)
mOutput << num_influences[i] << " ";
mOutput << "</vcount>" << endstr;
mOutput << startstr << "<v>";
ai_uint joint_weight_indices_length = 0;
std::vector<ai_uint> accum_influences;
accum_influences.reserve(num_influences.size());
for( size_t i = 0; i < num_influences.size(); ++i)
{
accum_influences.push_back(joint_weight_indices_length);
joint_weight_indices_length += num_influences[i];
}
ai_uint weight_index = 0;
std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
for( size_t i = 0; i < mesh->mNumBones; ++i)
for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
{
unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
for( size_t k = 0; k < num_influences[vId]; ++k)
{
if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1)
{
joint_weight_indices[2 * (accum_influences[vId] + k)] = i;
joint_weight_indices[2 * (accum_influences[vId] + k) + 1] = weight_index;
break;
}
}
++weight_index;
}
for( size_t i = 0; i < joint_weight_indices.size(); ++i)
mOutput << joint_weight_indices[i] << " ";
num_influences.clear();
accum_influences.clear();
joint_weight_indices.clear();
mOutput << "</v>" << endstr;
PopTag();
mOutput << startstr << "</vertex_weights>" << endstr;
PopTag();
mOutput << startstr << "</skin>" << endstr;
PopTag();
mOutput << startstr << "</controller>" << endstr;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Writes the geometry library // Writes the geometry library
void ColladaExporter::WriteGeometryLibrary() void ColladaExporter::WriteGeometryLibrary()
@ -949,6 +1123,8 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
case FloatType_TexCoord2: floatsPerElement = 2; break; case FloatType_TexCoord2: floatsPerElement = 2; break;
case FloatType_TexCoord3: floatsPerElement = 3; break; case FloatType_TexCoord3: floatsPerElement = 3; break;
case FloatType_Color: floatsPerElement = 3; break; case FloatType_Color: floatsPerElement = 3; break;
case FloatType_Mat4x4: floatsPerElement = 16; break;
case FloatType_Weight: floatsPerElement = 1; break;
default: default:
return; return;
} }
@ -1017,6 +1193,14 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
mOutput << startstr << "<param name=\"G\" type=\"float\" />" << endstr; mOutput << startstr << "<param name=\"G\" type=\"float\" />" << endstr;
mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr; mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr;
break; break;
case FloatType_Mat4x4:
mOutput << startstr << "<param name=\"TRANSFORM\" type=\"float4x4\" />" << endstr;
break;
case FloatType_Weight:
mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << endstr;
break;
} }
PopTag(); PopTag();
@ -1078,16 +1262,24 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
// If the node is associated with a bone, it is a joint node (JOINT) // If the node is associated with a bone, it is a joint node (JOINT)
// otherwise it is a normal node (NODE) // otherwise it is a normal node (NODE)
const char * node_type; const char * node_type;
bool is_joint, is_skeleton_root = false;
if (NULL == findBone(pScene, pNode->mName.C_Str())) { if (NULL == findBone(pScene, pNode->mName.C_Str())) {
node_type = "NODE"; node_type = "NODE";
is_joint = false;
} else { } else {
node_type = "JOINT"; node_type = "JOINT";
is_joint = true;
if(!pNode->mParent || NULL == findBone(pScene, pNode->mParent->mName.C_Str()))
is_skeleton_root = true;
} }
const std::string node_name_escaped = XMLEscape(pNode->mName.data); const std::string node_name_escaped = XMLEscape(pNode->mName.data);
mOutput << startstr mOutput << startstr
<< "<node id=\"" << node_name_escaped << "<node ";
<< "\" name=\"" << node_name_escaped if(is_skeleton_root)
mOutput << "id=\"" << "skeleton_root" << "\" "; // For now, only support one skeleton in a scene.
mOutput << (is_joint ? "s" : "") << "id=\"" << node_name_escaped;
mOutput << "\" name=\"" << node_name_escaped
<< "\" type=\"" << node_type << "\" type=\"" << node_type
<< "\">" << endstr; << "\">" << endstr;
PushTag(); PushTag();
@ -1095,7 +1287,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
// write transformation - we can directly put the matrix there // write transformation - we can directly put the matrix there
// TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards
const aiMatrix4x4& mat = pNode->mTransformation; const aiMatrix4x4& mat = pNode->mTransformation;
mOutput << startstr << "<matrix>"; mOutput << startstr << "<matrix sid=\"transform\">";
mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " "; mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " "; mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " ";
mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " "; mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " ";
@ -1126,8 +1318,21 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
// do not instanciate mesh if empty. I wonder how this could happen // do not instanciate mesh if empty. I wonder how this could happen
if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
continue; continue;
if( mesh->mNumBones == 0 )
{
mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr; mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
PushTag(); PushTag();
}
else
{
mOutput << startstr
<< "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">"
<< endstr;
PushTag();
mOutput << startstr << "<skeleton>#skeleton_root</skeleton>" << endstr;
}
mOutput << startstr << "<bind_material>" << endstr; mOutput << startstr << "<bind_material>" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<technique_common>" << endstr; mOutput << startstr << "<technique_common>" << endstr;
@ -1148,8 +1353,12 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
mOutput << startstr << "</technique_common>" << endstr; mOutput << startstr << "</technique_common>" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</bind_material>" << endstr; mOutput << startstr << "</bind_material>" << endstr;
PopTag(); PopTag();
if( mesh->mNumBones == 0)
mOutput << startstr << "</instance_geometry>" << endstr; mOutput << startstr << "</instance_geometry>" << endstr;
else
mOutput << startstr << "</instance_controller>" << endstr;
} }
// recurse into subnodes // recurse into subnodes

View File

@ -102,13 +102,19 @@ protected:
void WriteSpotLight(const aiLight *const light); void WriteSpotLight(const aiLight *const light);
void WriteAmbienttLight(const aiLight *const light); void WriteAmbienttLight(const aiLight *const light);
/// Writes the controller library
void WriteControllerLibrary();
/// Writes a skin controller of the given mesh
void WriteController( size_t pIndex);
/// Writes the geometry library /// Writes the geometry library
void WriteGeometryLibrary(); void WriteGeometryLibrary();
/// Writes the given mesh /// Writes the given mesh
void WriteGeometry( size_t pIndex); void WriteGeometry( size_t pIndex);
enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color }; enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight };
/// Writes a float array of the given type /// Writes a float array of the given type
void WriteFloatArray( const std::string& pIdString, FloatDataType pType, const ai_real* pData, size_t pElementCount); void WriteFloatArray( const std::string& pIdString, FloatDataType pType, const ai_real* pData, size_t pElementCount);

View File

@ -192,6 +192,9 @@ corresponding preprocessor flag to selectively disable formats.
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
# include "X3DImporter.hpp" # include "X3DImporter.hpp"
#endif #endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
# include "MMDImporter.h"
#endif
namespace Assimp { namespace Assimp {
@ -341,6 +344,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
out.push_back( new X3DImporter() ); out.push_back( new X3DImporter() );
#endif #endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
out.push_back( new MMDImporter() );
#endif
} }
/** will delete all registered importers. */ /** will delete all registered importers. */

View File

@ -67,7 +67,7 @@ static const aiImporterDesc desc = {
"LightWave/Modo Object Importer", "LightWave/Modo Object Importer",
"", "",
"", "",
"http://www.newtek.com/lightwave.html\nhttp://www.luxology.com/modo/", "https://www.lightwave3d.com/lightwave_sdk/",
aiImporterFlags_SupportTextFlavour, aiImporterFlags_SupportTextFlavour,
0, 0,
0, 0,

View File

@ -546,10 +546,6 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
} }
else if (iMasked || !iType || (iType && iWidth && iHeight)) else if (iMasked || !iType || (iType && iWidth && iHeight))
{ {
// ***** STANDARD COLOR TEXTURE *****
if(pcNew!= nullptr)
delete pcNew;
pcNew = new aiTexture(); pcNew = new aiTexture();
if (!iHeight || !iWidth) if (!iHeight || !iWidth)
{ {

42
code/MMDCpp14.h 100644
View File

@ -0,0 +1,42 @@
#pragma once
#ifndef MMD_CPP14_H
#define MMD_CPP14_H
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
namespace mmd {
template<class T> struct _Unique_if {
typedef std::unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]> {
typedef std::unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]> {
typedef void _Known_bound;
};
template<class T, class... Args>
typename _Unique_if<T>::_Single_object
make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T>
typename _Unique_if<T>::_Unknown_bound
make_unique(size_t n) {
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[n]());
}
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;
}
#endif

View File

@ -0,0 +1,370 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2016, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
#include "MMDImporter.h"
#include "MMDPmdParser.h"
#include "MMDPmxParser.h"
#include "MMDVmdParser.h"
#include "ConvertToLHProcess.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp>
#include <assimp/ai_assert.h>
#include <assimp/scene.h>
#include <fstream>
#include <iomanip>
#include <memory>
static const aiImporterDesc desc = {"MMD Importer",
"",
"",
"surfaces supported?",
aiImporterFlags_SupportTextFlavour,
0,
0,
0,
0,
"pmx"};
namespace Assimp {
using namespace std;
// ------------------------------------------------------------------------------------------------
// Default constructor
MMDImporter::MMDImporter()
: m_Buffer(),
// m_pRootObject( NULL ),
m_strAbsPath("") {
DefaultIOSystem io;
m_strAbsPath = io.getOsSeparator();
}
// ------------------------------------------------------------------------------------------------
// Destructor.
MMDImporter::~MMDImporter() {
// delete m_pRootObject;
// m_pRootObject = NULL;
}
// ------------------------------------------------------------------------------------------------
// Returns true, if file is an pmx file.
bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler,
bool checkSig) const {
if (!checkSig) // Check File Extension
{
return SimpleExtensionCheck(pFile, "pmx");
} else // Check file Header
{
static const char *pTokens[] = {"PMX "};
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens,
1);
}
}
// ------------------------------------------------------------------------------------------------
const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; }
// ------------------------------------------------------------------------------------------------
// MMD import implementation
void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
IOSystem *pIOHandler) {
// Read file by istream
std::filebuf fb;
if (!fb.open(file, std::ios::in | std::ios::binary)) {
throw DeadlyImportError("Failed to open file " + file + ".");
}
std::istream fileStream(&fb);
// Get the file-size and validate it, throwing an exception when fails
fileStream.seekg(0, fileStream.end);
size_t fileSize = fileStream.tellg();
fileStream.seekg(0, fileStream.beg);
if (fileSize < sizeof(pmx::PmxModel)) {
throw DeadlyImportError(file + " is too small.");
}
pmx::PmxModel model;
model.Read(&fileStream);
CreateDataFromImport(&model, pScene);
}
// ------------------------------------------------------------------------------------------------
void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
aiScene *pScene) {
if (pModel == NULL) {
return;
}
aiNode *pNode = new aiNode;
if (!pModel->model_name.empty()) {
pNode->mName.Set(pModel->model_name);
} else {
ai_assert(false);
}
pScene->mRootNode = pNode;
pNode = new aiNode;
pScene->mRootNode->addChildren(1, &pNode);
pNode->mName.Set(string(pModel->model_name) + string("_mesh"));
// split mesh by materials
pNode->mNumMeshes = pModel->material_count;
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
for (unsigned int index = 0; index < pNode->mNumMeshes; index++) {
pNode->mMeshes[index] = index;
}
pScene->mNumMeshes = pModel->material_count;
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) {
const int indexCount = pModel->materials[i].index_count;
pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount);
pScene->mMeshes[i]->mName = pModel->materials[i].material_name;
pScene->mMeshes[i]->mMaterialIndex = i;
indexStart += indexCount;
}
// create node hierarchy for bone position
aiNode **ppNode = new aiNode *[pModel->bone_count];
for (auto i = 0; i < pModel->bone_count; i++) {
ppNode[i] = new aiNode(pModel->bones[i].bone_name);
}
for (auto i = 0; i < pModel->bone_count; i++) {
const pmx::PmxBone &bone = pModel->bones[i];
if (bone.parent_index < 0) {
pScene->mRootNode->addChildren(1, ppNode + i);
} else {
ppNode[bone.parent_index]->addChildren(1, ppNode + i);
aiVector3D v3 = aiVector3D(
bone.position[0] - pModel->bones[bone.parent_index].position[0],
bone.position[1] - pModel->bones[bone.parent_index].position[1],
bone.position[2] - pModel->bones[bone.parent_index].position[2]);
aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation);
}
}
// create materials
pScene->mNumMaterials = pModel->material_count;
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials; i++) {
pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel);
}
// Convert everything to OpenGL space
MakeLeftHandedProcess convertProcess;
convertProcess.Execute(pScene);
FlipUVsProcess uvFlipper;
uvFlipper.Execute(pScene);
FlipWindingOrderProcess windingFlipper;
windingFlipper.Execute(pScene);
}
// ------------------------------------------------------------------------------------------------
aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
const int indexStart, const int indexCount) {
aiMesh *pMesh = new aiMesh;
pMesh->mNumVertices = indexCount;
pMesh->mNumFaces = indexCount / 3;
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
const int numIndices = 3; // trianglular face
for (unsigned int index = 0; index < pMesh->mNumFaces; index++) {
pMesh->mFaces[index].mNumIndices = numIndices;
unsigned int *indices = new unsigned int[numIndices];
indices[0] = numIndices * index;
indices[1] = numIndices * index + 1;
indices[2] = numIndices * index + 2;
pMesh->mFaces[index].mIndices = indices;
}
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
pMesh->mNumUVComponents[0] = 2;
// additional UVs
for (int i = 1; i <= pModel->setting.uv; i++) {
pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices];
pMesh->mNumUVComponents[i] = 4;
}
map<int, vector<aiVertexWeight>> bone_vertex_map;
// fill in contents and create bones
for (int index = 0; index < indexCount; index++) {
const pmx::PmxVertex *v =
&pModel->vertices[pModel->indices[indexStart + index]];
const float *position = v->position;
pMesh->mVertices[index].Set(position[0], position[1], position[2]);
const float *normal = v->normal;
pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]);
pMesh->mTextureCoords[0][index].x = v->uv[0];
pMesh->mTextureCoords[0][index].y = v->uv[1];
for (int i = 1; i <= pModel->setting.uv; i++) {
// TODO: wrong here? use quaternion transform?
pMesh->mTextureCoords[i][index].x = v->uva[i][0];
pMesh->mTextureCoords[i][index].y = v->uva[i][1];
}
// handle bone map
const auto vsBDEF1_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF1 *>(v->skinning.get());
const auto vsBDEF2_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF2 *>(v->skinning.get());
const auto vsBDEF4_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF4 *>(v->skinning.get());
const auto vsSDEF_ptr =
dynamic_cast<pmx::PmxVertexSkinningSDEF *>(v->skinning.get());
switch (v->skinning_type) {
case pmx::PmxVertexSkinningType::BDEF1:
bone_vertex_map[vsBDEF1_ptr->bone_index].push_back(
aiVertexWeight(index, 1.0));
break;
case pmx::PmxVertexSkinningType::BDEF2:
bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back(
aiVertexWeight(index, vsBDEF2_ptr->bone_weight));
bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back(
aiVertexWeight(index, 1.0 - vsBDEF2_ptr->bone_weight));
break;
case pmx::PmxVertexSkinningType::BDEF4:
bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight1));
bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight2));
bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight3));
bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight4));
break;
case pmx::PmxVertexSkinningType::SDEF: // TODO: how to use sdef_c, sdef_r0,
// sdef_r1?
bone_vertex_map[vsSDEF_ptr->bone_index1].push_back(
aiVertexWeight(index, vsSDEF_ptr->bone_weight));
bone_vertex_map[vsSDEF_ptr->bone_index2].push_back(
aiVertexWeight(index, 1.0 - vsSDEF_ptr->bone_weight));
break;
case pmx::PmxVertexSkinningType::QDEF:
const auto vsQDEF_ptr =
dynamic_cast<pmx::PmxVertexSkinningQDEF *>(v->skinning.get());
bone_vertex_map[vsQDEF_ptr->bone_index1].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight1));
bone_vertex_map[vsQDEF_ptr->bone_index2].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight2));
bone_vertex_map[vsQDEF_ptr->bone_index3].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight3));
bone_vertex_map[vsQDEF_ptr->bone_index4].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight4));
break;
}
}
// make all bones for each mesh
// assign bone weights to skinned bones (otherwise just initialize)
auto bone_ptr_ptr = new aiBone *[pModel->bone_count];
pMesh->mNumBones = pModel->bone_count;
pMesh->mBones = bone_ptr_ptr;
for (auto ii = 0; ii < pModel->bone_count; ++ii) {
auto pBone = new aiBone;
const auto &pmxBone = pModel->bones[ii];
pBone->mName = pmxBone.bone_name;
aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]);
aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix);
auto it = bone_vertex_map.find(ii);
if (it != bone_vertex_map.end()) {
pBone->mNumWeights = it->second.size();
pBone->mWeights = it->second.data();
it->second.swap(*(new vector<aiVertexWeight>));
}
bone_ptr_ptr[ii] = pBone;
}
return pMesh;
}
// ------------------------------------------------------------------------------------------------
aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat,
const pmx::PmxModel *pModel) {
aiMaterial *mat = new aiMaterial();
aiString name(pMat->material_english_name);
mat->AddProperty(&name, AI_MATKEY_NAME);
aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]);
mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]);
mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR);
aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]);
mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT);
float opacity = pMat->diffuse[3];
mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY);
float shininess = pMat->specularlity;
mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH);
aiString texture_path(pModel->textures[pMat->diffuse_texture_index]);
mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
int mapping_uvwsrc = 0;
mat->AddProperty(&mapping_uvwsrc, 1,
AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0));
return mat;
}
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER

96
code/MMDImporter.h 100644
View File

@ -0,0 +1,96 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2016, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef MMD_FILE_IMPORTER_H_INC
#define MMD_FILE_IMPORTER_H_INC
#include "BaseImporter.h"
#include "MMDPmxParser.h"
#include <assimp/material.h>
#include <vector>
struct aiMesh;
namespace Assimp {
// ------------------------------------------------------------------------------------------------
/// \class MMDImporter
/// \brief Imports MMD a pmx/pmd/vmd file
// ------------------------------------------------------------------------------------------------
class MMDImporter : public BaseImporter {
public:
/// \brief Default constructor
MMDImporter();
/// \brief Destructor
~MMDImporter();
public:
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
private:
//! \brief Appends the supported extension.
const aiImporterDesc* GetInfo () const;
//! \brief File import implementation.
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
//! \brief Create the data from imported content.
void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene);
//! \brief Create the mesh
aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount);
//! \brief Create the material
aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel);
private:
//! Data buffer
std::vector<char> m_Buffer;
//! Absolute pathname of model in file system
std::string m_strAbsPath;
};
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif

630
code/MMDPmdParser.h 100644
View File

@ -0,0 +1,630 @@
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <iostream>
#include <fstream>
#include "MMDCpp14.h"
namespace pmd
{
/// ヘッダ
class PmdHeader
{
public:
/// モデル名
std::string name;
/// モデル名(英語)
std::string name_english;
/// コメント
std::string comment;
/// コメント(英語)
std::string comment_english;
bool Read(std::ifstream* stream)
{
char buffer[256];
stream->read(buffer, 20);
name = std::string(buffer);
stream->read(buffer, 256);
comment = std::string(buffer);
return true;
}
bool ReadExtension(std::ifstream* stream)
{
char buffer[256];
stream->read(buffer, 20);
name_english = std::string(buffer);
stream->read(buffer, 256);
comment_english = std::string(buffer);
return true;
}
};
/// 頂点
class PmdVertex
{
public:
/// 位置
float position[3];
/// 法線
float normal[3];
/// UV座標
float uv[2];
/// 関連ボーンインデックス
uint16_t bone_index[2];
/// ボーンウェイト
uint8_t bone_weight;
/// エッジ不可視
bool edge_invisible;
bool Read(std::ifstream* stream)
{
stream->read((char*) position, sizeof(float) * 3);
stream->read((char*) normal, sizeof(float) * 3);
stream->read((char*) uv, sizeof(float) * 2);
stream->read((char*) bone_index, sizeof(uint16_t) * 2);
stream->read((char*) &bone_weight, sizeof(uint8_t));
stream->read((char*) &edge_invisible, sizeof(uint8_t));
return true;
}
};
/// 材質
class PmdMaterial
{
public:
/// 減衰色
float diffuse[4];
/// 光沢度
float power;
/// 光沢色
float specular[3];
/// 環境色
float ambient[3];
/// トーンインデックス
uint8_t toon_index;
/// エッジ
uint8_t edge_flag;
/// インデックス数
uint32_t index_count;
/// テクスチャファイル名
std::string texture_filename;
/// スフィアファイル名
std::string sphere_filename;
bool Read(std::ifstream* stream)
{
char buffer[20];
stream->read((char*) &diffuse, sizeof(float) * 4);
stream->read((char*) &power, sizeof(float));
stream->read((char*) &specular, sizeof(float) * 3);
stream->read((char*) &ambient, sizeof(float) * 3);
stream->read((char*) &toon_index, sizeof(uint8_t));
stream->read((char*) &edge_flag, sizeof(uint8_t));
stream->read((char*) &index_count, sizeof(uint32_t));
stream->read((char*) &buffer, sizeof(char) * 20);
char* pstar = strchr(buffer, '*');
if (NULL == pstar)
{
texture_filename = std::string(buffer);
sphere_filename.clear();
}
else {
*pstar = (char)NULL;
texture_filename = std::string(buffer);
sphere_filename = std::string(pstar+1);
}
return true;
}
};
enum class BoneType : uint8_t
{
Rotation,
RotationAndMove,
IkEffector,
Unknown,
IkEffectable,
RotationEffectable,
IkTarget,
Invisible,
Twist,
RotationMovement
};
/// ボーン
class PmdBone
{
public:
/// ボーン名
std::string name;
/// ボーン名(英語)
std::string name_english;
/// 親ボーン番号
uint16_t parent_bone_index;
/// 末端ボーン番号
uint16_t tail_pos_bone_index;
/// ボーン種類
BoneType bone_type;
/// IKボーン番号
uint16_t ik_parent_bone_index;
/// ボーンのヘッドの位置
float bone_head_pos[3];
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name = std::string(buffer);
stream->read((char*) &parent_bone_index, sizeof(uint16_t));
stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t));
stream->read((char*) &bone_type, sizeof(uint8_t));
stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t));
stream->read((char*) &bone_head_pos, sizeof(float) * 3);
}
void ReadExpantion(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name_english = std::string(buffer);
}
};
/// IK
class PmdIk
{
public:
/// IKボーン番号
uint16_t ik_bone_index;
/// IKターゲットボーン番号
uint16_t target_bone_index;
/// 再帰回数
uint16_t interations;
/// 角度制限
float angle_limit;
/// 影響下ボーン番号
std::vector<uint16_t> ik_child_bone_index;
void Read(std::istream *stream)
{
stream->read((char *) &ik_bone_index, sizeof(uint16_t));
stream->read((char *) &target_bone_index, sizeof(uint16_t));
uint8_t ik_chain_length;
stream->read((char*) &ik_chain_length, sizeof(uint8_t));
stream->read((char *) &interations, sizeof(uint16_t));
stream->read((char *) &angle_limit, sizeof(float));
ik_child_bone_index.resize(ik_chain_length);
for (int i = 0; i < ik_chain_length; i++)
{
stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t));
}
}
};
class PmdFaceVertex
{
public:
int vertex_index;
float position[3];
void Read(std::istream *stream)
{
stream->read((char *) &vertex_index, sizeof(int));
stream->read((char *) position, sizeof(float) * 3);
}
};
enum class FaceCategory : uint8_t
{
Base,
Eyebrow,
Eye,
Mouth,
Other
};
class PmdFace
{
public:
std::string name;
FaceCategory type;
std::vector<PmdFaceVertex> vertices;
std::string name_english;
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name = std::string(buffer);
int vertex_count;
stream->read((char*) &vertex_count, sizeof(int));
stream->read((char*) &type, sizeof(uint8_t));
vertices.resize(vertex_count);
for (int i = 0; i < vertex_count; i++)
{
vertices[i].Read(stream);
}
}
void ReadExpantion(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name_english = std::string(buffer);
}
};
/// ボーン枠用の枠名
class PmdBoneDispName
{
public:
std::string bone_disp_name;
std::string bone_disp_name_english;
void Read(std::istream *stream)
{
char buffer[50];
stream->read(buffer, 50);
bone_disp_name = std::string(buffer);
bone_disp_name_english.clear();
}
void ReadExpantion(std::istream *stream)
{
char buffer[50];
stream->read(buffer, 50);
bone_disp_name_english = std::string(buffer);
}
};
class PmdBoneDisp
{
public:
uint16_t bone_index;
uint8_t bone_disp_index;
void Read(std::istream *stream)
{
stream->read((char*) &bone_index, sizeof(uint16_t));
stream->read((char*) &bone_disp_index, sizeof(uint8_t));
}
};
/// 衝突形状
enum class RigidBodyShape : uint8_t
{
/// 球
Sphere = 0,
/// 直方体
Box = 1,
/// カプセル
Cpusel = 2
};
/// 剛体タイプ
enum class RigidBodyType : uint8_t
{
/// ボーン追従
BoneConnected = 0,
/// 物理演算
Physics = 1,
/// 物理演算(Bone位置合せ)
ConnectedPhysics = 2
};
/// 剛体
class PmdRigidBody
{
public:
/// 名前
std::string name;
/// 関連ボーン番号
uint16_t related_bone_index;
/// グループ番号
uint8_t group_index;
/// マスク
uint16_t mask;
/// 形状
RigidBodyShape shape;
/// 大きさ
float size[3];
/// 位置
float position[3];
/// 回転
float orientation[3];
/// 質量
float weight;
/// 移動ダンピング
float linear_damping;
/// 回転ダンピング
float anglar_damping;
/// 反発係数
float restitution;
/// 摩擦係数
float friction;
/// 演算方法
RigidBodyType rigid_type;
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, sizeof(char) * 20);
name = (std::string(buffer));
stream->read((char*) &related_bone_index, sizeof(uint16_t));
stream->read((char*) &group_index, sizeof(uint8_t));
stream->read((char*) &mask, sizeof(uint16_t));
stream->read((char*) &shape, sizeof(uint8_t));
stream->read((char*) size, sizeof(float) * 3);
stream->read((char*) position, sizeof(float) * 3);
stream->read((char*) orientation, sizeof(float) * 3);
stream->read((char*) &weight, sizeof(float));
stream->read((char*) &linear_damping, sizeof(float));
stream->read((char*) &anglar_damping, sizeof(float));
stream->read((char*) &restitution, sizeof(float));
stream->read((char*) &friction, sizeof(float));
stream->read((char*) &rigid_type, sizeof(char));
}
};
/// 剛体の拘束
class PmdConstraint
{
public:
/// 名前
std::string name;
/// 剛体Aのインデックス
uint32_t rigid_body_index_a;
/// 剛体Bのインデックス
uint32_t rigid_body_index_b;
/// 位置
float position[3];
/// 回転
float orientation[3];
/// 最小移動制限
float linear_lower_limit[3];
/// 最大移動制限
float linear_upper_limit[3];
/// 最小回転制限
float angular_lower_limit[3];
/// 最大回転制限
float angular_upper_limit[3];
/// 移動に対する復元力
float linear_stiffness[3];
/// 回転に対する復元力
float angular_stiffness[3];
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name = std::string(buffer);
stream->read((char *) &rigid_body_index_a, sizeof(uint32_t));
stream->read((char *) &rigid_body_index_b, sizeof(uint32_t));
stream->read((char *) position, sizeof(float) * 3);
stream->read((char *) orientation, sizeof(float) * 3);
stream->read((char *) linear_lower_limit, sizeof(float) * 3);
stream->read((char *) linear_upper_limit, sizeof(float) * 3);
stream->read((char *) angular_lower_limit, sizeof(float) * 3);
stream->read((char *) angular_upper_limit, sizeof(float) * 3);
stream->read((char *) linear_stiffness, sizeof(float) * 3);
stream->read((char *) angular_stiffness, sizeof(float) * 3);
}
};
/// PMDモデル
class PmdModel
{
public:
float version;
PmdHeader header;
std::vector<PmdVertex> vertices;
std::vector<uint16_t> indices;
std::vector<PmdMaterial> materials;
std::vector<PmdBone> bones;
std::vector<PmdIk> iks;
std::vector<PmdFace> faces;
std::vector<uint16_t> faces_indices;
std::vector<PmdBoneDispName> bone_disp_name;
std::vector<PmdBoneDisp> bone_disp;
std::vector<std::string> toon_filenames;
std::vector<PmdRigidBody> rigid_bodies;
std::vector<PmdConstraint> constraints;
static std::unique_ptr<PmdModel> LoadFromFile(const char *filename)
{
std::ifstream stream(filename, std::ios::binary);
if (stream.fail())
{
std::cerr << "could not open \"" << filename << "\"" << std::endl;
return nullptr;
}
auto result = LoadFromStream(&stream);
stream.close();
return result;
}
/// ファイルからPmdModelを生成する
static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream)
{
auto result = mmd::make_unique<PmdModel>();
char buffer[100];
// magic
char magic[3];
stream->read(magic, 3);
if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd')
{
std::cerr << "invalid file" << std::endl;
return nullptr;
}
// version
stream->read((char*) &(result->version), sizeof(float));
if (result ->version != 1.0f)
{
std::cerr << "invalid version" << std::endl;
return nullptr;
}
// header
result->header.Read(stream);
// vertices
uint32_t vertex_num;
stream->read((char*) &vertex_num, sizeof(uint32_t));
result->vertices.resize(vertex_num);
for (uint32_t i = 0; i < vertex_num; i++)
{
result->vertices[i].Read(stream);
}
// indices
uint32_t index_num;
stream->read((char*) &index_num, sizeof(uint32_t));
result->indices.resize(index_num);
for (uint32_t i = 0; i < index_num; i++)
{
stream->read((char*) &result->indices[i], sizeof(uint16_t));
}
// materials
uint32_t material_num;
stream->read((char*) &material_num, sizeof(uint32_t));
result->materials.resize(material_num);
for (uint32_t i = 0; i < material_num; i++)
{
result->materials[i].Read(stream);
}
// bones
uint16_t bone_num;
stream->read((char*) &bone_num, sizeof(uint16_t));
result->bones.resize(bone_num);
for (uint32_t i = 0; i < bone_num; i++)
{
result->bones[i].Read(stream);
}
// iks
uint16_t ik_num;
stream->read((char*) &ik_num, sizeof(uint16_t));
result->iks.resize(ik_num);
for (uint32_t i = 0; i < ik_num; i++)
{
result->iks[i].Read(stream);
}
// faces
uint16_t face_num;
stream->read((char*) &face_num, sizeof(uint16_t));
result->faces.resize(face_num);
for (uint32_t i = 0; i < face_num; i++)
{
result->faces[i].Read(stream);
}
// face frames
uint8_t face_frame_num;
stream->read((char*) &face_frame_num, sizeof(uint8_t));
result->faces_indices.resize(face_frame_num);
for (uint32_t i = 0; i < face_frame_num; i++)
{
stream->read((char*) &result->faces_indices[i], sizeof(uint16_t));
}
// bone names
uint8_t bone_disp_num;
stream->read((char*) &bone_disp_num, sizeof(uint8_t));
result->bone_disp_name.resize(bone_disp_num);
for (uint32_t i = 0; i < bone_disp_num; i++)
{
result->bone_disp_name[i].Read(stream);
}
// bone frame
uint32_t bone_frame_num;
stream->read((char*) &bone_frame_num, sizeof(uint32_t));
result->bone_disp.resize(bone_frame_num);
for (uint32_t i = 0; i < bone_frame_num; i++)
{
result->bone_disp[i].Read(stream);
}
// english name
bool english;
stream->read((char*) &english, sizeof(char));
if (english)
{
result->header.ReadExtension(stream);
for (uint32_t i = 0; i < bone_num; i++)
{
result->bones[i].ReadExpantion(stream);
}
for (uint32_t i = 0; i < face_num; i++)
{
if (result->faces[i].type == pmd::FaceCategory::Base)
{
continue;
}
result->faces[i].ReadExpantion(stream);
}
for (uint32_t i = 0; i < result->bone_disp_name.size(); i++)
{
result->bone_disp_name[i].ReadExpantion(stream);
}
}
// toon textures
if (stream->peek() == std::ios::traits_type::eof())
{
result->toon_filenames.clear();
}
else {
result->toon_filenames.resize(10);
for (uint32_t i = 0; i < 10; i++)
{
stream->read(buffer, 100);
result->toon_filenames[i] = std::string(buffer);
}
}
// physics
if (stream->peek() == std::ios::traits_type::eof())
{
result->rigid_bodies.clear();
result->constraints.clear();
}
else {
uint32_t rigid_body_num;
stream->read((char*) &rigid_body_num, sizeof(uint32_t));
result->rigid_bodies.resize(rigid_body_num);
for (uint32_t i = 0; i < rigid_body_num; i++)
{
result->rigid_bodies[i].Read(stream);
}
uint32_t constraint_num;
stream->read((char*) &constraint_num, sizeof(uint32_t));
result->constraints.resize(constraint_num);
for (uint32_t i = 0; i < constraint_num; i++)
{
result->constraints[i].Read(stream);
}
}
if (stream->peek() != std::ios::traits_type::eof())
{
std::cerr << "there is unknown data" << std::endl;
}
return result;
}
};
}

View File

@ -0,0 +1,608 @@
#include <utility>
#include "MMDPmxParser.h"
#include "Exceptional.h"
#include "../contrib/ConvertUTF/ConvertUTF.h"
namespace pmx
{
/// インデックス値を読み込む
int ReadIndex(std::istream *stream, int size)
{
switch (size)
{
case 1:
uint8_t tmp8;
stream->read((char*) &tmp8, sizeof(uint8_t));
if (255 == tmp8)
{
return -1;
}
else {
return (int) tmp8;
}
case 2:
uint16_t tmp16;
stream->read((char*) &tmp16, sizeof(uint16_t));
if (65535 == tmp16)
{
return -1;
}
else {
return (int) tmp16;
}
case 4:
int tmp32;
stream->read((char*) &tmp32, sizeof(int));
return tmp32;
default:
return -1;
}
}
/// 文字列を読み込む
std::string ReadString(std::istream *stream, uint8_t encoding)
{
int size;
stream->read((char*) &size, sizeof(int));
std::vector<char> buffer;
if (size == 0)
{
return std::string("");
}
buffer.reserve(size);
stream->read((char*) buffer.data(), size);
if (encoding == 0)
{
// UTF16 to UTF8
std::string result;
const char* sourceStart = buffer.data();
const unsigned int targetSize = size * 3; // enough to encode
char* targetStart = new char[targetSize]();
const char* targetReserved = targetStart;
ConversionFlags flags = ConversionFlags::lenientConversion;
ConversionResult conversionResult;
if( ( conversionResult = ConvertUTF16toUTF8(
(const UTF16**)&sourceStart, (const UTF16*)(sourceStart + size),
(UTF8**)&targetStart, (UTF8*)(targetStart + targetSize),
flags) ) != ConversionResult::conversionOK) {
throw DeadlyImportError( "Convert " + std::string(sourceStart) + " to UTF8 is not valid." );
}
result.assign(targetReserved, targetStart - targetReserved);
delete[] targetReserved;
return result;
}
else
{
// the name is already UTF8
return std::string((const char*)buffer.data(), size);
}
}
void PmxSetting::Read(std::istream *stream)
{
uint8_t count;
stream->read((char*) &count, sizeof(uint8_t));
if (count < 8)
{
throw;
}
stream->read((char*) &encoding, sizeof(uint8_t));
stream->read((char*) &uv, sizeof(uint8_t));
stream->read((char*) &vertex_index_size, sizeof(uint8_t));
stream->read((char*) &texture_index_size, sizeof(uint8_t));
stream->read((char*) &material_index_size, sizeof(uint8_t));
stream->read((char*) &bone_index_size, sizeof(uint8_t));
stream->read((char*) &morph_index_size, sizeof(uint8_t));
stream->read((char*) &rigidbody_index_size, sizeof(uint8_t));
uint8_t temp;
for (int i = 8; i < count; i++)
{
stream->read((char*)&temp, sizeof(uint8_t));
}
}
void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_index = ReadIndex(stream, setting->bone_index_size);
}
void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->bone_weight, sizeof(float));
}
void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
this->bone_index3 = ReadIndex(stream, setting->bone_index_size);
this->bone_index4 = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->bone_weight1, sizeof(float));
stream->read((char*) &this->bone_weight2, sizeof(float));
stream->read((char*) &this->bone_weight3, sizeof(float));
stream->read((char*) &this->bone_weight4, sizeof(float));
}
void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->bone_weight, sizeof(float));
stream->read((char*) this->sdef_c, sizeof(float) * 3);
stream->read((char*) this->sdef_r0, sizeof(float) * 3);
stream->read((char*) this->sdef_r1, sizeof(float) * 3);
}
void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
this->bone_index3 = ReadIndex(stream, setting->bone_index_size);
this->bone_index4 = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->bone_weight1, sizeof(float));
stream->read((char*) &this->bone_weight2, sizeof(float));
stream->read((char*) &this->bone_weight3, sizeof(float));
stream->read((char*) &this->bone_weight4, sizeof(float));
}
void PmxVertex::Read(std::istream *stream, PmxSetting *setting)
{
stream->read((char*) this->position, sizeof(float) * 3);
stream->read((char*) this->normal, sizeof(float) * 3);
stream->read((char*) this->uv, sizeof(float) * 2);
for (int i = 0; i < setting->uv; ++i)
{
stream->read((char*) this->uva[i], sizeof(float) * 4);
}
stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType));
switch (this->skinning_type)
{
case PmxVertexSkinningType::BDEF1:
this->skinning = mmd::make_unique<PmxVertexSkinningBDEF1>();
break;
case PmxVertexSkinningType::BDEF2:
this->skinning = mmd::make_unique<PmxVertexSkinningBDEF2>();
break;
case PmxVertexSkinningType::BDEF4:
this->skinning = mmd::make_unique<PmxVertexSkinningBDEF4>();
break;
case PmxVertexSkinningType::SDEF:
this->skinning = mmd::make_unique<PmxVertexSkinningSDEF>();
break;
case PmxVertexSkinningType::QDEF:
this->skinning = mmd::make_unique<PmxVertexSkinningQDEF>();
break;
default:
throw "invalid skinning type";
}
this->skinning->Read(stream, setting);
stream->read((char*) &this->edge, sizeof(float));
}
void PmxMaterial::Read(std::istream *stream, PmxSetting *setting)
{
this->material_name = std::move(ReadString(stream, setting->encoding));
this->material_english_name = std::move(ReadString(stream, setting->encoding));
stream->read((char*) this->diffuse, sizeof(float) * 4);
stream->read((char*) this->specular, sizeof(float) * 3);
stream->read((char*) &this->specularlity, sizeof(float));
stream->read((char*) this->ambient, sizeof(float) * 3);
stream->read((char*) &this->flag, sizeof(uint8_t));
stream->read((char*) this->edge_color, sizeof(float) * 4);
stream->read((char*) &this->edge_size, sizeof(float));
this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size);
this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size);
stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t));
stream->read((char*) &this->common_toon_flag, sizeof(uint8_t));
if (this->common_toon_flag)
{
stream->read((char*) &this->toon_texture_index, sizeof(uint8_t));
}
else {
this->toon_texture_index = ReadIndex(stream, setting->texture_index_size);
}
this->memo = std::move(ReadString(stream, setting->encoding));
stream->read((char*) &this->index_count, sizeof(int));
}
void PmxIkLink::Read(std::istream *stream, PmxSetting *setting)
{
this->link_target = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->angle_lock, sizeof(uint8_t));
if (angle_lock == 1)
{
stream->read((char*) this->max_radian, sizeof(float) * 3);
stream->read((char*) this->min_radian, sizeof(float) * 3);
}
}
void PmxBone::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_name = std::move(ReadString(stream, setting->encoding));
this->bone_english_name = std::move(ReadString(stream, setting->encoding));
stream->read((char*) this->position, sizeof(float) * 3);
this->parent_index = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->level, sizeof(int));
stream->read((char*) &this->bone_flag, sizeof(uint16_t));
if (this->bone_flag & 0x0001) {
this->target_index = ReadIndex(stream, setting->bone_index_size);
}
else {
stream->read((char*)this->offset, sizeof(float) * 3);
}
if (this->bone_flag & (0x0100 | 0x0200)) {
this->grant_parent_index = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->grant_weight, sizeof(float));
}
if (this->bone_flag & 0x0400) {
stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3);
}
if (this->bone_flag & 0x0800) {
stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3);
stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3);
}
if (this->bone_flag & 0x2000) {
stream->read((char*) &this->key, sizeof(int));
}
if (this->bone_flag & 0x0020) {
this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &ik_loop, sizeof(int));
stream->read((char*) &ik_loop_angle_limit, sizeof(float));
stream->read((char*) &ik_link_count, sizeof(int));
this->ik_links = mmd::make_unique<PmxIkLink []>(ik_link_count);
for (int i = 0; i < ik_link_count; i++) {
ik_links[i].Read(stream, setting);
}
}
}
void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->vertex_index = ReadIndex(stream, setting->vertex_index_size);
stream->read((char*)this->position_offset, sizeof(float) * 3);
}
void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->vertex_index = ReadIndex(stream, setting->vertex_index_size);
stream->read((char*)this->uv_offset, sizeof(float) * 4);
}
void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->bone_index = ReadIndex(stream, setting->bone_index_size);
stream->read((char*)this->translation, sizeof(float) * 3);
stream->read((char*)this->rotation, sizeof(float) * 4);
}
void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->material_index = ReadIndex(stream, setting->material_index_size);
stream->read((char*) &this->offset_operation, sizeof(uint8_t));
stream->read((char*)this->diffuse, sizeof(float) * 4);
stream->read((char*)this->specular, sizeof(float) * 3);
stream->read((char*) &this->specularity, sizeof(float));
stream->read((char*)this->ambient, sizeof(float) * 3);
stream->read((char*)this->edge_color, sizeof(float) * 4);
stream->read((char*) &this->edge_size, sizeof(float));
stream->read((char*)this->texture_argb, sizeof(float) * 4);
stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4);
stream->read((char*)this->toon_texture_argb, sizeof(float) * 4);
}
void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->morph_index = ReadIndex(stream, setting->morph_index_size);
stream->read((char*) &this->morph_weight, sizeof(float));
}
void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->morph_index = ReadIndex(stream, setting->morph_index_size);
stream->read((char*) &this->morph_value, sizeof(float));
}
void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting)
{
this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size);
stream->read((char*) &this->is_local, sizeof(uint8_t));
stream->read((char*)this->velocity, sizeof(float) * 3);
stream->read((char*)this->angular_torque, sizeof(float) * 3);
}
void PmxMorph::Read(std::istream *stream, PmxSetting *setting)
{
this->morph_name = ReadString(stream, setting->encoding);
this->morph_english_name = ReadString(stream, setting->encoding);
stream->read((char*) &category, sizeof(MorphCategory));
stream->read((char*) &morph_type, sizeof(MorphType));
stream->read((char*) &this->offset_count, sizeof(int));
switch (this->morph_type)
{
case MorphType::Group:
group_offsets = mmd::make_unique<PmxMorphGroupOffset []>(this->offset_count);
for (int i = 0; i < offset_count; i++)
{
group_offsets[i].Read(stream, setting);
}
break;
case MorphType::Vertex:
vertex_offsets = mmd::make_unique<PmxMorphVertexOffset []>(this->offset_count);
for (int i = 0; i < offset_count; i++)
{
vertex_offsets[i].Read(stream, setting);
}
break;
case MorphType::Bone:
bone_offsets = mmd::make_unique<PmxMorphBoneOffset []>(this->offset_count);
for (int i = 0; i < offset_count; i++)
{
bone_offsets[i].Read(stream, setting);
}
break;
case MorphType::Matrial:
material_offsets = mmd::make_unique<PmxMorphMaterialOffset []>(this->offset_count);
for (int i = 0; i < offset_count; i++)
{
material_offsets[i].Read(stream, setting);
}
break;
case MorphType::UV:
case MorphType::AdditionalUV1:
case MorphType::AdditionalUV2:
case MorphType::AdditionalUV3:
case MorphType::AdditionalUV4:
uv_offsets = mmd::make_unique<PmxMorphUVOffset []>(this->offset_count);
for (int i = 0; i < offset_count; i++)
{
uv_offsets[i].Read(stream, setting);
}
break;
default:
throw;
}
}
void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting)
{
stream->read((char*) &this->element_target, sizeof(uint8_t));
if (this->element_target == 0x00)
{
this->index = ReadIndex(stream, setting->bone_index_size);
}
else {
this->index = ReadIndex(stream, setting->morph_index_size);
}
}
void PmxFrame::Read(std::istream *stream, PmxSetting *setting)
{
this->frame_name = ReadString(stream, setting->encoding);
this->frame_english_name = ReadString(stream, setting->encoding);
stream->read((char*) &this->frame_flag, sizeof(uint8_t));
stream->read((char*) &this->element_count, sizeof(int));
this->elements = mmd::make_unique<PmxFrameElement []>(this->element_count);
for (int i = 0; i < this->element_count; i++)
{
this->elements[i].Read(stream, setting);
}
}
void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting)
{
this->girid_body_name = ReadString(stream, setting->encoding);
this->girid_body_english_name = ReadString(stream, setting->encoding);
this->target_bone = ReadIndex(stream, setting->bone_index_size);
stream->read((char*) &this->group, sizeof(uint8_t));
stream->read((char*) &this->mask, sizeof(uint16_t));
stream->read((char*) &this->shape, sizeof(uint8_t));
stream->read((char*) this->size, sizeof(float) * 3);
stream->read((char*) this->position, sizeof(float) * 3);
stream->read((char*) this->orientation, sizeof(float) * 3);
stream->read((char*) &this->mass, sizeof(float));
stream->read((char*) &this->move_attenuation, sizeof(float));
stream->read((char*) &this->rotation_attenuation, sizeof(float));
stream->read((char*) &this->repulsion, sizeof(float));
stream->read((char*) &this->friction, sizeof(float));
stream->read((char*) &this->physics_calc_type, sizeof(uint8_t));
}
void PmxJointParam::Read(std::istream *stream, PmxSetting *setting)
{
this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size);
this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size);
stream->read((char*) this->position, sizeof(float) * 3);
stream->read((char*) this->orientaiton, sizeof(float) * 3);
stream->read((char*) this->move_limitation_min, sizeof(float) * 3);
stream->read((char*) this->move_limitation_max, sizeof(float) * 3);
stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3);
stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3);
stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3);
stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3);
}
void PmxJoint::Read(std::istream *stream, PmxSetting *setting)
{
this->joint_name = ReadString(stream, setting->encoding);
this->joint_english_name = ReadString(stream, setting->encoding);
stream->read((char*) &this->joint_type, sizeof(uint8_t));
this->param.Read(stream, setting);
}
void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting)
{
this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size);
this->related_vertex = ReadIndex(stream, setting->vertex_index_size);
stream->read((char*) &this->is_near, sizeof(uint8_t));
}
void PmxSoftBody::Read(std::istream *stream, PmxSetting *setting)
{
// 未実装
std::cerr << "Not Implemented Exception" << std::endl;
throw;
}
void PmxModel::Init()
{
this->version = 0.0f;
this->model_name.clear();
this->model_english_name.clear();
this->model_comment.clear();
this->model_english_comment.clear();
this->vertex_count = 0;
this->vertices = nullptr;
this->index_count = 0;
this->indices = nullptr;
this->texture_count = 0;
this->textures = nullptr;
this->material_count = 0;
this->materials = nullptr;
this->bone_count = 0;
this->bones = nullptr;
this->morph_count = 0;
this->morphs = nullptr;
this->frame_count = 0;
this->frames = nullptr;
this->rigid_body_count = 0;
this->rigid_bodies = nullptr;
this->joint_count = 0;
this->joints = nullptr;
this->soft_body_count = 0;
this->soft_bodies = nullptr;
}
void PmxModel::Read(std::istream *stream)
{
// マジック
char magic[4];
stream->read((char*) magic, sizeof(char) * 4);
if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20)
{
std::cerr << "invalid magic number." << std::endl;
throw;
}
// バージョン
stream->read((char*) &version, sizeof(float));
if (version != 2.0f && version != 2.1f)
{
std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
throw;
}
// ファイル設定
this->setting.Read(stream);
// モデル情報
this->model_name = std::move(ReadString(stream, setting.encoding));
this->model_english_name = std::move(ReadString(stream, setting.encoding));
this->model_comment = std::move(ReadString(stream, setting.encoding));
this->model_english_comment = std::move(ReadString(stream, setting.encoding));
// 頂点
stream->read((char*) &vertex_count, sizeof(int));
this->vertices = mmd::make_unique<PmxVertex []>(vertex_count);
for (int i = 0; i < vertex_count; i++)
{
vertices[i].Read(stream, &setting);
}
// 面
stream->read((char*) &index_count, sizeof(int));
this->indices = mmd::make_unique<int []>(index_count);
for (int i = 0; i < index_count; i++)
{
this->indices[i] = ReadIndex(stream, setting.vertex_index_size);
}
// テクスチャ
stream->read((char*) &texture_count, sizeof(int));
this->textures = mmd::make_unique<std::string []>(texture_count);
for (int i = 0; i < texture_count; i++)
{
this->textures[i] = ReadString(stream, setting.encoding);
}
// マテリアル
stream->read((char*) &material_count, sizeof(int));
this->materials = mmd::make_unique<PmxMaterial []>(material_count);
for (int i = 0; i < material_count; i++)
{
this->materials[i].Read(stream, &setting);
}
// ボーン
stream->read((char*) &this->bone_count, sizeof(int));
this->bones = mmd::make_unique<PmxBone []>(this->bone_count);
for (int i = 0; i < this->bone_count; i++)
{
this->bones[i].Read(stream, &setting);
}
// モーフ
stream->read((char*) &this->morph_count, sizeof(int));
this->morphs = mmd::make_unique<PmxMorph []>(this->morph_count);
for (int i = 0; i < this->morph_count; i++)
{
this->morphs[i].Read(stream, &setting);
}
// 表示枠
stream->read((char*) &this->frame_count, sizeof(int));
this->frames = mmd::make_unique<PmxFrame []>(this->frame_count);
for (int i = 0; i < this->frame_count; i++)
{
this->frames[i].Read(stream, &setting);
}
// 剛体
stream->read((char*) &this->rigid_body_count, sizeof(int));
this->rigid_bodies = mmd::make_unique<PmxRigidBody []>(this->rigid_body_count);
for (int i = 0; i < this->rigid_body_count; i++)
{
this->rigid_bodies[i].Read(stream, &setting);
}
// ジョイント
stream->read((char*) &this->joint_count, sizeof(int));
this->joints = mmd::make_unique<PmxJoint []>(this->joint_count);
for (int i = 0; i < this->joint_count; i++)
{
this->joints[i].Read(stream, &setting);
}
//// ソフトボディ
//if (this->version == 2.1f)
//{
// stream->read((char*) &this->soft_body_count, sizeof(int));
// this->soft_bodies = mmd::make_unique<PmxSoftBody []>(this->soft_body_count);
// for (int i = 0; i < this->soft_body_count; i++)
// {
// this->soft_bodies[i].Read(stream, &setting);
// }
//}
}
//std::unique_ptr<PmxModel> ReadFromFile(const char *filename)
//{
// auto stream = std::ifstream(filename, std::ios_base::binary);
// auto pmx = PmxModel::ReadFromStream(&stream);
// if (!stream.eof())
// {
// std::cerr << "don't reach the end of file." << std::endl;
// }
// stream.close();
// return pmx;
//}
//std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream)
//{
// auto pmx = mmd::make_unique<PmxModel>();
// pmx->Read(stream);
// return pmx;
//}
}

860
code/MMDPmxParser.h 100644
View File

@ -0,0 +1,860 @@
#pragma once
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <memory>
#include "MMDCpp14.h"
namespace pmx
{
/// インデックス設定
class PmxSetting
{
public:
PmxSetting()
: encoding(0)
, uv(0)
, vertex_index_size(0)
, texture_index_size(0)
, material_index_size(0)
, bone_index_size(0)
, morph_index_size(0)
, rigidbody_index_size(0)
{}
/// エンコード方式
uint8_t encoding;
/// 追加UV数
uint8_t uv;
/// 頂点インデックスサイズ
uint8_t vertex_index_size;
/// テクスチャインデックスサイズ
uint8_t texture_index_size;
/// マテリアルインデックスサイズ
uint8_t material_index_size;
/// ボーンインデックスサイズ
uint8_t bone_index_size;
/// モーフインデックスサイズ
uint8_t morph_index_size;
/// 剛体インデックスサイズ
uint8_t rigidbody_index_size;
void Read(std::istream *stream);
};
/// 頂点スキニングタイプ
enum class PmxVertexSkinningType : uint8_t
{
BDEF1 = 0,
BDEF2 = 1,
BDEF4 = 2,
SDEF = 3,
QDEF = 4,
};
/// 頂点スキニング
class PmxVertexSkinning
{
public:
virtual void Read(std::istream *stream, PmxSetting *setting) = 0;
};
class PmxVertexSkinningBDEF1 : public PmxVertexSkinning
{
public:
PmxVertexSkinningBDEF1()
: bone_index(0)
{}
int bone_index;
void Read(std::istream *stresam, PmxSetting *setting);
};
class PmxVertexSkinningBDEF2 : public PmxVertexSkinning
{
public:
PmxVertexSkinningBDEF2()
: bone_index1(0)
, bone_index2(0)
, bone_weight(0.0f)
{}
int bone_index1;
int bone_index2;
float bone_weight;
void Read(std::istream *stresam, PmxSetting *setting);
};
class PmxVertexSkinningBDEF4 : public PmxVertexSkinning
{
public:
PmxVertexSkinningBDEF4()
: bone_index1(0)
, bone_index2(0)
, bone_index3(0)
, bone_index4(0)
, bone_weight1(0.0f)
, bone_weight2(0.0f)
, bone_weight3(0.0f)
, bone_weight4(0.0f)
{}
int bone_index1;
int bone_index2;
int bone_index3;
int bone_index4;
float bone_weight1;
float bone_weight2;
float bone_weight3;
float bone_weight4;
void Read(std::istream *stresam, PmxSetting *setting);
};
class PmxVertexSkinningSDEF : public PmxVertexSkinning
{
public:
PmxVertexSkinningSDEF()
: bone_index1(0)
, bone_index2(0)
, bone_weight(0.0f)
{
for (int i = 0; i < 3; ++i) {
sdef_c[i] = 0.0f;
sdef_r0[i] = 0.0f;
sdef_r1[i] = 0.0f;
}
}
int bone_index1;
int bone_index2;
float bone_weight;
float sdef_c[3];
float sdef_r0[3];
float sdef_r1[3];
void Read(std::istream *stresam, PmxSetting *setting);
};
class PmxVertexSkinningQDEF : public PmxVertexSkinning
{
public:
PmxVertexSkinningQDEF()
: bone_index1(0)
, bone_index2(0)
, bone_index3(0)
, bone_index4(0)
, bone_weight1(0.0f)
, bone_weight2(0.0f)
, bone_weight3(0.0f)
, bone_weight4(0.0f)
{}
int bone_index1;
int bone_index2;
int bone_index3;
int bone_index4;
float bone_weight1;
float bone_weight2;
float bone_weight3;
float bone_weight4;
void Read(std::istream *stresam, PmxSetting *setting);
};
/// 頂点
class PmxVertex
{
public:
PmxVertex()
: edge(0.0f)
{
uv[0] = uv[1] = 0.0f;
for (int i = 0; i < 3; ++i) {
position[i] = 0.0f;
normal[i] = 0.0f;
}
for (int i = 0; i < 4; ++i) {
for (int k = 0; k < 4; ++k) {
uva[i][k] = 0.0f;
}
}
}
/// 位置
float position[3];
/// 法線
float normal[3];
/// テクスチャ座標
float uv[2];
/// 追加テクスチャ座標
float uva[4][4];
/// スキニングタイプ
PmxVertexSkinningType skinning_type;
/// スキニング
std::unique_ptr<PmxVertexSkinning> skinning;
/// エッジ倍率
float edge;
void Read(std::istream *stream, PmxSetting *setting);
};
/// マテリアル
class PmxMaterial
{
public:
PmxMaterial()
: specularlity(0.0f)
, flag(0)
, edge_size(0.0f)
, diffuse_texture_index(0)
, sphere_texture_index(0)
, sphere_op_mode(0)
, common_toon_flag(0)
, toon_texture_index(0)
, index_count(0)
{
for (int i = 0; i < 3; ++i) {
specular[i] = 0.0f;
ambient[i] = 0.0f;
edge_color[i] = 0.0f;
}
for (int i = 0; i < 4; ++i) {
diffuse[i] = 0.0f;
}
}
/// モデル名
std::string material_name;
/// モデル英名
std::string material_english_name;
/// 減衰色
float diffuse[4];
/// 光沢色
float specular[3];
/// 光沢度
float specularlity;
/// 環境色
float ambient[3];
/// 描画フラグ
uint8_t flag;
/// エッジ色
float edge_color[4];
/// エッジサイズ
float edge_size;
/// アルベドテクスチャインデックス
int diffuse_texture_index;
/// スフィアテクスチャインデックス
int sphere_texture_index;
/// スフィアテクスチャ演算モード
uint8_t sphere_op_mode;
/// 共有トゥーンフラグ
uint8_t common_toon_flag;
/// トゥーンテクスチャインデックス
int toon_texture_index;
/// メモ
std::string memo;
/// 頂点インデックス数
int index_count;
void Read(std::istream *stream, PmxSetting *setting);
};
/// リンク
class PmxIkLink
{
public:
PmxIkLink()
: link_target(0)
, angle_lock(0)
{
for (int i = 0; i < 3; ++i) {
max_radian[i] = 0.0f;
min_radian[i] = 0.0f;
}
}
/// リンクボーンインデックス
int link_target;
/// 角度制限
uint8_t angle_lock;
/// 最大制限角度
float max_radian[3];
/// 最小制限角度
float min_radian[3];
void Read(std::istream *stream, PmxSetting *settingn);
};
/// ボーン
class PmxBone
{
public:
PmxBone()
: parent_index(0)
, level(0)
, bone_flag(0)
, target_index(0)
, grant_parent_index(0)
, grant_weight(0.0f)
, key(0)
, ik_target_bone_index(0)
, ik_loop(0)
, ik_loop_angle_limit(0.0f)
, ik_link_count(0)
{
for (int i = 0; i < 3; ++i) {
position[i] = 0.0f;
offset[i] = 0.0f;
lock_axis_orientation[i] = 0.0f;
local_axis_x_orientation[i] = 0.0f;
local_axis_y_orientation[i] = 0.0f;
}
}
/// ボーン名
std::string bone_name;
/// ボーン英名
std::string bone_english_name;
/// 位置
float position[3];
/// 親ボーンインデックス
int parent_index;
/// 階層
int level;
/// ボーンフラグ
uint16_t bone_flag;
/// 座標オフセット(has Target)
float offset[3];
/// 接続先ボーンインデックス(not has Target)
int target_index;
/// 付与親ボーンインデックス
int grant_parent_index;
/// 付与率
float grant_weight;
/// 固定軸の方向
float lock_axis_orientation[3];
/// ローカル軸のX軸方向
float local_axis_x_orientation[3];
/// ローカル軸のY軸方向
float local_axis_y_orientation[3];
/// 外部親変形のkey値
int key;
/// IKターゲットボーン
int ik_target_bone_index;
/// IKループ回数
int ik_loop;
/// IKループ計算時の角度制限(ラジアン)
float ik_loop_angle_limit;
/// IKリンク数
int ik_link_count;
/// IKリンク
std::unique_ptr<PmxIkLink []> ik_links;
void Read(std::istream *stream, PmxSetting *setting);
};
enum class MorphType : uint8_t
{
Group = 0,
Vertex = 1,
Bone = 2,
UV = 3,
AdditionalUV1 = 4,
AdditionalUV2 = 5,
AdditionalUV3 = 6,
AdditionalUV4 = 7,
Matrial = 8,
Flip = 9,
Implus = 10,
};
enum class MorphCategory : uint8_t
{
ReservedCategory = 0,
Eyebrow = 1,
Eye = 2,
Mouth = 3,
Other = 4,
};
class PmxMorphOffset
{
public:
void virtual Read(std::istream *stream, PmxSetting *setting) = 0;
};
class PmxMorphVertexOffset : public PmxMorphOffset
{
public:
PmxMorphVertexOffset()
: vertex_index(0)
{
for (int i = 0; i < 3; ++i) {
position_offset[i] = 0.0f;
}
}
int vertex_index;
float position_offset[3];
void Read(std::istream *stream, PmxSetting *setting); //override;
};
class PmxMorphUVOffset : public PmxMorphOffset
{
public:
PmxMorphUVOffset()
: vertex_index(0)
{
for (int i = 0; i < 4; ++i) {
uv_offset[i] = 0.0f;
}
}
int vertex_index;
float uv_offset[4];
void Read(std::istream *stream, PmxSetting *setting); //override;
};
class PmxMorphBoneOffset : public PmxMorphOffset
{
public:
PmxMorphBoneOffset()
: bone_index(0)
{
for (int i = 0; i < 3; ++i) {
translation[i] = 0.0f;
}
for (int i = 0; i < 4; ++i) {
rotation[i] = 0.0f;
}
}
int bone_index;
float translation[3];
float rotation[4];
void Read(std::istream *stream, PmxSetting *setting); //override;
};
class PmxMorphMaterialOffset : public PmxMorphOffset
{
public:
PmxMorphMaterialOffset()
: specularity(0.0f)
, edge_size(0.0f)
{
for (int i = 0; i < 3; ++i) {
specular[i] = 0.0f;
ambient[i] = 0.0f;
}
for (int i = 0; i < 4; ++i) {
diffuse[i] = 0.0f;
edge_color[i] = 0.0f;
texture_argb[i] = 0.0f;
sphere_texture_argb[i] = 0.0f;
toon_texture_argb[i] = 0.0f;
}
}
int material_index;
uint8_t offset_operation;
float diffuse[4];
float specular[3];
float specularity;
float ambient[3];
float edge_color[4];
float edge_size;
float texture_argb[4];
float sphere_texture_argb[4];
float toon_texture_argb[4];
void Read(std::istream *stream, PmxSetting *setting); //override;
};
class PmxMorphGroupOffset : public PmxMorphOffset
{
public:
PmxMorphGroupOffset()
: morph_index(0)
, morph_weight(0.0f)
{}
int morph_index;
float morph_weight;
void Read(std::istream *stream, PmxSetting *setting); //override;
};
class PmxMorphFlipOffset : public PmxMorphOffset
{
public:
PmxMorphFlipOffset()
: morph_index(0)
, morph_value(0.0f)
{}
int morph_index;
float morph_value;
void Read(std::istream *stream, PmxSetting *setting); //override;
};
class PmxMorphImplusOffset : public PmxMorphOffset
{
public:
PmxMorphImplusOffset()
: rigid_body_index(0)
, is_local(0)
{
for (int i = 0; i < 3; ++i) {
velocity[i] = 0.0f;
angular_torque[i] = 0.0f;
}
}
int rigid_body_index;
uint8_t is_local;
float velocity[3];
float angular_torque[3];
void Read(std::istream *stream, PmxSetting *setting); //override;
};
/// モーフ
class PmxMorph
{
public:
PmxMorph()
: offset_count(0)
{
}
/// モーフ名
std::string morph_name;
/// モーフ英名
std::string morph_english_name;
/// カテゴリ
MorphCategory category;
/// モーフタイプ
MorphType morph_type;
/// オフセット数
int offset_count;
/// 頂点モーフ配列
std::unique_ptr<PmxMorphVertexOffset []> vertex_offsets;
/// UVモーフ配列
std::unique_ptr<PmxMorphUVOffset []> uv_offsets;
/// ボーンモーフ配列
std::unique_ptr<PmxMorphBoneOffset []> bone_offsets;
/// マテリアルモーフ配列
std::unique_ptr<PmxMorphMaterialOffset []> material_offsets;
/// グループモーフ配列
std::unique_ptr<PmxMorphGroupOffset []> group_offsets;
/// フリップモーフ配列
std::unique_ptr<PmxMorphFlipOffset []> flip_offsets;
/// インパルスモーフ配列
std::unique_ptr<PmxMorphImplusOffset []> implus_offsets;
void Read(std::istream *stream, PmxSetting *setting);
};
/// 枠内要素
class PmxFrameElement
{
public:
PmxFrameElement()
: element_target(0)
, index(0)
{
}
/// 要素対象
uint8_t element_target;
/// 要素対象インデックス
int index;
void Read(std::istream *stream, PmxSetting *setting);
};
/// 表示枠
class PmxFrame
{
public:
PmxFrame()
: frame_flag(0)
, element_count(0)
{
}
/// 枠名
std::string frame_name;
/// 枠英名
std::string frame_english_name;
/// 特殊枠フラグ
uint8_t frame_flag;
/// 枠内要素数
int element_count;
/// 枠内要素配列
std::unique_ptr<PmxFrameElement []> elements;
void Read(std::istream *stream, PmxSetting *setting);
};
class PmxRigidBody
{
public:
PmxRigidBody()
: target_bone(0)
, group(0)
, mask(0)
, shape(0)
, mass(0.0f)
, move_attenuation(0.0f)
, rotation_attenuation(0.0f)
, repulsion(0.0f)
, friction(0.0f)
, physics_calc_type(0)
{
for (int i = 0; i < 3; ++i) {
size[i] = 0.0f;
position[i] = 0.0f;
orientation[i] = 0.0f;
}
}
/// 剛体名
std::string girid_body_name;
/// 剛体英名
std::string girid_body_english_name;
/// 関連ボーンインデックス
int target_bone;
/// グループ
uint8_t group;
/// マスク
uint16_t mask;
/// 形状
uint8_t shape;
float size[3];
float position[3];
float orientation[3];
float mass;
float move_attenuation;
float rotation_attenuation;
float repulsion;
float friction;
uint8_t physics_calc_type;
void Read(std::istream *stream, PmxSetting *setting);
};
enum class PmxJointType : uint8_t
{
Generic6DofSpring = 0,
Generic6Dof = 1,
Point2Point = 2,
ConeTwist = 3,
Slider = 5,
Hinge = 6
};
class PmxJointParam
{
public:
PmxJointParam()
: rigid_body1(0)
, rigid_body2(0)
{
for (int i = 0; i < 3; ++i) {
position[i] = 0.0f;
orientaiton[i] = 0.0f;
move_limitation_min[i] = 0.0f;
move_limitation_max[i] = 0.0f;
rotation_limitation_min[i] = 0.0f;
rotation_limitation_max[i] = 0.0f;
spring_move_coefficient[i] = 0.0f;
spring_rotation_coefficient[i] = 0.0f;
}
}
int rigid_body1;
int rigid_body2;
float position[3];
float orientaiton[3];
float move_limitation_min[3];
float move_limitation_max[3];
float rotation_limitation_min[3];
float rotation_limitation_max[3];
float spring_move_coefficient[3];
float spring_rotation_coefficient[3];
void Read(std::istream *stream, PmxSetting *setting);
};
class PmxJoint
{
public:
std::string joint_name;
std::string joint_english_name;
PmxJointType joint_type;
PmxJointParam param;
void Read(std::istream *stream, PmxSetting *setting);
};
enum PmxSoftBodyFlag : uint8_t
{
BLink = 0x01,
Cluster = 0x02,
Link = 0x04
};
class PmxAncherRigidBody
{
public:
PmxAncherRigidBody()
: related_rigid_body(0)
, related_vertex(0)
, is_near(false)
{}
int related_rigid_body;
int related_vertex;
bool is_near;
void Read(std::istream *stream, PmxSetting *setting);
};
class PmxSoftBody
{
public:
PmxSoftBody()
: shape(0)
, target_material(0)
, group(0)
, mask(0)
, blink_distance(0)
, cluster_count(0)
, mass(0.0)
, collisioni_margin(0.0)
, aero_model(0)
, VCF(0.0f)
, DP(0.0f)
, DG(0.0f)
, LF(0.0f)
, PR(0.0f)
, VC(0.0f)
, DF(0.0f)
, MT(0.0f)
, CHR(0.0f)
, KHR(0.0f)
, SHR(0.0f)
, AHR(0.0f)
, SRHR_CL(0.0f)
, SKHR_CL(0.0f)
, SSHR_CL(0.0f)
, SR_SPLT_CL(0.0f)
, SK_SPLT_CL(0.0f)
, SS_SPLT_CL(0.0f)
, V_IT(0)
, P_IT(0)
, D_IT(0)
, C_IT(0)
, LST(0.0f)
, AST(0.0f)
, VST(0.0f)
, anchor_count(0)
, pin_vertex_count(0)
{}
std::string soft_body_name;
std::string soft_body_english_name;
uint8_t shape;
int target_material;
uint8_t group;
uint16_t mask;
PmxSoftBodyFlag flag;
int blink_distance;
int cluster_count;
float mass;
float collisioni_margin;
int aero_model;
float VCF;
float DP;
float DG;
float LF;
float PR;
float VC;
float DF;
float MT;
float CHR;
float KHR;
float SHR;
float AHR;
float SRHR_CL;
float SKHR_CL;
float SSHR_CL;
float SR_SPLT_CL;
float SK_SPLT_CL;
float SS_SPLT_CL;
int V_IT;
int P_IT;
int D_IT;
int C_IT;
float LST;
float AST;
float VST;
int anchor_count;
std::unique_ptr<PmxAncherRigidBody []> anchers;
int pin_vertex_count;
std::unique_ptr<int []> pin_vertices;
void Read(std::istream *stream, PmxSetting *setting);
};
/// PMXモデル
class PmxModel
{
public:
PmxModel()
: version(0.0f)
, vertex_count(0)
, index_count(0)
, texture_count(0)
, material_count(0)
, bone_count(0)
, morph_count(0)
, frame_count(0)
, rigid_body_count(0)
, joint_count(0)
, soft_body_count(0)
{}
/// バージョン
float version;
/// 設定
PmxSetting setting;
/// モデル名
std::string model_name;
/// モデル英名
std::string model_english_name;
/// コメント
std::string model_comment;
/// 英語コメント
std::string model_english_comment;
/// 頂点数
int vertex_count;
/// 頂点配列
std::unique_ptr<PmxVertex []> vertices;
/// インデックス数
int index_count;
/// インデックス配列
std::unique_ptr<int []> indices;
/// テクスチャ数
int texture_count;
/// テクスチャ配列
std::unique_ptr< std::string []> textures;
/// マテリアル数
int material_count;
/// マテリアル
std::unique_ptr<PmxMaterial []> materials;
/// ボーン数
int bone_count;
/// ボーン配列
std::unique_ptr<PmxBone []> bones;
/// モーフ数
int morph_count;
/// モーフ配列
std::unique_ptr<PmxMorph []> morphs;
/// 表示枠数
int frame_count;
/// 表示枠配列
std::unique_ptr<PmxFrame [] > frames;
/// 剛体数
int rigid_body_count;
/// 剛体配列
std::unique_ptr<PmxRigidBody []> rigid_bodies;
/// ジョイント数
int joint_count;
/// ジョイント配列
std::unique_ptr<PmxJoint []> joints;
/// ソフトボディ数
int soft_body_count;
/// ソフトボディ配列
std::unique_ptr<PmxSoftBody []> soft_bodies;
/// モデル初期化
void Init();
/// モデル読み込み
void Read(std::istream *stream);
///// ファイルからモデルの読み込み
//static std::unique_ptr<PmxModel> ReadFromFile(const char *filename);
///// 入力ストリームからモデルの読み込み
//static std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream);
};
}

367
code/MMDVmdParser.h 100644
View File

@ -0,0 +1,367 @@
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <iostream>
#include <fstream>
#include <ostream>
#include "MMDCpp14.h"
namespace vmd
{
/// ボーンフレーム
class VmdBoneFrame
{
public:
/// ボーン名
std::string name;
/// フレーム番号
int frame;
/// 位置
float position[3];
/// 回転
float orientation[4];
/// 補間曲線
char interpolation[4][4][4];
void Read(std::istream* stream)
{
char buffer[15];
stream->read((char*) buffer, sizeof(char)*15);
name = std::string(buffer);
stream->read((char*) &frame, sizeof(int));
stream->read((char*) position, sizeof(float)*3);
stream->read((char*) orientation, sizeof(float)*4);
stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4);
}
void Write(std::ostream* stream)
{
stream->write((char*)name.c_str(), sizeof(char) * 15);
stream->write((char*)&frame, sizeof(int));
stream->write((char*)position, sizeof(float) * 3);
stream->write((char*)orientation, sizeof(float) * 4);
stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4);
}
};
/// 表情フレーム
class VmdFaceFrame
{
public:
/// 表情名
std::string face_name;
/// 表情の重み
float weight;
/// フレーム番号
uint32_t frame;
void Read(std::istream* stream)
{
char buffer[15];
stream->read((char*) &buffer, sizeof(char) * 15);
face_name = std::string(buffer);
stream->read((char*) &frame, sizeof(int));
stream->read((char*) &weight, sizeof(float));
}
void Write(std::ostream* stream)
{
stream->write((char*)face_name.c_str(), sizeof(char) * 15);
stream->write((char*)&frame, sizeof(int));
stream->write((char*)&weight, sizeof(float));
}
};
/// カメラフレーム
class VmdCameraFrame
{
public:
/// フレーム番号
int frame;
/// 距離
float distance;
/// 位置
float position[3];
/// 回転
float orientation[3];
/// 補間曲線
char interpolation[6][4];
/// 視野角
float angle;
/// 不明データ
char unknown[3];
void Read(std::istream *stream)
{
stream->read((char*) &frame, sizeof(int));
stream->read((char*) &distance, sizeof(float));
stream->read((char*) position, sizeof(float) * 3);
stream->read((char*) orientation, sizeof(float) * 3);
stream->read((char*) interpolation, sizeof(char) * 24);
stream->read((char*) &angle, sizeof(float));
stream->read((char*) unknown, sizeof(char) * 3);
}
void Write(std::ostream *stream)
{
stream->write((char*)&frame, sizeof(int));
stream->write((char*)&distance, sizeof(float));
stream->write((char*)position, sizeof(float) * 3);
stream->write((char*)orientation, sizeof(float) * 3);
stream->write((char*)interpolation, sizeof(char) * 24);
stream->write((char*)&angle, sizeof(float));
stream->write((char*)unknown, sizeof(char) * 3);
}
};
/// ライトフレーム
class VmdLightFrame
{
public:
/// フレーム番号
int frame;
/// 色
float color[3];
/// 位置
float position[3];
void Read(std::istream* stream)
{
stream->read((char*) &frame, sizeof(int));
stream->read((char*) color, sizeof(float) * 3);
stream->read((char*) position, sizeof(float) * 3);
}
void Write(std::ostream* stream)
{
stream->write((char*)&frame, sizeof(int));
stream->write((char*)color, sizeof(float) * 3);
stream->write((char*)position, sizeof(float) * 3);
}
};
/// IKの有効無効
class VmdIkEnable
{
public:
std::string ik_name;
bool enable;
};
/// IKフレーム
class VmdIkFrame
{
public:
int frame;
bool display;
std::vector<VmdIkEnable> ik_enable;
void Read(std::istream *stream)
{
char buffer[20];
stream->read((char*) &frame, sizeof(int));
stream->read((char*) &display, sizeof(uint8_t));
int ik_count;
stream->read((char*) &ik_count, sizeof(int));
ik_enable.resize(ik_count);
for (int i = 0; i < ik_count; i++)
{
stream->read(buffer, 20);
ik_enable[i].ik_name = std::string(buffer);
stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t));
}
}
void Write(std::ostream *stream)
{
stream->write((char*)&frame, sizeof(int));
stream->write((char*)&display, sizeof(uint8_t));
int ik_count = static_cast<int>(ik_enable.size());
stream->write((char*)&ik_count, sizeof(int));
for (int i = 0; i < ik_count; i++)
{
const VmdIkEnable& ik_enable = this->ik_enable.at(i);
stream->write(ik_enable.ik_name.c_str(), 20);
stream->write((char*)&ik_enable.enable, sizeof(uint8_t));
}
}
};
/// VMDモーション
class VmdMotion
{
public:
/// モデル名
std::string model_name;
/// バージョン
int version;
/// ボーンフレーム
std::vector<VmdBoneFrame> bone_frames;
/// 表情フレーム
std::vector<VmdFaceFrame> face_frames;
/// カメラフレーム
std::vector<VmdCameraFrame> camera_frames;
/// ライトフレーム
std::vector<VmdLightFrame> light_frames;
/// IKフレーム
std::vector<VmdIkFrame> ik_frames;
static std::unique_ptr<VmdMotion> LoadFromFile(char const *filename)
{
std::ifstream stream(filename, std::ios::binary);
auto result = LoadFromStream(&stream);
stream.close();
return result;
}
static std::unique_ptr<VmdMotion> LoadFromStream(std::ifstream *stream)
{
char buffer[30];
auto result = mmd::make_unique<VmdMotion>();
// magic and version
stream->read((char*) buffer, 30);
if (strncmp(buffer, "Vocaloid Motion Data", 20))
{
std::cerr << "invalid vmd file." << std::endl;
return nullptr;
}
result->version = std::atoi(buffer + 20);
// name
stream->read(buffer, 20);
result->model_name = std::string(buffer);
// bone frames
int bone_frame_num;
stream->read((char*) &bone_frame_num, sizeof(int));
result->bone_frames.resize(bone_frame_num);
for (int i = 0; i < bone_frame_num; i++)
{
result->bone_frames[i].Read(stream);
}
// face frames
int face_frame_num;
stream->read((char*) &face_frame_num, sizeof(int));
result->face_frames.resize(face_frame_num);
for (int i = 0; i < face_frame_num; i++)
{
result->face_frames[i].Read(stream);
}
// camera frames
int camera_frame_num;
stream->read((char*) &camera_frame_num, sizeof(int));
result->camera_frames.resize(camera_frame_num);
for (int i = 0; i < camera_frame_num; i++)
{
result->camera_frames[i].Read(stream);
}
// light frames
int light_frame_num;
stream->read((char*) &light_frame_num, sizeof(int));
result->light_frames.resize(light_frame_num);
for (int i = 0; i < light_frame_num; i++)
{
result->light_frames[i].Read(stream);
}
// unknown2
stream->read(buffer, 4);
// ik frames
if (stream->peek() != std::ios::traits_type::eof())
{
int ik_num;
stream->read((char*) &ik_num, sizeof(int));
result->ik_frames.resize(ik_num);
for (int i = 0; i < ik_num; i++)
{
result->ik_frames[i].Read(stream);
}
}
if (stream->peek() != std::ios::traits_type::eof())
{
std::cerr << "vmd stream has unknown data." << std::endl;
}
return result;
}
bool SaveToFile(const std::u16string& filename)
{
// TODO: How to adapt u16string to string?
/*
std::ofstream stream(filename.c_str(), std::ios::binary);
auto result = SaveToStream(&stream);
stream.close();
return result;
*/
return false;
}
bool SaveToStream(std::ofstream *stream)
{
std::string magic = "Vocaloid Motion Data 0002\0";
magic.resize(30);
// magic and version
stream->write(magic.c_str(), 30);
// name
stream->write(model_name.c_str(), 20);
// bone frames
const int bone_frame_num = static_cast<int>(bone_frames.size());
stream->write(reinterpret_cast<const char*>(&bone_frame_num), sizeof(int));
for (int i = 0; i < bone_frame_num; i++)
{
bone_frames[i].Write(stream);
}
// face frames
const int face_frame_num = static_cast<int>(face_frames.size());
stream->write(reinterpret_cast<const char*>(&face_frame_num), sizeof(int));
for (int i = 0; i < face_frame_num; i++)
{
face_frames[i].Write(stream);
}
// camera frames
const int camera_frame_num = static_cast<int>(camera_frames.size());
stream->write(reinterpret_cast<const char*>(&camera_frame_num), sizeof(int));
for (int i = 0; i < camera_frame_num; i++)
{
camera_frames[i].Write(stream);
}
// light frames
const int light_frame_num = static_cast<int>(light_frames.size());
stream->write(reinterpret_cast<const char*>(&light_frame_num), sizeof(int));
for (int i = 0; i < light_frame_num; i++)
{
light_frames[i].Write(stream);
}
// self shadow datas
const int self_shadow_num = 0;
stream->write(reinterpret_cast<const char*>(&self_shadow_num), sizeof(int));
// ik frames
const int ik_num = static_cast<int>(ik_frames.size());
stream->write(reinterpret_cast<const char*>(&ik_num), sizeof(int));
for (int i = 0; i < ik_num; i++)
{
ik_frames[i].Write(stream);
}
return true;
}
};
}

View File

@ -122,14 +122,17 @@ std::string ObjExporter :: GetMaterialLibName()
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::string ObjExporter :: GetMaterialLibFileName() std::string ObjExporter::GetMaterialLibFileName() {
{ // Remove existing .obj file extention so that the final material file name will be fileName.mtl and not fileName.obj.mtl
size_t lastdot = filename.find_last_of('.');
if (lastdot != std::string::npos)
return filename.substr(0, lastdot) + MaterialExt;
return filename + MaterialExt; return filename + MaterialExt;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ObjExporter :: WriteHeader(std::ostringstream& out) void ObjExporter :: WriteHeader(std::ostringstream& out) {
{
out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl; out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl;
out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl << endl; out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl << endl;
} }

View File

@ -105,7 +105,7 @@ private:
void AddNode(const aiNode* nd, const aiMatrix4x4& mParent); void AddNode(const aiNode* nd, const aiMatrix4x4& mParent);
private: private:
const std::string filename; std::string filename;
const aiScene* const pScene; const aiScene* const pScene;
std::vector<aiVector3D> vp, vn, vt; std::vector<aiVector3D> vp, vn, vt;

View File

@ -113,4 +113,4 @@ private:
} // Namespace Assimp } // Namespace Assimp
#endif #endif // OBJFILEMTLIMPORTER_H_INC

View File

@ -57,8 +57,17 @@ namespace Assimp {
const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
// ------------------------------------------------------------------- ObjFileParser::ObjFileParser()
// Constructor with loaded data and directories. : m_DataIt()
, m_DataItEnd()
, m_pModel( NULL )
, m_uiLine( 0 )
, m_pIO( nullptr )
, m_progress( nullptr )
, m_originalObjFileName( "" ) {
// empty
}
ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName, ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName,
IOSystem *io, ProgressHandler* progress, IOSystem *io, ProgressHandler* progress,
const std::string &originalObjFileName) : const std::string &originalObjFileName) :
@ -86,18 +95,20 @@ ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::str
parseFile( streamBuffer ); parseFile( streamBuffer );
} }
// -------------------------------------------------------------------
// Destructor
ObjFileParser::~ObjFileParser() { ObjFileParser::~ObjFileParser() {
delete m_pModel; delete m_pModel;
m_pModel = NULL; m_pModel = NULL;
} }
// ------------------------------------------------------------------- void ObjFileParser::setBuffer( std::vector<char> &buffer ) {
// Returns a pointer to the model instance. m_DataIt = buffer.begin();
m_DataItEnd = buffer.end();
}
ObjFile::Model *ObjFileParser::GetModel() const { ObjFile::Model *ObjFileParser::GetModel() const {
return m_pModel; return m_pModel;
} }
void ignoreNewLines(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer) void ignoreNewLines(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer)
{ {
auto curPosition = buffer.begin(); auto curPosition = buffer.begin();
@ -119,8 +130,7 @@ void ignoreNewLines(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffe
} }
} while (*curPosition!='\n'); } while (*curPosition!='\n');
} }
// -------------------------------------------------------------------
// File parsing method.
void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) { void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
// only update every 100KB or it'll be too slow // only update every 100KB or it'll be too slow
//const unsigned int updateProgressEveryBytes = 100 * 1024; //const unsigned int updateProgressEveryBytes = 100 * 1024;
@ -144,7 +154,7 @@ void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
progressCounter++; progressCounter++;
m_progress->UpdateFileRead( progressOffset + processed * 2, progressTotal ); m_progress->UpdateFileRead( progressOffset + processed * 2, progressTotal );
} }
ignoreNewLines(streamBuffer, buffer); //ignoreNewLines(streamBuffer, buffer);
// parse line // parse line
switch (*m_DataIt) { switch (*m_DataIt) {
case 'v': // Parse a vertex texture coordinate case 'v': // Parse a vertex texture coordinate
@ -243,11 +253,14 @@ pf_skip_line:
} }
} }
// -------------------------------------------------------------------
// Copy the next word in a temporary buffer
void ObjFileParser::copyNextWord(char *pBuffer, size_t length) { void ObjFileParser::copyNextWord(char *pBuffer, size_t length) {
size_t index = 0; size_t index = 0;
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd); m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
if ( *m_DataIt == '\\' ) {
m_DataIt++;
m_DataIt++;
m_DataIt = getNextWord<DataArrayIt>( m_DataIt, m_DataItEnd );
}
while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) { while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
pBuffer[index] = *m_DataIt; pBuffer[index] = *m_DataIt;
index++; index++;
@ -300,8 +313,6 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
} }
// -------------------------------------------------------------------
// Get values for a new 3D vector instance
void ObjFileParser::getVector3( std::vector<aiVector3D> &point3d_array ) { void ObjFileParser::getVector3( std::vector<aiVector3D> &point3d_array ) {
ai_real x, y, z; ai_real x, y, z;
copyNextWord(m_buffer, Buffersize); copyNextWord(m_buffer, Buffersize);

View File

@ -65,7 +65,7 @@ class ProgressHandler;
/// \class ObjFileParser /// \class ObjFileParser
/// \brief Parser for a obj waveform file /// \brief Parser for a obj waveform file
class ObjFileParser { class ASSIMP_API ObjFileParser {
public: public:
static const size_t Buffersize = 4096; static const size_t Buffersize = 4096;
typedef std::vector<char> DataArray; typedef std::vector<char> DataArray;
@ -73,14 +73,18 @@ public:
typedef std::vector<char>::const_iterator ConstDataArrayIt; typedef std::vector<char>::const_iterator ConstDataArrayIt;
public: public:
/// \brief Constructor with data array. /// @brief The default constructor.
ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &strModelName, IOSystem* io, ProgressHandler* progress, const std::string &originalObjFileName); ObjFileParser();
/// \brief Destructor /// @brief Constructor with data array.
ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName, IOSystem* io, ProgressHandler* progress, const std::string &originalObjFileName);
/// @brief Destructor
~ObjFileParser(); ~ObjFileParser();
/// \brief Model getter. /// @brief If you want to load in-core data.
void setBuffer( std::vector<char> &buffer );
/// @brief Model getter.
ObjFile::Model *GetModel() const; ObjFile::Model *GetModel() const;
private: protected:
/// Parse the loaded file /// Parse the loaded file
void parseFile( IOStreamBuffer<char> &streamBuffer ); void parseFile( IOStreamBuffer<char> &streamBuffer );
/// Method to copy the new delimited word in the current line. /// Method to copy the new delimited word in the current line.

View File

@ -79,8 +79,10 @@ inline Char_T getNextWord( Char_T pBuffer, Char_T pEnd )
{ {
while ( !isEndOfBuffer( pBuffer, pEnd ) ) while ( !isEndOfBuffer( pBuffer, pEnd ) )
{ {
if( !IsSpaceOrNewLine( *pBuffer ) || IsLineEnd( *pBuffer ) ) if ( !IsSpaceOrNewLine( *pBuffer ) || IsLineEnd( *pBuffer ) ) {
//if ( *pBuffer != '\\' )
break; break;
}
pBuffer++; pBuffer++;
} }
return pBuffer; return pBuffer;

View File

@ -196,7 +196,6 @@ namespace Grammar {
return NoneType; return NoneType;
} }
} // Namespace Grammar } // Namespace Grammar
namespace Assimp { namespace Assimp {
@ -523,6 +522,10 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) {
if ( !objRefNames.empty() ) { if ( !objRefNames.empty() ) {
m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ); m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) );
} }
} else if ( m_tokenType == Grammar::LightNodeToken ) {
// TODO!
} else if ( m_tokenType == Grammar::CameraNodeToken ) {
// TODO!
} }
} }
@ -602,6 +605,13 @@ void OpenGEXImporter::handleCameraObject( ODDLParser::DDLNode *node, aiScene *pS
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
void OpenGEXImporter::handleLightObject( ODDLParser::DDLNode *node, aiScene *pScene ) { void OpenGEXImporter::handleLightObject( ODDLParser::DDLNode *node, aiScene *pScene ) {
aiLight *light( new aiLight );
m_lightCache.push_back( light );
std::string objName = node->getName();
if ( !objName.empty() ) {
light->mName.Set( objName );
}
m_currentLight = light;
Property *prop( node->findPropertyByName( "type" ) ); Property *prop( node->findPropertyByName( "type" ) );
if ( nullptr != prop ) { if ( nullptr != prop ) {
@ -917,7 +927,7 @@ void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene *
} }
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
static void getColorRGB( aiColor3D *pColor, DataArrayList *colList ) { static void getColorRGB3( aiColor3D *pColor, DataArrayList *colList ) {
if( nullptr == pColor || nullptr == colList ) { if( nullptr == pColor || nullptr == colList ) {
return; return;
} }
@ -931,6 +941,23 @@ static void getColorRGB( aiColor3D *pColor, DataArrayList *colList ) {
pColor->b = val->getFloat(); pColor->b = val->getFloat();
} }
//------------------------------------------------------------------------------------------------
static void getColorRGB4( aiColor4D *pColor, DataArrayList *colList ) {
if ( nullptr == pColor || nullptr == colList ) {
return;
}
ai_assert( 4 == colList->m_numItems );
Value *val( colList->m_dataList );
pColor->r = val->getFloat();
val = val->getNext();
pColor->g = val->getFloat();
val = val->getNext();
pColor->b = val->getFloat();
val = val->getNext();
pColor->a = val->getFloat();
}
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
enum ColorType { enum ColorType {
NoneColor = 0, NoneColor = 0,
@ -981,7 +1008,17 @@ void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene *pScen
return; return;
} }
aiColor3D col; aiColor3D col;
getColorRGB( &col, colList ); if ( 3 == colList->m_numItems ) {
aiColor3D col3;
getColorRGB3( &col3, colList );
col = col3;
} else {
aiColor4D col4;
getColorRGB4( &col4, colList );
col.r = col4.r;
col.g = col4.g;
col.b = col4.b;
}
const ColorType colType( getColorType( prop->m_key ) ); const ColorType colType( getColorType( prop->m_key ) );
if( DiffuseColor == colType ) { if( DiffuseColor == colType ) {
m_currentMaterial->AddProperty( &col, 1, AI_MATKEY_COLOR_DIFFUSE ); m_currentMaterial->AddProperty( &col, 1, AI_MATKEY_COLOR_DIFFUSE );

View File

@ -132,7 +132,6 @@ protected:
void copyMeshes( aiScene *pScene ); void copyMeshes( aiScene *pScene );
void copyCameras( aiScene *pScene ); void copyCameras( aiScene *pScene );
void copyLights( aiScene *pScene ); void copyLights( aiScene *pScene );
void resolveReferences(); void resolveReferences();
void pushNode( aiNode *node, aiScene *pScene ); void pushNode( aiNode *node, aiScene *pScene );
aiNode *popNode(); aiNode *popNode();

View File

@ -306,7 +306,7 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
if (!pMesh->mColors[i])break; if (!pMesh->mColors[i])break;
if (configDeleteFlags & aiComponent_COLORSn(i) || b) if (configDeleteFlags & aiComponent_COLORSn(i) || b)
{ {
delete pMesh->mColors[i]; delete [] pMesh->mColors[i];
pMesh->mColors[i] = NULL; pMesh->mColors[i] = NULL;
ret = true; ret = true;

View File

@ -241,7 +241,7 @@ void X3DImporter::XML_CheckNode_MustBeEmpty()
void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName) void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
{ {
const size_t Uns_Skip_Len = 189; static const size_t Uns_Skip_Len = 190;
const char* Uns_Skip[ Uns_Skip_Len ] = { const char* Uns_Skip[ Uns_Skip_Len ] = {
// CAD geometry component // CAD geometry component
"CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet", "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
@ -268,7 +268,7 @@ void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeNa
"PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D", "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
"SplineScalarInterpolator", "SquadOrientationInterpolator", "SplineScalarInterpolator", "SquadOrientationInterpolator",
// Key device sensor component // Key device sensor component
"KeySensor", "StringSensor" "KeySensor", "StringSensor",
// Layering component // Layering component
"Layer", "LayerSet", "Viewport", "Layer", "LayerSet", "Viewport",
// Layout component // Layout component

View File

@ -504,6 +504,9 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
// copy additional information from children // copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{ {
if ( nullptr == *pMesh ) {
break;
}
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value,tnemesh.ColorPerVertex); MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value,tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)

View File

@ -3,7 +3,7 @@
Open Asset Import Library (assimp) Open Asset Import Library (assimp)
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
Copyright (c) 2006-2012, assimp team Copyright (c) 2006-2017, assimp team
All rights reserved. All rights reserved.

View File

@ -472,22 +472,9 @@ Just derivate your own logger from the abstract base class LogStream and overwri
@code @code
// Example stream // Example stream
class myStream : class myStream : public LogStream
public LogStream
{ {
public: public:
// Constructor
myStream()
{
// empty
}
// Destructor
~myStream()
{
// empty
}
// Write womethink using your own functionality // Write womethink using your own functionality
void write(const char* message) void write(const char* message)
{ {
@ -496,10 +483,10 @@ public:
}; };
// Select the kinds of messages you want to receive on this log stream // Select the kinds of messages you want to receive on this log stream
const unsigned int severity = Logger::DEBUGGING|Logger::INFO|Logger::ERR|Logger::WARN; const unsigned int severity = Logger::Debugging|Logger::Info|Logger::Err|Logger::Warn;
// Attaching it to the default logger // Attaching it to the default logger
Assimp::DefaultLogger::get()->attachStream( new myStream(), severity ); Assimp::DefaultLogger::get()->attachStream( new myStream, severity );
@endcode @endcode
@ -512,10 +499,10 @@ flag set:
@code @code
unsigned int severity = 0; unsigned int severity = 0;
severity |= Logger::DEBUGGING; severity |= Logger::Debugging;
// Detach debug messages from you self defined stream // Detach debug messages from you self defined stream
Assimp::DefaultLogger::get()->attachStream( new myStream(), severity ); Assimp::DefaultLogger::get()->attachStream( new myStream, severity );
@endcode @endcode
@ -743,6 +730,8 @@ need them at all.
Normally textures used by assets are stored in separate files, however, Normally textures used by assets are stored in separate files, however,
there are file formats embedding their textures directly into the model file. there are file formats embedding their textures directly into the model file.
Such textures are loaded into an aiTexture structure. Such textures are loaded into an aiTexture structure.
For embedded textures, the value of `AI_MATKEY_TEXTURE(textureType, index)` will be `*<index>` where
`<index>` is the index of the texture in aiScene::mTextures.
<br> <br>
There are two cases: There are two cases:
<br> <br>
@ -930,7 +919,7 @@ All material key constants start with 'AI_MATKEY' (it's an ugly macro for histor
<td><tt>TEXTURE(t,n)</tt></td> <td><tt>TEXTURE(t,n)</tt></td>
<td>aiString</td> <td>aiString</td>
<td>n/a</td> <td>n/a</td>
<td>Defines the path to the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' is one of the #aiTextureType enumerated values.</td> <td>Defines the path of the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' is one of the #aiTextureType enumerated values. Either a filepath or `*<index>`, where `<index>` is the index of an embedded texture in aiScene::mTextures.</td>
<td>See the 'Textures' section above.</td> <td>See the 'Textures' section above.</td>
</tr> </tr>

View File

@ -38,7 +38,6 @@
#---------------------------------------------------------------------- #----------------------------------------------------------------------
cmake_minimum_required( VERSION 2.6 ) cmake_minimum_required( VERSION 2.6 )
#INCLUDE( AddGTest )
include( CTest ) include( CTest )
enable_testing() enable_testing()
@ -107,11 +106,14 @@ SET( TEST_SRCS
unit/utSIBImporter.cpp unit/utSIBImporter.cpp
unit/utObjImportExport.cpp unit/utObjImportExport.cpp
unit/utObjTools.cpp unit/utObjTools.cpp
unit/utOpenGEXImportExport.cpp
unit/utPretransformVertices.cpp unit/utPretransformVertices.cpp
unit/utPLYImportExport.cpp unit/utPLYImportExport.cpp
unit/utPMXImporter.cpp
unit/utRemoveComments.cpp unit/utRemoveComments.cpp
unit/utRemoveComponent.cpp unit/utRemoveComponent.cpp
unit/utRemoveRedundantMaterials.cpp unit/utRemoveRedundantMaterials.cpp
unit/utRemoveVCProcess.cpp
unit/utScenePreprocessor.cpp unit/utScenePreprocessor.cpp
unit/utSharedPPData.cpp unit/utSharedPPData.cpp
unit/utStringUtils.cpp unit/utStringUtils.cpp

Binary file not shown.

View File

@ -0,0 +1,49 @@
————————————————————————
ニコニ立体公式キャラクター「ニコニ立体ちゃん」
http://3d.nicovideo.jp/alicia/
version 4
©dwango, inc. All rights reserved.
————————————————————————
この度は、ニコニ立体公式キャラクター「ニコニ立体ちゃん」をダウンロードいただきましてありがとうございます。
◯ 内容物
1. MikuMikuDance(MMD)用ファイル2種 (ニコニ立体ちゃん本体・ビーム彫刻刀)
2. MMD用モーションファイル16種 (ニコファーレのモーションキャプチャーデバイスを用いて収録)
2. FBXファイル2種 (MMD用データ、Unity用データ) + MAXファイル(オリジナルデータ)
3. Unity Package1種
4. ニコニ立体ちゃん壁紙1種
◯よくある質問
* 「ニコニ立体ちゃん」と「アリシア・ソリッド」、正式名称はどちら?
キャラクターの本名はアリシア・ソリッドですが、クレジット等での表記はニコニ立体ちゃんが正式名称となります。
作品発表の際には「ニコニ立体ちゃん」タグをご活用ください。
* ニコニ立体ちゃんを用いた、年齢制限のある二次創作物の公開は可能なのか?
利用規約にある禁止事項に抵触しない限りにおいて可能です。
◯ 利用規約抜粋
必ず利用規約( http://3d.nicovideo.jp/alicia/rule.html )をご確認ください。
利用規約の改訂などによって下記抜粋と利用規約の内容が異なる場合は利用規約が優先されます。
* 非営利、営利に関わらず個人(同人サークルなど、法人を除く団体を含む)の利用が可能です。
* 二次創作物について株式会社ドワンゴ(以下、当社)のクレジットを表記する必要はありません。
* 改変物を配布することができます。
* niconico内でキャラクターやモーションを利用した作品を投稿する場合は、コンテンツツリーの親作品にキャラクター( http://3d.nicovideo.jp/works/td14712 )を登録するよう努めるものとします。
◯禁止事項
* 第三者の知的財産権その他一切の権利及び名誉を侵害しないこと。
* 当社(当社が提供するサービス等を含む)及び当社キャラクターの名誉・品位傷つける行為をしないこと。
* 公序良俗に反する行為や目的、暴力的な表現、反社会的な行為や目的、特定の信条や宗教、政治的発言のため利用しないこと。
* 当社の公式商品であるかのような誤解を招く利用をしないこと。
* その他、当社が不適切と判断する行為に利用しないこと。
◯ クレジット
企画: 株式会社ドワンゴ
キャラクターデザイン: 黒星紅白
モデリング: 雨刻

View File

@ -0,0 +1,21 @@
LightObject (type = "infinite") {
Param (attrib = "intensity") {
float { 3.0 }
}
Color (attrib = "light") {
float[3] { { 0.7, 1.0, 0.1 } }
}
}
LightObject (type = "point") {
Param (attrib = "intensity") {
float { 0.5 }
}
}
LightObject (type = "spot") {
Color (attrib = "light") {
float[4] { { 0.1, 0.0, 0.1, 1.0 } }
}
}

View File

@ -41,6 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "UnitTestPCH.h" #include "UnitTestPCH.h"
#include "ObjTools.h" #include "ObjTools.h"
#include "ObjFileParser.h"
using namespace ::Assimp; using namespace ::Assimp;
@ -48,6 +49,15 @@ class utObjTools : public ::testing::Test {
// empty // empty
}; };
class TestObjFileParser : public ObjFileParser {
public:
TestObjFileParser() : ObjFileParser(){}
~TestObjFileParser() {}
void testCopyNextWord( char *pBuffer, size_t length ) {
copyNextWord( pBuffer, length );
}
};
TEST_F( utObjTools, skipDataLine_OneLine_Success ) { TEST_F( utObjTools, skipDataLine_OneLine_Success ) {
std::vector<char> buffer; std::vector<char> buffer;
std::string data( "v -0.5 -0.5 0.5\nend" ); std::string data( "v -0.5 -0.5 0.5\nend" );
@ -60,6 +70,24 @@ TEST_F( utObjTools, skipDataLine_OneLine_Success ) {
} }
TEST_F( utObjTools, skipDataLine_TwoLines_Success ) { TEST_F( utObjTools, skipDataLine_TwoLines_Success ) {
TestObjFileParser test_parser;
std::string data( "vn -2.061493116917992e-15 -0.9009688496589661 \\n-0.4338837265968323" ); std::string data( "vn -2.061493116917992e-15 -0.9009688496589661 \\n-0.4338837265968323" );
std::vector<char> buffer;
buffer.resize( data.size() );
::memcpy( &buffer[ 0 ], &data[ 0 ], data.size() );
test_parser.setBuffer( buffer );
static const size_t Size = 4096UL;
char data_buffer[ Size ];
test_parser.testCopyNextWord( data_buffer, Size );
EXPECT_EQ( 0, strncmp( data_buffer, "vn", 2 ) );
test_parser.testCopyNextWord( data_buffer, Size );
EXPECT_EQ( data_buffer[0], '-' );
test_parser.testCopyNextWord( data_buffer, Size );
EXPECT_EQ( data_buffer[0], '-' );
test_parser.testCopyNextWord( data_buffer, Size );
EXPECT_EQ( data_buffer[ 0 ], '-' );
} }

View File

@ -0,0 +1,70 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2017, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#include "UnitTestPCH.h"
#include "AbstractImportExportBase.h"
#include <assimp/Importer.hpp>
using namespace Assimp;
class utOpenGEXImportExport : public AbstractImportExportBase {
public:
virtual bool importerTest() {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OpenGEX/Example.ogex", 0 );
return nullptr != scene;
return true;
}
};
TEST_F( utOpenGEXImportExport, importLWSFromFileTest ) {
EXPECT_TRUE( importerTest() );
}
TEST_F( utOpenGEXImportExport, Importissue1262_NoCrash ) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OpenGEX/light_issue1262.ogex", 0 );
}

View File

@ -0,0 +1,62 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2016, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#include "UnitTestPCH.h"
#include "SceneDiffer.h"
#include "AbstractImportExportBase.h"
#include "MMDImporter.h"
#include <assimp/Importer.hpp>
using namespace ::Assimp;
class utPMXImporter : public AbstractImportExportBase {
public:
virtual bool importerTest() {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/MMD/Alicia_blade.pmx", 0 );
return nullptr != scene;
}
};
TEST_F( utPMXImporter, importTest ) {
EXPECT_TRUE( importerTest() );
}

View File

@ -0,0 +1,75 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2017, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#include "UnitTestPCH.h"
#include "code/RemoveVCProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
using namespace ::Assimp;
class utRevmoveVCProcess : public ::testing::Test {
// empty
};
TEST_F( utRevmoveVCProcess, createTest ) {
bool ok = true;
try {
RemoveVCProcess *process = new RemoveVCProcess;
delete process;
} catch ( ... ) {
ok = false;
}
EXPECT_TRUE( ok );
}
TEST_F( utRevmoveVCProcess, issue1266_ProcessMeshTest_NoCrash ) {
aiScene *scene = new aiScene;
scene->mNumMeshes = 1;
scene->mMeshes = new aiMesh*[ 1 ];
aiMesh *mesh = new aiMesh;
mesh->mNumVertices = 1;
mesh->mColors[ 0 ] = new aiColor4D[ 2 ];
scene->mMeshes[ 0 ] = mesh;
RemoveVCProcess *process = new RemoveVCProcess;
process->Execute( scene );
}

View File

@ -1,5 +1,5 @@
project(assimp_qt_viewer)
set(PROJECT_VERSION "") set(PROJECT_VERSION "")
project(assimp_qt_viewer)
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)

View File

@ -295,7 +295,7 @@ aiReturn rv;
// begin export // begin export
time_begin = QTime::currentTime(); time_begin = QTime::currentTime();
rv = exporter.Export(mScene, format_id.toLocal8Bit(), filename.toLocal8Bit()); rv = exporter.Export(mScene, format_id.toLocal8Bit(), filename.toLocal8Bit(), aiProcess_FlipUVs);
ui->lblExportTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime()))); ui->lblExportTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime())));
if(rv == aiReturn_SUCCESS) if(rv == aiReturn_SUCCESS)
LogInfo("Export done: " + filename); LogInfo("Export done: " + filename);