Merge with feature-export-assimp
--HG-- branch : develop
This commit is contained in:
commit
6c2490090d
22 changed files with 1951 additions and 1 deletions
19
.editorconfig
Normal file
19
.editorconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
; Top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
; 4-column tab indentation
|
||||
[*.cpp]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.c]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.h]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
|
@ -147,6 +147,10 @@ IF(WITH_QT)
|
|||
FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui QtXml QtOpenGL REQUIRED)
|
||||
ENDIF(WITH_QT)
|
||||
|
||||
IF(WITH_ASSIMP)
|
||||
FIND_PACKAGE(assimp REQUIRED)
|
||||
ENDIF(WITH_ASSIMP)
|
||||
|
||||
IF(WITH_NEL)
|
||||
IF(WITH_NEL_TESTS)
|
||||
FIND_PACKAGE(CppTest)
|
||||
|
|
25
code/CMakeModules/Findassimp.cmake
Normal file
25
code/CMakeModules/Findassimp.cmake
Normal file
|
@ -0,0 +1,25 @@
|
|||
FIND_PATH(
|
||||
assimp_INCLUDE_DIRS
|
||||
NAMES assimp/postprocess.h assimp/scene.h assimp/version.h assimp/config.h assimp/cimport.h
|
||||
PATHS /usr/local/include/
|
||||
)
|
||||
|
||||
FIND_LIBRARY(
|
||||
assimp_LIBRARIES
|
||||
NAMES assimp
|
||||
PATHS /usr/local/lib/
|
||||
)
|
||||
|
||||
IF (assimp_INCLUDE_DIRS AND assimp_LIBRARIES)
|
||||
SET(assimp_FOUND TRUE)
|
||||
ENDIF (assimp_INCLUDE_DIRS AND assimp_LIBRARIES)
|
||||
|
||||
IF (assimp_FOUND)
|
||||
IF (NOT assimp_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found asset importer library: ${assimp_LIBRARIES}")
|
||||
ENDIF (NOT assimp_FIND_QUIETLY)
|
||||
ELSE (assimp_FOUND)
|
||||
IF (assimp_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find asset importer library")
|
||||
ENDIF (assimp_FIND_REQUIRED)
|
||||
ENDIF (assimp_FOUND)
|
|
@ -274,6 +274,8 @@ MACRO(NL_SETUP_DEFAULT_OPTIONS)
|
|||
OPTION(WITH_STATIC_EXTERNAL "With static external libraries" OFF)
|
||||
OPTION(WITH_INSTALL_LIBRARIES "Install development files." ON )
|
||||
|
||||
OPTION(WITH_ASSIMP "Use assimp exporter" OFF)
|
||||
|
||||
###
|
||||
# GUI toolkits
|
||||
###
|
||||
|
|
210
code/nel/include/nel/misc/tool_logger.h
Normal file
210
code/nel/include/nel/misc/tool_logger.h
Normal file
|
@ -0,0 +1,210 @@
|
|||
/**
|
||||
* \file tool_logger.h
|
||||
* \brief CToolLogger
|
||||
* \date 2012-02-19 10:33GMT
|
||||
* \author Jan Boon (Kaetemi)
|
||||
* Tool logger is fully implemented in header so small tools do not
|
||||
* need to link to this library unnecessarily.
|
||||
* NOTE: Needs to be changed not to use time_nl and string_common.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 by authors
|
||||
*
|
||||
* This file is part of RYZOM CORE PIPELINE.
|
||||
* RYZOM CORE PIPELINE is free software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* RYZOM CORE PIPELINE is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RYZOM CORE PIPELINE; see the file COPYING. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NLMISC_TOOL_LOGGER_H
|
||||
#define NLMISC_TOOL_LOGGER_H
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
// STL includes
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// NeL includes
|
||||
#include <nel/misc/time_nl.h>
|
||||
#include <nel/misc/string_common.h>
|
||||
|
||||
// Project includes
|
||||
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
|
||||
#ifdef NL_DEBUG_H
|
||||
#define tlerror(toolLogger, path, error, ...) nlwarning(error, __VA_ARGS__), toolLogger.writeError(NLMISC::ERROR, path, error, __VA_ARGS__)
|
||||
#define tlwarning(toolLogger, path, error, ...) nlwarning(error, __VA_ARGS__), toolLogger.writeError(NLMISC::WARNING, path, error, __VA_ARGS__)
|
||||
#define tlmessage(toolLogger, path, error, ...) nlinfo(error, __VA_ARGS__), toolLogger.writeError(NLMISC::MESSAGE, path, error, __VA_ARGS__)
|
||||
#else
|
||||
#define tlerror(toolLogger, path, error, ...) toolLogger.writeError(NLMISC::ERROR, path, error, __VA_ARGS__)
|
||||
#define tlwarning(toolLogger, path, error, ...) toolLogger.writeError(NLMISC::WARNING, path, error, __VA_ARGS__)
|
||||
#define tlmessage(toolLogger, path, error, ...) toolLogger.writeError(NLMISC::MESSAGE, path, error, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace NLMISC {
|
||||
|
||||
enum TError
|
||||
{
|
||||
ERROR,
|
||||
WARNING,
|
||||
MESSAGE,
|
||||
};
|
||||
|
||||
enum TDepend
|
||||
{
|
||||
BUILD,
|
||||
DIRECTORY,
|
||||
RUNTIME,
|
||||
};
|
||||
|
||||
const std::string s_ErrorHeader = "type\tpath\ttime\terror";
|
||||
const std::string s_DependHeader = "type\toutput_file\tinput_file";
|
||||
|
||||
/**
|
||||
* \brief CToolLogger
|
||||
* \date 2012-02-19 10:33GMT
|
||||
* \author Jan Boon (Kaetemi)
|
||||
* CToolLogger
|
||||
*/
|
||||
class CToolLogger
|
||||
{
|
||||
private:
|
||||
FILE *m_ErrorLog;
|
||||
FILE *m_DependLog;
|
||||
|
||||
public:
|
||||
inline CToolLogger() : m_ErrorLog(NULL), m_DependLog(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline ~CToolLogger()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
inline void initError(const std::string &errorLog)
|
||||
{
|
||||
releaseError();
|
||||
|
||||
m_ErrorLog = fopen(errorLog.c_str(), "wt");
|
||||
fwrite(s_ErrorHeader.c_str(), 1, s_ErrorHeader.length(), m_ErrorLog);
|
||||
fwrite("\n", 1, 1, m_ErrorLog);
|
||||
fflush(m_ErrorLog);
|
||||
|
||||
}
|
||||
|
||||
inline void initDepend(const std::string &dependLog)
|
||||
{
|
||||
releaseDepend();
|
||||
|
||||
m_DependLog = fopen(dependLog.c_str(), "wt");
|
||||
fwrite(s_DependHeader.c_str(), 1, s_DependHeader.length(), m_DependLog);
|
||||
fwrite("\n", 1, 1, m_DependLog);
|
||||
// fflush(m_DependLog);
|
||||
}
|
||||
|
||||
inline void writeError(TError type, const char *path, const char *error, ...)
|
||||
{
|
||||
if (m_ErrorLog)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ERROR:
|
||||
fwrite("ERROR", 1, 5, m_ErrorLog);
|
||||
break;
|
||||
case WARNING:
|
||||
fwrite("WARNING", 1, 7, m_ErrorLog);
|
||||
break;
|
||||
case MESSAGE:
|
||||
fwrite("MESSAGE", 1, 7, m_ErrorLog);
|
||||
break;
|
||||
}
|
||||
fwrite("\t", 1, 1, m_ErrorLog);
|
||||
fprintf(m_ErrorLog, "%s", path);
|
||||
fwrite("\t", 1, 1, m_ErrorLog);
|
||||
std::string time = NLMISC::toString(NLMISC::CTime::getSecondsSince1970());
|
||||
fwrite(time.c_str(), 1, time.length(), m_ErrorLog);
|
||||
fwrite("\t", 1, 1, m_ErrorLog);
|
||||
va_list args;
|
||||
va_start(args, error);
|
||||
vfprintf(m_ErrorLog, error, args);
|
||||
va_end(args);
|
||||
fwrite("\n", 1, 1, m_ErrorLog);
|
||||
fflush(m_ErrorLog);
|
||||
}
|
||||
}
|
||||
|
||||
/// inputFile can only be file. [? May be not-yet-existing file for expected input for future build runs. ?] Directories are handled on process level. [? You should call this before calling writeError on inputFile, so the error is also linked from the outputFile. ?]
|
||||
inline void writeDepend(TDepend type, const char *outputFile, const char *inputFile)
|
||||
{
|
||||
if (m_DependLog)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BUILD:
|
||||
fwrite("BUILD", 1, 5, m_DependLog);
|
||||
break;
|
||||
case DIRECTORY:
|
||||
fwrite("DIRECTORY", 1, 9, m_DependLog);
|
||||
break;
|
||||
case RUNTIME:
|
||||
fwrite("RUNTIME", 1, 7, m_DependLog);
|
||||
break;
|
||||
}
|
||||
fwrite("\t", 1, 1, m_DependLog);
|
||||
fprintf(m_DependLog, "%s", outputFile);
|
||||
fwrite("\t", 1, 1, m_DependLog);
|
||||
fprintf(m_DependLog, "%s", inputFile);
|
||||
fwrite("\n", 1, 1, m_DependLog);
|
||||
// fflush(m_DependLog);
|
||||
}
|
||||
}
|
||||
|
||||
inline void releaseError()
|
||||
{
|
||||
if (m_ErrorLog)
|
||||
{
|
||||
fflush(m_ErrorLog);
|
||||
fclose(m_ErrorLog);
|
||||
m_ErrorLog = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline void releaseDepend()
|
||||
{
|
||||
if (m_DependLog)
|
||||
{
|
||||
fflush(m_DependLog);
|
||||
fclose(m_DependLog);
|
||||
m_DependLog = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline void release()
|
||||
{
|
||||
releaseError();
|
||||
releaseDepend();
|
||||
}
|
||||
}; /* class CToolLogger */
|
||||
|
||||
} /* namespace NLMISC */
|
||||
|
||||
#endif /* #ifndef NLMISC_TOOL_LOGGER_H */
|
||||
|
||||
/* end of file */
|
45
code/nel/src/misc/tool_logger.cpp
Normal file
45
code/nel/src/misc/tool_logger.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* \file tool_logger.cpp
|
||||
* \brief CToolLogger
|
||||
* \date 2012-02-19 10:33GMT
|
||||
* \author Jan Boon (Kaetemi)
|
||||
* CToolLogger
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 by authors
|
||||
*
|
||||
* This file is part of RYZOM CORE PIPELINE.
|
||||
* RYZOM CORE PIPELINE is free software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* RYZOM CORE PIPELINE is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RYZOM CORE PIPELINE; see the file COPYING. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stdmisc.h"
|
||||
#include "nel/misc/tool_logger.h"
|
||||
|
||||
// STL includes
|
||||
|
||||
// NeL includes
|
||||
// #include <nel/misc/debug.h>
|
||||
|
||||
// Project includes
|
||||
|
||||
namespace NLMISC {
|
||||
|
||||
// Tool logger is fully implemented in header so small tools do not need to link to this library unnecessarily.
|
||||
void dummy_tool_logger_cpp() { }
|
||||
|
||||
} /* namespace NLMISC */
|
||||
|
||||
/* end of file */
|
|
@ -1,6 +1,11 @@
|
|||
|
||||
IF(WITH_NEL_TOOLS)
|
||||
IF(WITH_3D)
|
||||
IF(WITH_ASSIMP)
|
||||
SUBDIRS(
|
||||
mesh_utils
|
||||
mesh_export)
|
||||
ENDIF()
|
||||
SUBDIRS(
|
||||
anim_builder
|
||||
animation_set_builder
|
||||
|
@ -27,7 +32,6 @@ IF(WITH_NEL_TOOLS)
|
|||
zone_dump
|
||||
zviewer)
|
||||
ENDIF()
|
||||
|
||||
SUBDIRS(
|
||||
build_interface
|
||||
get_neighbors
|
||||
|
|
12
code/nel/tools/3d/mesh_export/CMakeLists.txt
Normal file
12
code/nel/tools/3d/mesh_export/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
FILE(GLOB SRCS *.cpp)
|
||||
FILE(GLOB HDRS *.h)
|
||||
|
||||
SOURCE_GROUP("" FILES ${SRCS} ${HDRS})
|
||||
|
||||
ADD_EXECUTABLE(mesh_export ${SRCS} ${HDRS})
|
||||
|
||||
TARGET_LINK_LIBRARIES(mesh_export mesh_utils nel3d nelmisc)
|
||||
NL_DEFAULT_PROPS(mesh_export "NeL, Tools, 3D: Mesh Export")
|
||||
NL_ADD_RUNTIME_FLAGS(mesh_export)
|
||||
|
||||
INSTALL(TARGETS mesh_export RUNTIME DESTINATION ${NL_BIN_PREFIX} COMPONENT tools3d)
|
77
code/nel/tools/3d/mesh_export/main.cpp
Normal file
77
code/nel/tools/3d/mesh_export/main.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "../mesh_utils/mesh_utils.h"
|
||||
|
||||
#include <nel/misc/cmd_args.h>
|
||||
#include <nel/misc/path.h>
|
||||
|
||||
#include <nel/3d/register_3d.h>
|
||||
#include <nel/3d/scene.h>
|
||||
|
||||
int printHelp(const NLMISC::CCmdArgs &args)
|
||||
{
|
||||
printf("NeL Mesh Export\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NLMISC::CApplicationContext app;
|
||||
|
||||
NLMISC::CCmdArgs args;
|
||||
args.setArgs(argc, (const char **)argv);
|
||||
|
||||
if (args.getArgs().size() == 1
|
||||
|| args.haveArg('h')
|
||||
|| args.haveLongArg("help"))
|
||||
return printHelp(args);
|
||||
|
||||
const NLMISC::CSString &filePath = args.getArgs().back();
|
||||
if (!NLMISC::CFile::fileExists(filePath))
|
||||
{
|
||||
printHelp(args);
|
||||
nlerror("File '%s' does not exist", filePath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
CMeshUtilsSettings settings;
|
||||
settings.SourceFilePath = filePath;
|
||||
|
||||
if (args.haveArg('d'))
|
||||
settings.DestinationDirectoryPath = args.getArg('d');
|
||||
if (settings.DestinationDirectoryPath.empty())
|
||||
settings.DestinationDirectoryPath = args.getLongArg("dst");
|
||||
if (settings.DestinationDirectoryPath.empty())
|
||||
settings.DestinationDirectoryPath = filePath + "_export";
|
||||
settings.DestinationDirectoryPath += "/";
|
||||
|
||||
settings.ToolDependLog = args.getLongArg("dependlog");
|
||||
if (settings.ToolDependLog.empty())
|
||||
settings.ToolDependLog = settings.DestinationDirectoryPath + "depend.log";
|
||||
settings.ToolErrorLog = args.getLongArg("errorlog");
|
||||
if (settings.ToolErrorLog.empty())
|
||||
settings.ToolErrorLog = settings.DestinationDirectoryPath + "error.log";
|
||||
|
||||
NL3D::CScene::registerBasics();
|
||||
NL3D::registerSerial3d();
|
||||
|
||||
return exportScene(settings);
|
||||
}
|
||||
|
||||
/* end of file */
|
16
code/nel/tools/3d/mesh_utils/CMakeLists.txt
Normal file
16
code/nel/tools/3d/mesh_utils/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
FILE(GLOB SRCS *.cpp)
|
||||
FILE(GLOB HDRS *.h)
|
||||
|
||||
SOURCE_GROUP("" FILES ${SRCS} ${HDRS})
|
||||
|
||||
INCLUDE_DIRECTORIES(${assimp_INCLUDE_DIRS})
|
||||
|
||||
NL_TARGET_LIB(mesh_utils ${SRCS} ${HDRS})
|
||||
|
||||
TARGET_LINK_LIBRARIES(mesh_utils ${assimp_LIBRARIES} nelmisc nel3d)
|
||||
NL_DEFAULT_PROPS(mesh_utils "NeL, Tools, 3D: Mesh Utils")
|
||||
NL_ADD_RUNTIME_FLAGS(mesh_utils)
|
||||
|
||||
IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC)
|
||||
INSTALL(TARGETS mesh_utils LIBRARY DESTINATION ${NL_LIB_PREFIX} ARCHIVE DESTINATION ${NL_LIB_PREFIX} COMPONENT tools3d)
|
||||
ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC)
|
228
code/nel/tools/3d/mesh_utils/assimp_material.cpp
Normal file
228
code/nel/tools/3d/mesh_utils/assimp_material.cpp
Normal file
|
@ -0,0 +1,228 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "assimp_shape.h"
|
||||
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
|
||||
#define NL_NODE_INTERNAL_TYPE aiNode
|
||||
#define NL_SCENE_INTERNAL_TYPE aiScene
|
||||
#include "scene_context.h"
|
||||
|
||||
#include <nel/misc/debug.h>
|
||||
#include <nel/misc/path.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
|
||||
#include <nel/3d/mesh.h>
|
||||
#include <nel/3d/texture_file.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace NLMISC;
|
||||
using namespace NL3D;
|
||||
|
||||
// http://assimp.sourceforge.net/lib_html/materials.html
|
||||
|
||||
inline CRGBA convColor(const aiColor3D &ac, uint8 a = 255)
|
||||
{
|
||||
return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f);
|
||||
}
|
||||
|
||||
inline CRGBA convColor(const aiColor4D &ac)
|
||||
{
|
||||
return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f, ac.a * 255.99f);
|
||||
}
|
||||
|
||||
void assimpMaterial(NL3D::CMaterial &mat, CMeshUtilsContext &context, const aiMaterial *am)
|
||||
{
|
||||
aiString amname;
|
||||
if (am->Get(AI_MATKEY_NAME, amname) != aiReturn_SUCCESS)
|
||||
amname = "";
|
||||
|
||||
mat.initLighted();
|
||||
mat.setShader(CMaterial::Normal);
|
||||
|
||||
int i;
|
||||
float f;
|
||||
aiColor3D c3;
|
||||
aiColor4D c4;
|
||||
|
||||
if (am->Get(AI_MATKEY_TWOSIDED, i) == aiReturn_SUCCESS)
|
||||
mat.setDoubleSided(i != 0);
|
||||
|
||||
if (am->Get(AI_MATKEY_BLEND_FUNC, i) == aiReturn_SUCCESS) switch ((aiBlendMode)i)
|
||||
{
|
||||
case aiBlendMode_Default:
|
||||
mat.setSrcBlend(CMaterial::srcalpha);
|
||||
mat.setDstBlend(CMaterial::invsrcalpha);
|
||||
break;
|
||||
case aiBlendMode_Additive:
|
||||
mat.setSrcBlend(CMaterial::one);
|
||||
mat.setDstBlend(CMaterial::one);
|
||||
break;
|
||||
}
|
||||
|
||||
// Colors follow GL convention
|
||||
// "While the ambient, diffuse, specular and emission
|
||||
// "material parameters all have alpha components, only the diffuse"
|
||||
// "alpha component is used in the lighting computation."
|
||||
if (am->Get(AI_MATKEY_COLOR_DIFFUSE, c3) == aiReturn_SUCCESS)
|
||||
mat.setDiffuse(convColor(c3));
|
||||
|
||||
if (am->Get(AI_MATKEY_OPACITY, f) == aiReturn_SUCCESS)
|
||||
mat.setOpacity(f * 255.99f);
|
||||
|
||||
if (am->Get(AI_MATKEY_COLOR_AMBIENT, c3) == aiReturn_SUCCESS)
|
||||
mat.setAmbient(convColor(c3));
|
||||
|
||||
if (am->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS)
|
||||
mat.setShininess(f); // (float)pow(2.0, f * 10.0) * 4.f;
|
||||
|
||||
if (am->Get(AI_MATKEY_COLOR_SPECULAR, c3) == aiReturn_SUCCESS)
|
||||
mat.setSpecular(convColor(c3));
|
||||
|
||||
if (am->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS)
|
||||
mat.setSpecular(CRGBAF(mat.getSpecular()) * f);
|
||||
else
|
||||
mat.setSpecular(NLMISC::CRGBA::Black);
|
||||
|
||||
if (am->Get(AI_MATKEY_COLOR_EMISSIVE, c3) == aiReturn_SUCCESS)
|
||||
mat.setEmissive(convColor(c3));
|
||||
|
||||
// Textures
|
||||
unsigned int texCount = am->GetTextureCount(aiTextureType_DIFFUSE);
|
||||
if (texCount > IDRV_MAT_MAXTEXTURES)
|
||||
{
|
||||
tlwarning(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Material '%s' has more than %i textures (%i textures found)", amname.C_Str(), IDRV_MAT_MAXTEXTURES, texCount);
|
||||
texCount = IDRV_MAT_MAXTEXTURES;
|
||||
}
|
||||
|
||||
for (unsigned int ti = 0; ti < texCount; ++ti)
|
||||
{
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
unsigned int uvindex;
|
||||
float blend; // Partially supported
|
||||
aiTextureOp op;
|
||||
aiTextureMapMode mapmode;
|
||||
if (am->GetTexture(aiTextureType_DIFFUSE, ti, &path, &mapping, &uvindex, &blend, &op, &mapmode) != aiReturn_SUCCESS)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Failed to get texture %i in material '%s'", ti, amname.C_Str());
|
||||
break;
|
||||
}
|
||||
|
||||
std::string fileName = CFile::getFilename(CPath::standardizePath(path.C_Str(), false));
|
||||
std::string knownPath = CPath::lookup(fileName, false, false, false);
|
||||
if (knownPath.empty())
|
||||
{
|
||||
tlwarning(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Texture '%s' referenced in material '%s' but not found in the database search paths", fileName.c_str(), amname.C_Str());
|
||||
}
|
||||
|
||||
// NeL supports bitmap and cubemap, but we import only basic bitmap here. Cubemap can be inserted from the mesh editor tool
|
||||
// NeL also has fancy multi-bitmap thing to switch between summer and winter and so on. Same story
|
||||
CSmartPtr<CTextureFile> tex = new CTextureFile();
|
||||
tex->setFileName(fileName);
|
||||
tex->setWrapS(mapmode == aiTextureMapMode_Clamp ? ITexture::Clamp : ITexture::Repeat);
|
||||
tex->setWrapT(mapmode == aiTextureMapMode_Clamp ? ITexture::Clamp : ITexture::Repeat);
|
||||
mat.setTexture(ti, tex);
|
||||
|
||||
// TODO uvindex for uv routing (probably necessary during shape import - if so also need to also ask the uv channel in the editor and store in meta)
|
||||
|
||||
// TODO aiTextureMapping texcoordgen if useful to import
|
||||
|
||||
mat.texEnvArg0Alpha(ti, CMaterial::Texture, CMaterial::SrcAlpha);
|
||||
mat.texEnvArg0RGB(ti, CMaterial::Texture, CMaterial::SrcColor);
|
||||
mat.texEnvArg1Alpha(ti, ti == 0 ? CMaterial::Diffuse : CMaterial::Previous, CMaterial::SrcAlpha);
|
||||
mat.texEnvArg1RGB(ti, ti == 0 ? CMaterial::Diffuse : CMaterial::Previous, CMaterial::SrcColor);
|
||||
switch (op)
|
||||
{
|
||||
case aiTextureOp_Multiply:
|
||||
default:
|
||||
mat.texEnvOpAlpha(ti, CMaterial::Modulate);
|
||||
mat.texEnvOpRGB(ti, CMaterial::Modulate);
|
||||
break;
|
||||
case aiTextureOp_Add:
|
||||
mat.texEnvOpAlpha(ti, CMaterial::Add);
|
||||
mat.texEnvOpRGB(ti, CMaterial::Add);
|
||||
break;
|
||||
case aiTextureOp_Subtract:
|
||||
mat.texEnvArg0Alpha(ti, CMaterial::Texture, CMaterial::InvSrcAlpha);
|
||||
mat.texEnvArg0RGB(ti, CMaterial::Texture, CMaterial::InvSrcColor);
|
||||
mat.texEnvOpAlpha(ti, CMaterial::Add);
|
||||
mat.texEnvOpRGB(ti, CMaterial::Add);
|
||||
break;
|
||||
case aiTextureOp_SignedAdd:
|
||||
mat.texEnvOpAlpha(ti, CMaterial::AddSigned);
|
||||
mat.texEnvOpRGB(ti, CMaterial::AddSigned);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CSmartPtr<CMaterial> assimpMaterial(CMeshUtilsContext &context, const aiMaterial *am)
|
||||
{
|
||||
CSmartPtr<CMaterial> matp = new CMaterial();
|
||||
CMaterial &mat = *matp;
|
||||
assimpMaterial(mat, context, am);
|
||||
return matp;
|
||||
}
|
||||
|
||||
void assimpMaterials(CMeshUtilsContext &context)
|
||||
{
|
||||
set<CSString> materialNames;
|
||||
|
||||
const aiScene *scene = context.InternalScene;
|
||||
for (unsigned int mi = 0; mi < scene->mNumMaterials; ++mi)
|
||||
{
|
||||
const aiMaterial *am = scene->mMaterials[mi];
|
||||
|
||||
for (unsigned int pi = 0; pi < am->mNumProperties; ++pi) // DEBUG
|
||||
{ // DEBUG
|
||||
const aiMaterialProperty *amp = am->mProperties[pi];
|
||||
printf("%s\n", amp->mKey.C_Str());
|
||||
} // DEBUG
|
||||
|
||||
aiString amname;
|
||||
if (am->Get(AI_MATKEY_NAME, amname) != aiReturn_SUCCESS)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Material has no name");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (materialNames.find(amname.C_Str()) != materialNames.end())
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Material name '%s' used more than once", amname.C_Str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (context.SceneMeta.Materials.find(amname.C_Str())
|
||||
== context.SceneMeta.Materials.end())
|
||||
{
|
||||
materialNames.insert(amname.C_Str());
|
||||
context.SceneMeta.Materials[amname.C_Str()] = assimpMaterial(context, am);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end of file */
|
28
code/nel/tools/3d/mesh_utils/assimp_material.h
Normal file
28
code/nel/tools/3d/mesh_utils/assimp_material.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
namespace NL3D {
|
||||
class CMaterial;
|
||||
}
|
||||
|
||||
struct CMeshUtilsContext;
|
||||
void assimpMaterial(NL3D::CMaterial &mat, CMeshUtilsContext &context, const aiMaterial *am);
|
||||
void assimpMaterials(CMeshUtilsContext &context);
|
||||
|
||||
/* end of file */
|
379
code/nel/tools/3d/mesh_utils/assimp_shape.cpp
Normal file
379
code/nel/tools/3d/mesh_utils/assimp_shape.cpp
Normal file
|
@ -0,0 +1,379 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "assimp_shape.h"
|
||||
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
|
||||
#define NL_NODE_INTERNAL_TYPE aiNode
|
||||
#define NL_SCENE_INTERNAL_TYPE aiScene
|
||||
#include "scene_context.h"
|
||||
|
||||
#include <nel/misc/debug.h>
|
||||
#include <nel/misc/path.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
|
||||
#include <nel/3d/mesh.h>
|
||||
|
||||
#include "assimp_material.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace NLMISC;
|
||||
using namespace NL3D;
|
||||
|
||||
// TODO: buildParticleSystem ??
|
||||
// TODO: buildWaveMakerShape ??
|
||||
// TODO: buildRemanence ??
|
||||
// TODO: buildFlare ??
|
||||
// Probably specific settings we can only do in meta editor on a dummy node..
|
||||
// TODO: pacs prim
|
||||
|
||||
// TODO: buildWaterShape specifics when node has water material
|
||||
|
||||
// TODO: CMeshMultiLod::CMeshMultiLodBuild multiLodBuild; export_mesh.cpp ln 228
|
||||
// TODO: LOD MRM
|
||||
|
||||
// TODO: Skinned - reverse transform by skeleton root bone to align?
|
||||
|
||||
/*inline CMatrix convMatrix(const aiMatrix4x4 &tf)
|
||||
{
|
||||
CMatrix m;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m.set(&tf.a1);
|
||||
return m;
|
||||
}*/
|
||||
|
||||
inline CVector convVector(const aiVector3D &av)
|
||||
{
|
||||
return CVector(av.x, av.y, av.z); // COORDINATE CONVERSION
|
||||
}
|
||||
|
||||
inline CRGBA convColor(const aiColor4D &ac)
|
||||
{
|
||||
return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f, ac.a * 255.99f);
|
||||
}
|
||||
|
||||
inline CUVW convUvw(const aiVector3D &av)
|
||||
{
|
||||
return CUVW(av.x, -av.y, av.z); // UH OH COORDINATE CONVERSION ?! ONLY FOR TEXTURES !!
|
||||
}
|
||||
|
||||
inline CQuat convQuat(const aiQuaternion &aq)
|
||||
{
|
||||
return CQuat(aq.x, aq.y, aq.z, aq.w);
|
||||
}
|
||||
|
||||
void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsContext &context, CNodeContext &nodeContext)
|
||||
{
|
||||
const aiNode *node = nodeContext.InternalNode;
|
||||
// Reference CExportNel::buildBaseMeshInterface
|
||||
|
||||
// Load materials
|
||||
buildBaseMesh.Materials.resize(node->mNumMeshes);
|
||||
|
||||
for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi)
|
||||
{
|
||||
const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]];
|
||||
const aiMaterial *am = context.InternalScene->mMaterials[mesh->mMaterialIndex];
|
||||
|
||||
aiString amname;
|
||||
if (am->Get(AI_MATKEY_NAME, amname) != aiReturn_SUCCESS)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Material used by node '%s' has no name", node->mName.C_Str()); // TODO: Maybe autogen names by index in mesh or node if this is actually a thing
|
||||
assimpMaterial(buildBaseMesh.Materials[mi], context, am);
|
||||
}
|
||||
else
|
||||
{
|
||||
buildBaseMesh.Materials[mi] = *context.SceneMeta.Materials[amname.C_Str()];
|
||||
}
|
||||
}
|
||||
|
||||
// Positioning
|
||||
const aiMatrix4x4 &root = context.InternalScene->mRootNode->mTransformation;
|
||||
const aiMatrix4x4 &tf = nodeContext.InternalNode->mTransformation; // COORDINATE CONVERSION HERE INSTEAD OF PER VERTEX ??
|
||||
aiVector3D scaling;
|
||||
aiQuaternion rotation;
|
||||
aiVector3D position;
|
||||
tf.Decompose(scaling, rotation, position);
|
||||
buildBaseMesh.DefaultScale = convVector(scaling);
|
||||
buildBaseMesh.DefaultRotQuat = convQuat(rotation);
|
||||
buildBaseMesh.DefaultRotEuler = CVector(0, 0, 0);
|
||||
buildBaseMesh.DefaultPivot = CVector(0, 0, 0);
|
||||
buildBaseMesh.DefaultPos = convVector(position);
|
||||
if (buildBaseMesh.DefaultScale.x != 1.0f || buildBaseMesh.DefaultScale.y != 1.0f || buildBaseMesh.DefaultScale.z != 1.0f)
|
||||
{
|
||||
tlmessage(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Node '%s' has a scaled transformation. This may be a mistake", node->mName.C_Str());
|
||||
}
|
||||
|
||||
// Meta
|
||||
// dst.CollisionMeshGeneration = src.CollisionMeshGeneration;
|
||||
|
||||
// TODO: Morph
|
||||
}
|
||||
|
||||
bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsContext &context, CNodeContext &nodeContext)
|
||||
{
|
||||
// TODO
|
||||
// *** If the mesh is skined, vertices will be exported in world space.
|
||||
// *** If the mesh is not skined, vertices will be exported in offset space.
|
||||
|
||||
// TODO Support skinning
|
||||
|
||||
const aiNode *node = nodeContext.InternalNode;
|
||||
nlassert(node->mNumMeshes);
|
||||
|
||||
// Basic validations before processing starts
|
||||
for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi)
|
||||
{
|
||||
// TODO: Maybe needs to be the same count too for all meshes, so compare with mesh 0
|
||||
const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]];
|
||||
if (mesh->GetNumColorChannels() > 2)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"(%s) mesh->GetNumColorChannels() > 2", node->mName.C_Str());
|
||||
return false;
|
||||
}
|
||||
if (mesh->GetNumUVChannels() > CVertexBuffer::MaxStage)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"(%s) mesh->GetNumUVChannels() > CVertexBuffer::MaxStage", node->mName.C_Str());
|
||||
return false;
|
||||
}
|
||||
if (!mesh->HasNormals())
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"(%s) !mesh->HasNormals()", node->mName.C_Str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Default vertex flags
|
||||
buildMesh.VertexFlags = CVertexBuffer::PositionFlag | CVertexBuffer::NormalFlag;
|
||||
|
||||
// TODO: UV Channels routing to correct texture stage
|
||||
for (uint i = 0; i < CVertexBuffer::MaxStage; ++i)
|
||||
buildMesh.UVRouting[i] = i;
|
||||
|
||||
// Meshes in assimp are separated per material, so we need to re-merge them for the mesh build process
|
||||
// This process also deduplicates vertices
|
||||
bool cleanupMesh = true;
|
||||
sint32 numVertices = 0;
|
||||
for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi)
|
||||
numVertices += context.InternalScene->mMeshes[node->mMeshes[mi]]->mNumVertices;
|
||||
buildMesh.Vertices.resize(numVertices);
|
||||
numVertices = 0;
|
||||
map<CVector, sint32> vertexIdentifiers;
|
||||
vector<vector<sint32> > vertexRemapping;
|
||||
vertexRemapping.resize(node->mNumMeshes);
|
||||
for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi)
|
||||
{
|
||||
const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]];
|
||||
vertexRemapping[mi].resize(mesh->mNumVertices);
|
||||
for (unsigned int vi = 0; vi < mesh->mNumVertices; ++vi)
|
||||
{
|
||||
CVector vec = convVector(mesh->mVertices[vi]);
|
||||
map<CVector, sint32>::iterator vecit = vertexIdentifiers.find(vec);
|
||||
if (vecit == vertexIdentifiers.end())
|
||||
{
|
||||
buildMesh.Vertices[numVertices] = vec;
|
||||
if (cleanupMesh) vertexIdentifiers[vec] = numVertices; // Don't remap if we don't wan't to lose vertex indices
|
||||
vertexRemapping[mi][vi] = numVertices;
|
||||
++numVertices;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexRemapping[mi][vi] = vecit->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
buildMesh.Vertices.resize(numVertices);
|
||||
|
||||
// Process all faces
|
||||
// WONT IMPLEMENT: Radial faces generation... is linked to smoothing group...
|
||||
// leave radial normals generation to modeling tool for now...
|
||||
sint32 numFaces = 0;
|
||||
for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi)
|
||||
numFaces += context.InternalScene->mMeshes[node->mMeshes[mi]]->mNumFaces;
|
||||
buildMesh.Faces.resize(numFaces);
|
||||
numFaces = 0;
|
||||
unsigned int refNumColorChannels = context.InternalScene->mMeshes[node->mMeshes[0]]->GetNumColorChannels();
|
||||
unsigned int refNumUVChannels = context.InternalScene->mMeshes[node->mMeshes[0]]->GetNumUVChannels();
|
||||
for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi)
|
||||
{
|
||||
const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]];
|
||||
|
||||
// Get channel numbers
|
||||
unsigned int numColorChannels = mesh->GetNumColorChannels();
|
||||
if (numColorChannels > 2)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Shape '%s' has too many color channels in mesh %i (%i channels found)", node->mName.C_Str(), mi, numColorChannels);
|
||||
}
|
||||
if (numColorChannels > 0)
|
||||
{
|
||||
buildMesh.VertexFlags |= CVertexBuffer::PrimaryColorFlag;
|
||||
if (numColorChannels > 1)
|
||||
{
|
||||
buildMesh.VertexFlags |= CVertexBuffer::SecondaryColorFlag;
|
||||
}
|
||||
}
|
||||
unsigned int numUVChannels = mesh->GetNumUVChannels();
|
||||
if (numUVChannels > CVertexBuffer::MaxStage)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Shape '%s' has too many uv channels in mesh %i (%i channels found)", node->mName.C_Str(), mi, numUVChannels);
|
||||
numUVChannels = CVertexBuffer::MaxStage;
|
||||
}
|
||||
for (unsigned int ui = 0; ui < numUVChannels; ++ui)
|
||||
buildMesh.VertexFlags |= (CVertexBuffer::TexCoord0Flag << ui); // TODO: Coord UV tex stage rerouting
|
||||
|
||||
// TODO: Channels do in fact differ between submeshes, so we need to correctly recount and reroute the materials properly
|
||||
if (numColorChannels != refNumColorChannels)
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Shape '%s' mismatch of nb color channel in mesh '%i', please contact developer", node->mName.C_Str(), mi);
|
||||
if (numUVChannels != refNumUVChannels)
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Shape '%s' mismatch of nb uv channel in mesh '%i', please contact developer", node->mName.C_Str(), mi);
|
||||
|
||||
for (unsigned int fi = 0; fi < mesh->mNumFaces; ++fi)
|
||||
{
|
||||
const aiFace &af = mesh->mFaces[fi];
|
||||
if (af.mNumIndices != 3)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"(%s) Face %i on mesh %i has %i faces", node->mName.C_Str(), fi, mi, af.mNumIndices);
|
||||
continue; // return false; Keep going, just drop the face for better user experience
|
||||
}
|
||||
if (cleanupMesh)
|
||||
{
|
||||
if (vertexRemapping[mi][af.mIndices[0]] == vertexRemapping[mi][af.mIndices[1]]
|
||||
|| vertexRemapping[mi][af.mIndices[1]] == vertexRemapping[mi][af.mIndices[2]]
|
||||
|| vertexRemapping[mi][af.mIndices[2]] == vertexRemapping[mi][af.mIndices[0]])
|
||||
continue; // Not a triangle
|
||||
}
|
||||
CMesh::CFace &face = buildMesh.Faces[numFaces];
|
||||
face.MaterialId = mi;
|
||||
face.SmoothGroup = 0; // No smoothing groups (bitfield)
|
||||
face.Corner[0].Vertex = vertexRemapping[mi][af.mIndices[0]];
|
||||
face.Corner[1].Vertex = vertexRemapping[mi][af.mIndices[1]];
|
||||
face.Corner[2].Vertex = vertexRemapping[mi][af.mIndices[2]];
|
||||
face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[0]]);
|
||||
face.Corner[1].Normal = convVector(mesh->mNormals[af.mIndices[1]]);
|
||||
face.Corner[2].Normal = convVector(mesh->mNormals[af.mIndices[2]]);
|
||||
// TODO: If we want normal maps, we need to add tangent vectors to CFace and build process
|
||||
// UV channels
|
||||
for (unsigned int ui = 0; ui < numUVChannels; ++ui) // TODO: UV Rerouting
|
||||
{
|
||||
face.Corner[0].Uvws[ui] = convUvw(mesh->mTextureCoords[ui][af.mIndices[0]]);
|
||||
face.Corner[1].Uvws[ui] = convUvw(mesh->mTextureCoords[ui][af.mIndices[1]]);
|
||||
face.Corner[2].Uvws[ui] = convUvw(mesh->mTextureCoords[ui][af.mIndices[2]]);
|
||||
}
|
||||
for (unsigned int ui = numUVChannels; ui < CVertexBuffer::MaxStage; ++ui)
|
||||
{
|
||||
face.Corner[0].Uvws[ui] = CUVW(0, 0, 0);
|
||||
face.Corner[1].Uvws[ui] = CUVW(0, 0, 0);
|
||||
face.Corner[2].Uvws[ui] = CUVW(0, 0, 0);
|
||||
}
|
||||
// Primary and secondary color channels
|
||||
if (numColorChannels > 0) // TODO: Verify
|
||||
{
|
||||
face.Corner[0].Color = convColor(mesh->mColors[0][af.mIndices[0]]);
|
||||
face.Corner[1].Color = convColor(mesh->mColors[0][af.mIndices[1]]);
|
||||
face.Corner[2].Color = convColor(mesh->mColors[0][af.mIndices[2]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
face.Corner[0].Color = CRGBA(255, 255, 255, 255);
|
||||
face.Corner[1].Color = CRGBA(255, 255, 255, 255);
|
||||
face.Corner[2].Color = CRGBA(255, 255, 255, 255);
|
||||
}
|
||||
if (numColorChannels > 1) // TODO: Verify
|
||||
{
|
||||
face.Corner[0].Specular = convColor(mesh->mColors[1][af.mIndices[0]]);
|
||||
face.Corner[1].Specular = convColor(mesh->mColors[1][af.mIndices[1]]);
|
||||
face.Corner[2].Specular = convColor(mesh->mColors[1][af.mIndices[2]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
face.Corner[0].Specular = CRGBA(255, 255, 255, 255);
|
||||
face.Corner[1].Specular = CRGBA(255, 255, 255, 255);
|
||||
face.Corner[2].Specular = CRGBA(255, 255, 255, 255);
|
||||
}
|
||||
// TODO: Color modulate, alpha, use color alpha for vp tree, etc
|
||||
++numFaces;
|
||||
}
|
||||
}
|
||||
if (numFaces != buildMesh.Faces.size())
|
||||
{
|
||||
tlmessage(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Removed %u degenerate faces in shape '%s'", (uint32)(buildMesh.Faces.size() - numFaces), node->mName.C_Str());
|
||||
buildMesh.Faces.resize(numFaces);
|
||||
}
|
||||
|
||||
// clear for MRM info
|
||||
buildMesh.Interfaces.clear();
|
||||
buildMesh.InterfaceLinks.clear();
|
||||
|
||||
// TODO: Export VP
|
||||
buildMesh.MeshVertexProgram = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext)
|
||||
{
|
||||
// Reference: export_mesh.cpp, buildShape
|
||||
nodeContext.Shape = NULL;
|
||||
|
||||
const aiNode *node = nodeContext.InternalNode;
|
||||
nlassert(node->mNumMeshes);
|
||||
|
||||
// Fill the build interface of CMesh
|
||||
CMeshBase::CMeshBaseBuild buildBaseMesh;
|
||||
assimpBuildBaseMesh(buildBaseMesh, context, nodeContext);
|
||||
|
||||
CMesh::CMeshBuild buildMesh;
|
||||
if (!assimpBuildMesh(buildMesh, buildBaseMesh, context, nodeContext))
|
||||
return false;
|
||||
|
||||
// Make a CMesh object
|
||||
CMesh *mesh = new CMesh();
|
||||
|
||||
// Build the mesh with the build interface
|
||||
mesh->build(buildBaseMesh, buildMesh);
|
||||
|
||||
// TODO
|
||||
// Reference: export_mesh.cpp, buildShape
|
||||
// Must be done after the build to update vertex links
|
||||
// Pass to buildMeshMorph if the original mesh is skinned or not
|
||||
// buildMeshMorph(buildMesh, node, time, nodeMap != NULL);
|
||||
// mesh->setBlendShapes(buildMesh.BlendShapes);
|
||||
|
||||
// optimize number of material
|
||||
// mesh->optimizeMaterialUsage(materialRemap);
|
||||
|
||||
// Store mesh in context
|
||||
nodeContext.Shape = mesh;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* end of file */
|
25
code/nel/tools/3d/mesh_utils/assimp_shape.h
Normal file
25
code/nel/tools/3d/mesh_utils/assimp_shape.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
struct CMeshUtilsContext;
|
||||
struct CNodeContext;
|
||||
|
||||
bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext);
|
||||
|
||||
/* end of file */
|
107
code/nel/tools/3d/mesh_utils/database_config.cpp
Normal file
107
code/nel/tools/3d/mesh_utils/database_config.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "database_config.h"
|
||||
|
||||
#include <nel/misc/debug.h>
|
||||
#include <nel/misc/path.h>
|
||||
#include <nel/misc/config_file.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace NLMISC;
|
||||
|
||||
TPathString CDatabaseConfig::s_RootPath;
|
||||
NLMISC::CConfigFile *CDatabaseConfig::s_ConfigFile = NULL;
|
||||
CDatabaseConfig CDatabaseConfig::s_Instance;
|
||||
uint32 CDatabaseConfig::s_ConfigFileModification;
|
||||
|
||||
static std::set<TPathString> s_SearchPaths;
|
||||
|
||||
void CDatabaseConfig::cleanup()
|
||||
{
|
||||
delete CDatabaseConfig::s_ConfigFile;
|
||||
CDatabaseConfig::s_ConfigFile = NULL;
|
||||
}
|
||||
|
||||
CDatabaseConfig::~CDatabaseConfig()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
bool CDatabaseConfig::init(const std::string &asset)
|
||||
{
|
||||
// release();
|
||||
|
||||
TPathString rootPath = NLMISC::CPath::standardizePath(asset, false);
|
||||
TPathString configPath = rootPath + "/database.cfg";
|
||||
while (!CFile::fileExists(configPath))
|
||||
{
|
||||
int sep = CFile::getLastSeparator(rootPath);
|
||||
if (sep == string::npos)
|
||||
return false;
|
||||
|
||||
rootPath = rootPath.substr(0, sep);
|
||||
if (rootPath.empty())
|
||||
return false;
|
||||
|
||||
configPath = rootPath + "/database.cfg";
|
||||
}
|
||||
|
||||
rootPath += "/";
|
||||
uint32 configFileModification = CFile::getFileModificationDate(configPath);
|
||||
if (rootPath == s_RootPath && s_ConfigFileModification == configFileModification)
|
||||
return true; // Do not reload
|
||||
|
||||
nldebug("Initializing database config '%s'", configPath.c_str());
|
||||
release();
|
||||
|
||||
s_RootPath = rootPath;
|
||||
s_ConfigFileModification = configFileModification;
|
||||
|
||||
s_ConfigFile = new CConfigFile();
|
||||
s_ConfigFile->load(configPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDatabaseConfig::initTextureSearchDirectories()
|
||||
{
|
||||
searchDirectories("TextureSearchDirectories");
|
||||
}
|
||||
|
||||
void CDatabaseConfig::searchDirectories(const char *var)
|
||||
{
|
||||
CConfigFile::CVar &paths = s_ConfigFile->getVar(var);
|
||||
for (uint i = 0; i < paths.size(); i++)
|
||||
{
|
||||
TPathString path = paths.asString(i);
|
||||
if (s_SearchPaths.find(path) == s_SearchPaths.end())
|
||||
{
|
||||
CPath::addSearchPath(s_RootPath + path);
|
||||
s_SearchPaths.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDatabaseConfig::release()
|
||||
{
|
||||
s_SearchPaths.clear();
|
||||
CPath::clearMap();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
/* end of file */
|
58
code/nel/tools/3d/mesh_utils/database_config.h
Normal file
58
code/nel/tools/3d/mesh_utils/database_config.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
namespace NLMISC {
|
||||
class CConfigFile;
|
||||
}
|
||||
|
||||
#ifdef NL_OS_WINDOWS
|
||||
#include <nel/misc/sstring.h>
|
||||
typedef NLMISC::CSString TPathString;
|
||||
#else
|
||||
typedef std::string TPathString;
|
||||
#endif
|
||||
|
||||
/// Asset database configuration
|
||||
class CDatabaseConfig
|
||||
{
|
||||
public:
|
||||
~CDatabaseConfig();
|
||||
|
||||
/// Searches for the configuration for the specified asset path by recursively going through all parent directories looking for 'database.cfg', initializes and applies the configuration.
|
||||
static bool init(const std::string &asset);
|
||||
static void release();
|
||||
|
||||
static void initTextureSearchDirectories();
|
||||
|
||||
static inline const TPathString &rootPath() { return s_RootPath; }
|
||||
static inline TPathString configPath() { return s_RootPath + "/database.cfg"; }
|
||||
|
||||
private:
|
||||
static void cleanup();
|
||||
static void searchDirectories(const char *var);
|
||||
|
||||
static CDatabaseConfig s_Instance;
|
||||
static uint32 s_ConfigFileModification;
|
||||
|
||||
static TPathString s_RootPath;
|
||||
static NLMISC::CConfigFile *s_ConfigFile;
|
||||
|
||||
};
|
||||
|
||||
/* end of file */
|
348
code/nel/tools/3d/mesh_utils/mesh_utils.cpp
Normal file
348
code/nel/tools/3d/mesh_utils/mesh_utils.cpp
Normal file
|
@ -0,0 +1,348 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "mesh_utils.h"
|
||||
|
||||
#include <nel/misc/debug.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
#include <nel/misc/sstring.h>
|
||||
#include <nel/misc/file.h>
|
||||
#include <nel/misc/path.h>
|
||||
|
||||
#include <nel/3d/shape.h>
|
||||
#include <nel/3d/mesh.h>
|
||||
#include <nel/3d/texture_file.h>
|
||||
|
||||
#include "database_config.h"
|
||||
#include "scene_meta.h"
|
||||
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
|
||||
#define NL_NODE_INTERNAL_TYPE aiNode
|
||||
#define NL_SCENE_INTERNAL_TYPE aiScene
|
||||
#include "scene_context.h"
|
||||
|
||||
#include "assimp_material.h"
|
||||
#include "assimp_shape.h"
|
||||
|
||||
CMeshUtilsSettings::CMeshUtilsSettings()
|
||||
{
|
||||
/*ShapeDirectory = "shape";
|
||||
IGDirectory = "ig";
|
||||
SkelDirectory = "skel";*/
|
||||
}
|
||||
|
||||
void importShapes(CMeshUtilsContext &context, const aiNode *node)
|
||||
{
|
||||
if (node != context.InternalScene->mRootNode)
|
||||
{
|
||||
CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()];
|
||||
CNodeMeta &nodeMeta = context.SceneMeta.Nodes[node->mName.C_Str()];
|
||||
if (nodeMeta.ExportMesh == TMeshShape && nodeMeta.InstanceName.empty())
|
||||
{
|
||||
if (node->mNumMeshes)
|
||||
{
|
||||
nldebug("Shape '%s' found containing '%u' meshes", node->mName.C_Str(), node->mNumMeshes);
|
||||
assimpShape(context, nodeContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < node->mNumChildren; ++i)
|
||||
importShapes(context, node->mChildren[i]);
|
||||
}
|
||||
|
||||
void validateInternalNodeNames(CMeshUtilsContext &context, const aiNode *node)
|
||||
{
|
||||
if (!node->mParent || node == context.InternalScene->mRootNode)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (node->mName.length == 0)
|
||||
{
|
||||
tlwarning(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Node has no name");
|
||||
}
|
||||
else
|
||||
{
|
||||
CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()];
|
||||
|
||||
if (nodeContext.InternalNode && nodeContext.InternalNode != node)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Node name '%s' appears multiple times", node->mName.C_Str());
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeContext.InternalNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < node->mNumChildren; ++i)
|
||||
validateInternalNodeNames(context, node->mChildren[i]);
|
||||
}
|
||||
|
||||
void flagAssimpBones(CMeshUtilsContext &context)
|
||||
{
|
||||
// Find out which nodes are bones by checking the mesh meta info
|
||||
const aiScene *scene = context.InternalScene;
|
||||
for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
|
||||
{
|
||||
// nldebug("FOUND MESH '%s'\n", scene->mMeshes[i]->mName.C_Str());
|
||||
const aiMesh *mesh = scene->mMeshes[i];
|
||||
for (unsigned int j = 0; j < mesh->mNumBones; ++j)
|
||||
{
|
||||
CNodeContext &nodeContext = context.Nodes[mesh->mBones[j]->mName.C_Str()];
|
||||
if (!nodeContext.InternalNode)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Bone '%s' has no associated node", mesh->mBones[j]->mName.C_Str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flag as bone
|
||||
nodeContext.IsBone = true;
|
||||
|
||||
// Flag all parents as bones
|
||||
/*const aiNode *parent = nodeContext.InternalNode;
|
||||
while (parent = parent->mParent) if (parent->mName.length)
|
||||
{
|
||||
context.Nodes[parent->mName.C_Str()].IsBone = true;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find out which nodes are bones by checking the animation info
|
||||
// TODO
|
||||
}
|
||||
|
||||
void flagRecursiveBones(CMeshUtilsContext &context, CNodeContext &nodeContext, bool autoStop = false)
|
||||
{
|
||||
nodeContext.IsBone = true;
|
||||
const aiNode *node = nodeContext.InternalNode;
|
||||
nlassert(node);
|
||||
for (unsigned int i = 0; i < node->mNumChildren; ++i)
|
||||
{
|
||||
CNodeContext &ctx = context.Nodes[node->mName.C_Str()];
|
||||
if (autoStop && ctx.IsBone)
|
||||
continue;
|
||||
flagRecursiveBones(context, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void flagMetaBones(CMeshUtilsContext &context)
|
||||
{
|
||||
for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it)
|
||||
{
|
||||
CNodeContext &ctx = it->second;
|
||||
CNodeMeta &meta = context.SceneMeta.Nodes[it->first];
|
||||
if (meta.ExportBone == TBoneForce)
|
||||
ctx.IsBone = true;
|
||||
else if (meta.ExportBone == TBoneRoot)
|
||||
flagRecursiveBones(context, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void flagLocalParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext)
|
||||
{
|
||||
const aiNode *node = nodeContext.InternalNode;
|
||||
}
|
||||
|
||||
void flagAllParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext, bool autoStop = false)
|
||||
{
|
||||
const aiNode *parent = nodeContext.InternalNode;
|
||||
while (parent = parent->mParent) if (parent->mName.length && parent != context.InternalScene->mRootNode)
|
||||
{
|
||||
CNodeContext &ctx = context.Nodes[parent->mName.C_Str()];
|
||||
if (autoStop && ctx.IsBone)
|
||||
break;
|
||||
ctx.IsBone = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasIndirectParentBone(CMeshUtilsContext &context, CNodeContext &nodeContext)
|
||||
{
|
||||
const aiNode *parent = nodeContext.InternalNode;
|
||||
while (parent = parent->mParent) if (parent->mName.length && parent != context.InternalScene->mRootNode)
|
||||
if (context.Nodes[parent->mName.C_Str()].IsBone) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void flagExpandedBones(CMeshUtilsContext &context)
|
||||
{
|
||||
switch (context.SceneMeta.SkeletonMode)
|
||||
{
|
||||
case TSkelLocal:
|
||||
for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it)
|
||||
{
|
||||
CNodeContext &nodeContext = it->second;
|
||||
if (nodeContext.IsBone && hasIndirectParentBone(context, nodeContext))
|
||||
flagAllParentBones(context, nodeContext, true);
|
||||
}
|
||||
break;
|
||||
case TSkelRoot:
|
||||
for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it)
|
||||
{
|
||||
CNodeContext &nodeContext = it->second;
|
||||
if (nodeContext.IsBone)
|
||||
flagAllParentBones(context, nodeContext, true);
|
||||
}
|
||||
break;
|
||||
case TSkelFull:
|
||||
for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it)
|
||||
{
|
||||
CNodeContext &nodeContext = it->second;
|
||||
if (nodeContext.IsBone)
|
||||
flagAllParentBones(context, nodeContext, true);
|
||||
}
|
||||
for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it)
|
||||
{
|
||||
CNodeContext &nodeContext = it->second;
|
||||
if (nodeContext.IsBone)
|
||||
flagRecursiveBones(context, nodeContext, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void exportShapes(CMeshUtilsContext &context)
|
||||
{
|
||||
for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it)
|
||||
{
|
||||
CNodeContext &nodeContext = it->second;
|
||||
if (nodeContext.Shape)
|
||||
{
|
||||
std::string shapePath = NLMISC::CPath::standardizePath(context.Settings.DestinationDirectoryPath, true) + it->first + ".shape";
|
||||
context.ToolLogger.writeDepend(NLMISC::BUILD, shapePath.c_str(), "*");
|
||||
NLMISC::COFile f;
|
||||
if (f.open(shapePath, false, false, true))
|
||||
{
|
||||
try
|
||||
{
|
||||
NL3D::CShapeStream shapeStream(nodeContext.Shape);
|
||||
shapeStream.serial(f);
|
||||
f.close();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Shape '%s' serialization failed!", it->first.c_str());
|
||||
}
|
||||
}
|
||||
if (NL3D::CMeshBase *mesh = dynamic_cast<NL3D::CMeshBase *>(nodeContext.Shape.getPtr()))
|
||||
{
|
||||
for (uint mi = 0; mi < mesh->getNbMaterial(); ++mi)
|
||||
{
|
||||
NL3D::CMaterial &mat = mesh->getMaterial(mi);
|
||||
for (uint ti = 0; ti < NL3D::IDRV_MAT_MAXTEXTURES; ++ti)
|
||||
{
|
||||
if (NL3D::ITexture *itex = mat.getTexture(ti))
|
||||
{
|
||||
if (NL3D::CTextureFile *tex = dynamic_cast<NL3D::CTextureFile *>(itex))
|
||||
{
|
||||
std::string fileName = tex->getFileName();
|
||||
std::string knownPath = NLMISC::CPath::lookup(fileName, false, false, false);
|
||||
if (!knownPath.empty())
|
||||
{
|
||||
context.ToolLogger.writeDepend(NLMISC::RUNTIME, shapePath.c_str(), knownPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Move this warning into nelmeta serialization so it's shown before export
|
||||
tlwarning(context.ToolLogger, context.Settings.SourceFilePath.c_str(),
|
||||
"Texture '%s' referenced in material but not found in the database search paths", fileName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Separate load scene and save scene functions
|
||||
int exportScene(const CMeshUtilsSettings &settings)
|
||||
{
|
||||
CMeshUtilsContext context(settings);
|
||||
NLMISC::CFile::createDirectoryTree(settings.DestinationDirectoryPath);
|
||||
|
||||
if (!settings.ToolDependLog.empty())
|
||||
context.ToolLogger.initDepend(settings.ToolDependLog);
|
||||
if (!settings.ToolErrorLog.empty())
|
||||
context.ToolLogger.initError(settings.ToolErrorLog);
|
||||
context.ToolLogger.writeDepend(NLMISC::BUILD, "*", NLMISC::CPath::standardizePath(context.Settings.SourceFilePath, false).c_str()); // Base input file
|
||||
|
||||
// Apply database configuration
|
||||
CDatabaseConfig::init(settings.SourceFilePath);
|
||||
CDatabaseConfig::initTextureSearchDirectories();
|
||||
|
||||
Assimp::Importer importer;
|
||||
const aiScene *scene = importer.ReadFile(settings.SourceFilePath, 0
|
||||
| aiProcess_Triangulate
|
||||
| aiProcess_ValidateDataStructure
|
||||
| aiProcess_GenNormals // Or GenSmoothNormals? TODO: Validate smoothness between material boundaries!
|
||||
); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights
|
||||
if (!scene)
|
||||
{
|
||||
const char *errs = importer.GetErrorString();
|
||||
if (errs) tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), "Assimp failed to load the scene: '%s'", errs);
|
||||
else tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), "Unable to load scene");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// aiProcess_Triangulate
|
||||
// aiProcess_ValidateDataStructure: TODO: Catch Assimp error output stream
|
||||
// aiProcess_RemoveRedundantMaterials: Not used because we may override materials with NeL Material from meta
|
||||
// aiProcess_ImproveCacheLocality: TODO: Verify this does not modify vertex indices
|
||||
//scene->mRootNode->mMetaData
|
||||
|
||||
context.InternalScene = scene;
|
||||
if (context.SceneMeta.load(context.Settings.SourceFilePath))
|
||||
context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.SceneMeta.metaFilePath().c_str()); // Meta input file
|
||||
|
||||
validateInternalNodeNames(context, context.InternalScene->mRootNode);
|
||||
|
||||
// -- SKEL FLAG --
|
||||
flagAssimpBones(context);
|
||||
flagMetaBones(context);
|
||||
flagExpandedBones(context);
|
||||
// TODO
|
||||
// [
|
||||
// Only necessary in TSkelLocal
|
||||
// For each shape test if all the bones have the same root bones for their skeleton
|
||||
// 1) Iterate each until a different is found
|
||||
// 2) When a different root is found, connect the two to the nearest common bone
|
||||
// ]
|
||||
// -- SKEL FLAG --
|
||||
|
||||
// First import materials
|
||||
assimpMaterials(context);
|
||||
|
||||
// Import shapes
|
||||
importShapes(context, context.InternalScene->mRootNode);
|
||||
|
||||
// Export shapes
|
||||
exportShapes(context);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* end of file */
|
44
code/nel/tools/3d/mesh_utils/mesh_utils.h
Normal file
44
code/nel/tools/3d/mesh_utils/mesh_utils.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef NL_MESH_UTILS_H
|
||||
#define NL_MESH_UTILS_H
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
struct CMeshUtilsSettings
|
||||
{
|
||||
CMeshUtilsSettings();
|
||||
|
||||
// Absolute Paths
|
||||
std::string SourceFilePath;
|
||||
std::string DestinationDirectoryPath;
|
||||
std::string ToolDependLog;
|
||||
std::string ToolErrorLog;
|
||||
|
||||
// Relative Directories
|
||||
/*std::string ShapeDirectory;
|
||||
std::string IGDirectory;
|
||||
std::string SkelDirectory;*/
|
||||
};
|
||||
|
||||
int exportScene(const CMeshUtilsSettings &settings);
|
||||
|
||||
#endif /* NL_MESH_UTILS_H */
|
||||
|
||||
/* end of file */
|
30
code/nel/tools/3d/mesh_utils/scene_context.cpp
Normal file
30
code/nel/tools/3d/mesh_utils/scene_context.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "scene_context.h"
|
||||
|
||||
#include <nel/misc/debug.h>
|
||||
#include <nel/misc/path.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace NLMISC;
|
||||
|
||||
void dummy_scene_context_cpp();
|
||||
|
||||
/* end of file */
|
81
code/nel/tools/3d/mesh_utils/scene_context.h
Normal file
81
code/nel/tools/3d/mesh_utils/scene_context.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef NL_SCENE_CONTEXT_H
|
||||
#define NL_SCENE_CONTEXT_H
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
#include "mesh_utils.h"
|
||||
#include "scene_meta.h"
|
||||
|
||||
#include <nel/misc/sstring.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
#include <nel/misc/smart_ptr.h>
|
||||
#include <nel/misc/matrix.h>
|
||||
|
||||
#include <nel/3d/shape.h>
|
||||
|
||||
#ifndef NL_NODE_INTERNAL_TYPE
|
||||
#define NL_NODE_INTERNAL_TYPE void
|
||||
#endif
|
||||
#ifndef NL_SCENE_INTERNAL_TYPE
|
||||
#define NL_SCENE_INTERNAL_TYPE void
|
||||
#endif
|
||||
|
||||
namespace NL3D {
|
||||
class IShape;
|
||||
class CMaterial;
|
||||
}
|
||||
|
||||
struct CNodeContext
|
||||
{
|
||||
CNodeContext() :
|
||||
InternalNode(NULL),
|
||||
IsBone(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const NL_NODE_INTERNAL_TYPE *InternalNode;
|
||||
bool IsBone;
|
||||
|
||||
// NLMISC::CMatrix Transform; // TODO
|
||||
NLMISC::CSmartPtr<NL3D::IShape> Shape;
|
||||
};
|
||||
|
||||
typedef std::map<NLMISC::CSString, CNodeContext> TNodeContextMap;
|
||||
struct CMeshUtilsContext
|
||||
{
|
||||
CMeshUtilsContext(const CMeshUtilsSettings &settings) : Settings(settings), InternalScene(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const CMeshUtilsSettings &Settings;
|
||||
|
||||
NLMISC::CToolLogger ToolLogger;
|
||||
|
||||
const NL_SCENE_INTERNAL_TYPE *InternalScene;
|
||||
CSceneMeta SceneMeta;
|
||||
|
||||
TNodeContextMap Nodes; // Impl note: Should never end up containing the scene root node.
|
||||
// std::map<const aiMesh *, NLMISC::CSString> MeshNames; // Maps meshes to a node name ********************* todo ***************
|
||||
};
|
||||
|
||||
#endif /* NL_SCENE_CONTEXT_H */
|
||||
|
||||
/* end of file */
|
100
code/nel/tools/3d/mesh_utils/scene_meta.cpp
Normal file
100
code/nel/tools/3d/mesh_utils/scene_meta.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <nel/misc/types_nl.h>
|
||||
#include "scene_meta.h"
|
||||
|
||||
#include <nel/misc/debug.h>
|
||||
#include <nel/misc/stream.h>
|
||||
#include <nel/misc/file.h>
|
||||
|
||||
#include <nel/3d/material.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace NLMISC;
|
||||
|
||||
CNodeMeta::CNodeMeta() :
|
||||
AddToIG(true),
|
||||
ExportMesh(TMeshShape),
|
||||
ExportBone(TBoneAuto),
|
||||
AutoAnim(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CNodeMeta::serial(NLMISC::IStream &s)
|
||||
{
|
||||
uint version = s.serialVersion(1);
|
||||
s.serial(AddToIG);
|
||||
s.serial((uint32 &)ExportMesh);
|
||||
s.serial((uint32 &)ExportBone);
|
||||
s.serial(InstanceShape);
|
||||
s.serial(InstanceName);
|
||||
s.serial(InstanceGroupName);
|
||||
s.serial(AutoAnim);
|
||||
}
|
||||
|
||||
CSceneMeta::CSceneMeta() :
|
||||
ImportShape(true),
|
||||
ImportSkel(true),
|
||||
ImportAnim(true),
|
||||
ImportCmb(true),
|
||||
ImportIG(true),
|
||||
ExportDefaultIG(false),
|
||||
SkeletonMode(TSkelRoot)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CSceneMeta::load(const std::string &filePath)
|
||||
{
|
||||
m_MetaFilePath = NLMISC::CPath::standardizePath(filePath + ".nelmeta", false);
|
||||
if (CFile::fileExists(m_MetaFilePath))
|
||||
{
|
||||
CIFile f(m_MetaFilePath);
|
||||
serial(f);
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CSceneMeta::save()
|
||||
{
|
||||
COFile f(m_MetaFilePath, false, false, true);
|
||||
serial(f);
|
||||
f.close();
|
||||
}
|
||||
|
||||
void CSceneMeta::serial(NLMISC::IStream &s)
|
||||
{
|
||||
uint version = s.serialVersion(1);
|
||||
|
||||
s.serial(ImportShape);
|
||||
s.serial(ImportSkel);
|
||||
s.serial(ImportAnim);
|
||||
s.serial(ImportCmb);
|
||||
s.serial(ImportIG);
|
||||
|
||||
s.serial(ExportDefaultIG);
|
||||
s.serial((uint32 &)SkeletonMode);
|
||||
|
||||
s.serialCont(Nodes);
|
||||
s.serialPtrCont(Materials);
|
||||
}
|
||||
|
||||
/* end of file */
|
108
code/nel/tools/3d/mesh_utils/scene_meta.h
Normal file
108
code/nel/tools/3d/mesh_utils/scene_meta.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2015 Winch Gate Property Limited
|
||||
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef NL_SCENE_META_H
|
||||
#define NL_SCENE_META_H
|
||||
#include <nel/misc/types_nl.h>
|
||||
|
||||
#include <nel/misc/sstring.h>
|
||||
#include <nel/misc/smart_ptr.h>
|
||||
|
||||
#include <nel/3d/material.h>
|
||||
|
||||
namespace NLMISC {
|
||||
class IStream;
|
||||
}
|
||||
|
||||
namespace NL3D {
|
||||
class CMaterial;
|
||||
}
|
||||
|
||||
enum TMesh
|
||||
{
|
||||
TMeshDisabled = 0,
|
||||
TMeshShape = 1,
|
||||
TMeshCollisionInt = 2,
|
||||
TMeshCollisionExt = 3,
|
||||
TMeshZone = 4,
|
||||
TMeshPortal = 5,
|
||||
TMeshCluster = 6,
|
||||
};
|
||||
|
||||
enum TBone
|
||||
{
|
||||
TBoneAuto = 0,
|
||||
TBoneForce = 1, // Force this node to be part of a skeleton
|
||||
TBoneRoot = 2, // Make this node the skeleton root, it will be exported using the scene name. There can only be one (editor should keep track and disable)
|
||||
};
|
||||
|
||||
struct CNodeMeta
|
||||
{
|
||||
CNodeMeta();
|
||||
|
||||
bool AddToIG; // Add this node to an instance group
|
||||
TMesh ExportMesh;
|
||||
TBone ExportBone;
|
||||
|
||||
std::string InstanceShape;
|
||||
std::string InstanceName;
|
||||
std::string InstanceGroupName;
|
||||
|
||||
bool AutoAnim;
|
||||
// std::vector<NLMISC::CSString> Materials; // In case there's an issue with nameless materials in some format... Map to material entirely in the meta editor.
|
||||
|
||||
void serial(NLMISC::IStream &s);
|
||||
};
|
||||
|
||||
enum TSkel
|
||||
{
|
||||
TSkelLocal = 0, // Export smallest skeleton possible from connected bones
|
||||
TSkelRoot = 1, // Export skeleton from a direct child node in the scene root node
|
||||
TSkelFull = 2, // Include all connected child nodes in the skeleton
|
||||
};
|
||||
|
||||
typedef std::map<NLMISC::CSString, NLMISC::CSmartPtr<NL3D::CMaterial> > TMaterialMap;
|
||||
struct CSceneMeta
|
||||
{
|
||||
CSceneMeta();
|
||||
|
||||
bool ImportShape;
|
||||
bool ImportSkel;
|
||||
bool ImportAnim;
|
||||
bool ImportCmb;
|
||||
bool ImportIG;
|
||||
|
||||
bool ExportDefaultIG; // Export a default instance group from nodes the scene that do not have an instance group set
|
||||
TSkel SkeletonMode;
|
||||
|
||||
std::map<NLMISC::CSString, CNodeMeta> Nodes;
|
||||
TMaterialMap Materials;
|
||||
|
||||
const std::string &metaFilePath() const { return m_MetaFilePath; }
|
||||
|
||||
bool load(const std::string &filePath);
|
||||
void save();
|
||||
void serial(NLMISC::IStream &s);
|
||||
|
||||
private:
|
||||
std::string m_MetaFilePath;
|
||||
|
||||
};
|
||||
|
||||
#endif NL_SCENE_META_H
|
||||
|
||||
/* end of file */
|
Loading…
Reference in a new issue