From 8179ee383611824d4f37c53ec1d253a1ed204e93 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 19 Sep 2015 16:46:59 +0200 Subject: [PATCH 01/21] Add assimp dependency --HG-- branch : feature-export-assimp --- code/CMakeLists.txt | 4 ++++ code/CMakeModules/Findassimp.cmake | 25 +++++++++++++++++++++++++ code/CMakeModules/nel.cmake | 2 ++ 3 files changed, 31 insertions(+) create mode 100644 code/CMakeModules/Findassimp.cmake diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 440a5fcd3..32e5e6591 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -146,6 +146,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) diff --git a/code/CMakeModules/Findassimp.cmake b/code/CMakeModules/Findassimp.cmake new file mode 100644 index 000000000..6748cd221 --- /dev/null +++ b/code/CMakeModules/Findassimp.cmake @@ -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) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 52ee640e9..28e36bd61 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -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 ### From d55124a6a94c3263216d91f63c28ff9a71e13246 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 19 Sep 2015 17:14:35 +0200 Subject: [PATCH 02/21] Placeholder projects --HG-- branch : feature-export-assimp --- code/nel/tools/3d/CMakeLists.txt | 6 ++++- code/nel/tools/3d/mesh_export/CMakeLists.txt | 12 +++++++++ code/nel/tools/3d/mesh_export/main.cpp | 26 ++++++++++++++++++++ code/nel/tools/3d/mesh_utils/CMakeLists.txt | 16 ++++++++++++ code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 23 +++++++++++++++++ code/nel/tools/3d/mesh_utils/mesh_utils.h | 20 +++++++++++++++ 6 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 code/nel/tools/3d/mesh_export/CMakeLists.txt create mode 100644 code/nel/tools/3d/mesh_export/main.cpp create mode 100644 code/nel/tools/3d/mesh_utils/CMakeLists.txt create mode 100644 code/nel/tools/3d/mesh_utils/mesh_utils.cpp create mode 100644 code/nel/tools/3d/mesh_utils/mesh_utils.h diff --git a/code/nel/tools/3d/CMakeLists.txt b/code/nel/tools/3d/CMakeLists.txt index cb709ffaa..37773e9ee 100644 --- a/code/nel/tools/3d/CMakeLists.txt +++ b/code/nel/tools/3d/CMakeLists.txt @@ -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 diff --git a/code/nel/tools/3d/mesh_export/CMakeLists.txt b/code/nel/tools/3d/mesh_export/CMakeLists.txt new file mode 100644 index 000000000..838ce49c2 --- /dev/null +++ b/code/nel/tools/3d/mesh_export/CMakeLists.txt @@ -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) diff --git a/code/nel/tools/3d/mesh_export/main.cpp b/code/nel/tools/3d/mesh_export/main.cpp new file mode 100644 index 000000000..be527719b --- /dev/null +++ b/code/nel/tools/3d/mesh_export/main.cpp @@ -0,0 +1,26 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include "../mesh_utils/mesh_utils.h" + +int main(int argc, char *argv[]) +{ + mesh_utils_placeholder(); + return 0; +} + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/CMakeLists.txt b/code/nel/tools/3d/mesh_utils/CMakeLists.txt new file mode 100644 index 000000000..a5a9fa952 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/CMakeLists.txt @@ -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) diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp new file mode 100644 index 000000000..f3dfaef84 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -0,0 +1,23 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +void mesh_utils_placeholder() +{ + +} + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.h b/code/nel/tools/3d/mesh_utils/mesh_utils.h new file mode 100644 index 000000000..8110ef8f4 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.h @@ -0,0 +1,20 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +void mesh_utils_placeholder(); + +/* end of file */ From deca2304c4aa14dae1039eb95b78776570831343 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 19 Sep 2015 17:15:06 +0200 Subject: [PATCH 03/21] Editor config script --HG-- branch : feature-export-assimp --- .editorconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..3fd6967c7 --- /dev/null +++ b/.editorconfig @@ -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 From 7d502cc6bbb1016ecea73b4dffbe24f91df4fe67 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 19 Sep 2015 18:08:25 +0200 Subject: [PATCH 04/21] Command line options --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_export/main.cpp | 39 +++++++++++++++++++-- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 7 ++-- code/nel/tools/3d/mesh_utils/mesh_utils.h | 12 ++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/code/nel/tools/3d/mesh_export/main.cpp b/code/nel/tools/3d/mesh_export/main.cpp index be527719b..e83727a5c 100644 --- a/code/nel/tools/3d/mesh_export/main.cpp +++ b/code/nel/tools/3d/mesh_export/main.cpp @@ -15,12 +15,47 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#include #include "../mesh_utils/mesh_utils.h" +#include +#include + +int printHelp(const NLMISC::CCmdArgs &args) +{ + printf("NeL Mesh Export\n"); + return EXIT_SUCCESS; +} + int main(int argc, char *argv[]) { - mesh_utils_placeholder(); - return 0; + 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; + + settings.DestinationDirectory = args.getArg('d'); + if (settings.DestinationDirectory.empty()) + settings.DestinationDirectory = args.getLongArg("dst"); + if (settings.DestinationDirectory.empty()) + settings.DestinationDirectory = filePath + "_export"; + settings.DestinationDirectory += "/"; + + return exportScene(settings); } /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index f3dfaef84..2430b1dc9 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -15,9 +15,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -void mesh_utils_placeholder() +#include +#include "mesh_utils.h" + +int exportScene(CMeshUtilsSettings &settings) { - + return EXIT_SUCCESS; } /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.h b/code/nel/tools/3d/mesh_utils/mesh_utils.h index 8110ef8f4..f021f66f8 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.h +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.h @@ -15,6 +15,16 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -void mesh_utils_placeholder(); +#include + +#include + +struct CMeshUtilsSettings +{ + std::string SourceFilePath; + std::string DestinationDirectory; +}; + +int exportScene(CMeshUtilsSettings &settings); /* end of file */ From 875d7eaa7c2ccdc7a72cf572c7bae6af34c41edc Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 19 Sep 2015 19:35:57 +0200 Subject: [PATCH 05/21] Flag bones in scene for skel export --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_export/main.cpp | 21 +++- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 132 +++++++++++++++++++- code/nel/tools/3d/mesh_utils/mesh_utils.h | 12 +- 3 files changed, 156 insertions(+), 9 deletions(-) diff --git a/code/nel/tools/3d/mesh_export/main.cpp b/code/nel/tools/3d/mesh_export/main.cpp index e83727a5c..2e97be830 100644 --- a/code/nel/tools/3d/mesh_export/main.cpp +++ b/code/nel/tools/3d/mesh_export/main.cpp @@ -21,6 +21,9 @@ #include #include +#include +#include + int printHelp(const NLMISC::CCmdArgs &args) { printf("NeL Mesh Export\n"); @@ -29,6 +32,8 @@ int printHelp(const NLMISC::CCmdArgs &args) int main(int argc, char *argv[]) { + NLMISC::CApplicationContext app; + NLMISC::CCmdArgs args; args.setArgs(argc, (const char **)argv); @@ -48,12 +53,16 @@ int main(int argc, char *argv[]) CMeshUtilsSettings settings; settings.SourceFilePath = filePath; - settings.DestinationDirectory = args.getArg('d'); - if (settings.DestinationDirectory.empty()) - settings.DestinationDirectory = args.getLongArg("dst"); - if (settings.DestinationDirectory.empty()) - settings.DestinationDirectory = filePath + "_export"; - settings.DestinationDirectory += "/"; + 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 += "/"; + + NL3D::CScene::registerBasics(); + NL3D::registerSerial3d(); return exportScene(settings); } diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 2430b1dc9..60e5c7201 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -18,8 +18,138 @@ #include #include "mesh_utils.h" -int exportScene(CMeshUtilsSettings &settings) +#include + +#include +#include +#include + +CMeshUtilsSettings::CMeshUtilsSettings() { + ShapeDirectory = "shape"; + IGDirectory = "ig"; + SkelDirectory = "skel"; +} + +struct CNodeContext +{ + CNodeContext() : + AssimpNode(NULL), + IsBone(false) + { + + } + + const aiNode *AssimpNode; + bool IsBone; +}; + +struct CMeshUtilsContext +{ + CMeshUtilsContext(const CMeshUtilsSettings &settings) : Settings(settings), AssimpScene(NULL) + { + + } + + const CMeshUtilsSettings &Settings; + + const aiScene *AssimpScene; + std::map Nodes; +}; + +struct CNodeMeta +{ + // TODO +}; +static const CNodeMeta g_DefaultMeta; + +void importNode(CMeshUtilsContext &context, const aiNode *node, const CNodeMeta *meta) +{ + if (node->mNumMeshes) + { + // TODO + } + + for (unsigned int i = 0; i < node->mNumChildren; ++i) + importNode(context, node->mChildren[i], &g_DefaultMeta); +} + +void validateAssimpNodeNames(CMeshUtilsContext &context, const aiNode *node) +{ + if (!node->mParent || node == context.AssimpScene->mRootNode) + { + // do nothing + } + else if (node->mName.length == 0) + { + nlwarning("CRITICAL: Node has no name"); + } + else + { + CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()]; + + if (nodeContext.AssimpNode && nodeContext.AssimpNode != node) + { + nlwarning("CRITICAL: Node name '%s' appears multiple times", node->mName.C_Str()); + } + else + { + nodeContext.AssimpNode = node; + } + } + + for (unsigned int i = 0; i < node->mNumChildren; ++i) + validateAssimpNodeNames(context, node->mChildren[i]); +} + +void flagAssimpBones(CMeshUtilsContext &context) +{ + // Find out which nodes are bones by checking the mesh meta info + const aiScene *scene = context.AssimpScene; + 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[i]->mName.C_Str()]; + if (!nodeContext.AssimpNode) + { + nlwarning("CRITICAL: Bone '%s' has no associated node", mesh->mBones[i]->mName.C_Str()); + } + else + { + // Flag as bone + nodeContext.IsBone = true; + + // Flag all parents as bones + const aiNode *parent = nodeContext.AssimpNode; + while (parent = parent->mParent) if (parent->mName.length) + { + context.Nodes[parent->mName.C_Str()].IsBone = true; + } + } + } + } +} + +// TODO: Separate load scene and save scene functions +int exportScene(const CMeshUtilsSettings &settings) +{ + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(settings.SourceFilePath, aiProcess_Triangulate | aiProcess_ValidateDataStructure); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights + // 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 + + CMeshUtilsContext context(settings); + context.AssimpScene = scene; + + flagAssimpBones(context); + + importNode(context, scene->mRootNode, &g_DefaultMeta); + return EXIT_SUCCESS; } diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.h b/code/nel/tools/3d/mesh_utils/mesh_utils.h index f021f66f8..b7c549689 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.h +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.h @@ -21,10 +21,18 @@ struct CMeshUtilsSettings { + CMeshUtilsSettings(); + + // Absolute Paths std::string SourceFilePath; - std::string DestinationDirectory; + std::string DestinationDirectoryPath; + + // Relative Directories + std::string ShapeDirectory; + std::string IGDirectory; + std::string SkelDirectory; }; -int exportScene(CMeshUtilsSettings &settings); +int exportScene(const CMeshUtilsSettings &settings); /* end of file */ From 8036d5341143d581c254d0b87fb7c3aa14a5a6a8 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 14:08:38 +0200 Subject: [PATCH 06/21] Add pipeline v3 tool logger --HG-- branch : feature-export-assimp --- code/nel/include/nel/misc/tool_logger.h | 210 ++++++++++++++++++++ code/nel/src/misc/tool_logger.cpp | 45 +++++ code/nel/tools/3d/mesh_export/main.cpp | 7 + code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 39 +++- code/nel/tools/3d/mesh_utils/mesh_utils.h | 2 + 5 files changed, 296 insertions(+), 7 deletions(-) create mode 100644 code/nel/include/nel/misc/tool_logger.h create mode 100644 code/nel/src/misc/tool_logger.cpp diff --git a/code/nel/include/nel/misc/tool_logger.h b/code/nel/include/nel/misc/tool_logger.h new file mode 100644 index 000000000..85171095c --- /dev/null +++ b/code/nel/include/nel/misc/tool_logger.h @@ -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 + * . + */ + +#ifndef NLMISC_TOOL_LOGGER_H +#define NLMISC_TOOL_LOGGER_H +#include + +// STL includes +#include +#include +#include + +// NeL includes +#include +#include + +// 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() + { + + } + + 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 */ diff --git a/code/nel/src/misc/tool_logger.cpp b/code/nel/src/misc/tool_logger.cpp new file mode 100644 index 000000000..e6a9fbf36 --- /dev/null +++ b/code/nel/src/misc/tool_logger.cpp @@ -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 + * . + */ + +#include "stdmisc.h" +#include "nel/misc/tool_logger.h" + +// STL includes + +// NeL includes +// #include + +// 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 */ diff --git a/code/nel/tools/3d/mesh_export/main.cpp b/code/nel/tools/3d/mesh_export/main.cpp index 2e97be830..1503a4b16 100644 --- a/code/nel/tools/3d/mesh_export/main.cpp +++ b/code/nel/tools/3d/mesh_export/main.cpp @@ -61,6 +61,13 @@ int main(int argc, char *argv[]) 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(); diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 60e5c7201..c8a0eb12d 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -19,6 +19,8 @@ #include "mesh_utils.h" #include +#include +#include #include #include @@ -52,9 +54,12 @@ struct CMeshUtilsContext } const CMeshUtilsSettings &Settings; + + NLMISC::CToolLogger ToolLogger; const aiScene *AssimpScene; - std::map Nodes; + std::map Nodes; + std::map MeshNames; // Maps meshes to a node name ********************* todo *************** }; struct CNodeMeta @@ -82,7 +87,8 @@ void validateAssimpNodeNames(CMeshUtilsContext &context, const aiNode *node) } else if (node->mName.length == 0) { - nlwarning("CRITICAL: Node has no name"); + tlwarning(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "Node has no name"); } else { @@ -90,7 +96,8 @@ void validateAssimpNodeNames(CMeshUtilsContext &context, const aiNode *node) if (nodeContext.AssimpNode && nodeContext.AssimpNode != node) { - nlwarning("CRITICAL: Node name '%s' appears multiple times", node->mName.C_Str()); + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "Node name '%s' appears multiple times", node->mName.C_Str()); } else { @@ -115,7 +122,8 @@ void flagAssimpBones(CMeshUtilsContext &context) CNodeContext &nodeContext = context.Nodes[mesh->mBones[i]->mName.C_Str()]; if (!nodeContext.AssimpNode) { - nlwarning("CRITICAL: Bone '%s' has no associated node", mesh->mBones[i]->mName.C_Str()); + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "Bone '%s' has no associated node", mesh->mBones[i]->mName.C_Str()); } else { @@ -123,11 +131,11 @@ void flagAssimpBones(CMeshUtilsContext &context) nodeContext.IsBone = true; // Flag all parents as bones - const aiNode *parent = nodeContext.AssimpNode; + /*const aiNode *parent = nodeContext.AssimpNode; while (parent = parent->mParent) if (parent->mName.length) { context.Nodes[parent->mName.C_Str()].IsBone = true; - } + }*/ } } } @@ -136,14 +144,31 @@ void flagAssimpBones(CMeshUtilsContext &context) // TODO: Separate load scene and save scene functions int exportScene(const CMeshUtilsSettings &settings) { + CMeshUtilsContext context(settings); + + if (!settings.ToolDependLog.empty()) + context.ToolLogger.initDepend(settings.ToolDependLog); + if (!settings.ToolErrorLog.empty()) + context.ToolLogger.initDepend(settings.ToolErrorLog); + context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.Settings.SourceFilePath.c_str()); // Base input file + + // Find database configuration + + Assimp::Importer importer; const aiScene *scene = importer.ReadFile(settings.SourceFilePath, aiProcess_Triangulate | aiProcess_ValidateDataStructure); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights + if (!scene) + { + const char *errs = importer.GetErrorString(); + if (errs) tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), errs); + else tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), "Unable to load scene"); + return EXIT_FAILURE; + } // 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 - CMeshUtilsContext context(settings); context.AssimpScene = scene; flagAssimpBones(context); diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.h b/code/nel/tools/3d/mesh_utils/mesh_utils.h index b7c549689..1af319dec 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.h +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.h @@ -26,6 +26,8 @@ struct CMeshUtilsSettings // Absolute Paths std::string SourceFilePath; std::string DestinationDirectoryPath; + std::string ToolDependLog; + std::string ToolErrorLog; // Relative Directories std::string ShapeDirectory; From dec6c2d88080f43c55cdc932e872c805d8f50ffd Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 15:23:27 +0200 Subject: [PATCH 07/21] Centralized asset database configuration --HG-- branch : feature-export-assimp --- code/nel/include/nel/misc/tool_logger.h | 2 +- .../tools/3d/mesh_utils/database_config.cpp | 107 ++++++++++++++++++ .../nel/tools/3d/mesh_utils/database_config.h | 58 ++++++++++ code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 22 ++-- code/nel/tools/3d/mesh_utils/mesh_utils.h | 4 +- 5 files changed, 182 insertions(+), 11 deletions(-) create mode 100644 code/nel/tools/3d/mesh_utils/database_config.cpp create mode 100644 code/nel/tools/3d/mesh_utils/database_config.h diff --git a/code/nel/include/nel/misc/tool_logger.h b/code/nel/include/nel/misc/tool_logger.h index 85171095c..3ef547045 100644 --- a/code/nel/include/nel/misc/tool_logger.h +++ b/code/nel/include/nel/misc/tool_logger.h @@ -88,7 +88,7 @@ private: FILE *m_DependLog; public: - inline CToolLogger() + inline CToolLogger() : m_ErrorLog(NULL), m_DependLog(NULL) { } diff --git a/code/nel/tools/3d/mesh_utils/database_config.cpp b/code/nel/tools/3d/mesh_utils/database_config.cpp new file mode 100644 index 000000000..7e6ea5b37 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/database_config.cpp @@ -0,0 +1,107 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include +#include "database_config.h" + +#include +#include +#include + +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 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 */ diff --git a/code/nel/tools/3d/mesh_utils/database_config.h b/code/nel/tools/3d/mesh_utils/database_config.h new file mode 100644 index 000000000..25bcc2a00 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/database_config.h @@ -0,0 +1,58 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include + +namespace NLMISC { + class CConfigFile; +} + +#ifdef NL_OS_WINDOWS +#include +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 */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index c8a0eb12d..4af32091b 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -22,15 +22,17 @@ #include #include +#include "database_config.h" + #include #include #include CMeshUtilsSettings::CMeshUtilsSettings() { - ShapeDirectory = "shape"; + /*ShapeDirectory = "shape"; IGDirectory = "ig"; - SkelDirectory = "skel"; + SkelDirectory = "skel";*/ } struct CNodeContext @@ -119,11 +121,11 @@ void flagAssimpBones(CMeshUtilsContext &context) const aiMesh *mesh = scene->mMeshes[i]; for (unsigned int j = 0; j < mesh->mNumBones; ++j) { - CNodeContext &nodeContext = context.Nodes[mesh->mBones[i]->mName.C_Str()]; + CNodeContext &nodeContext = context.Nodes[mesh->mBones[j]->mName.C_Str()]; if (!nodeContext.AssimpNode) { tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), - "Bone '%s' has no associated node", mesh->mBones[i]->mName.C_Str()); + "Bone '%s' has no associated node", mesh->mBones[j]->mName.C_Str()); } else { @@ -145,25 +147,28 @@ void flagAssimpBones(CMeshUtilsContext &context) 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.initDepend(settings.ToolErrorLog); + context.ToolLogger.initError(settings.ToolErrorLog); context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.Settings.SourceFilePath.c_str()); // Base input file - // Find database configuration - + // Apply database configuration + CDatabaseConfig::init(settings.SourceFilePath); + CDatabaseConfig::initTextureSearchDirectories(); Assimp::Importer importer; const aiScene *scene = importer.ReadFile(settings.SourceFilePath, aiProcess_Triangulate | aiProcess_ValidateDataStructure); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights if (!scene) { const char *errs = importer.GetErrorString(); - if (errs) tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), errs); + 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 @@ -171,6 +176,7 @@ int exportScene(const CMeshUtilsSettings &settings) context.AssimpScene = scene; + validateAssimpNodeNames(context, context.AssimpScene->mRootNode); flagAssimpBones(context); importNode(context, scene->mRootNode, &g_DefaultMeta); diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.h b/code/nel/tools/3d/mesh_utils/mesh_utils.h index 1af319dec..de6dd63fc 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.h +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.h @@ -30,9 +30,9 @@ struct CMeshUtilsSettings std::string ToolErrorLog; // Relative Directories - std::string ShapeDirectory; + /*std::string ShapeDirectory; std::string IGDirectory; - std::string SkelDirectory; + std::string SkelDirectory;*/ }; int exportScene(const CMeshUtilsSettings &settings); From fec41ebda9cdd3d6db3da840d4ffd38b1b0c2b77 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 16:44:56 +0200 Subject: [PATCH 08/21] Scene meta data --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 95 ++++++++++++++++++--- code/nel/tools/3d/mesh_utils/scene_meta.cpp | 84 ++++++++++++++++++ code/nel/tools/3d/mesh_utils/scene_meta.h | 83 ++++++++++++++++++ 3 files changed, 251 insertions(+), 11 deletions(-) create mode 100644 code/nel/tools/3d/mesh_utils/scene_meta.cpp create mode 100644 code/nel/tools/3d/mesh_utils/scene_meta.h diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 4af32091b..57fea70e6 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -23,6 +23,7 @@ #include #include "database_config.h" +#include "scene_meta.h" #include #include @@ -48,6 +49,7 @@ struct CNodeContext bool IsBone; }; +typedef std::map TNodeContextMap; struct CMeshUtilsContext { CMeshUtilsContext(const CMeshUtilsSettings &settings) : Settings(settings), AssimpScene(NULL) @@ -60,17 +62,13 @@ struct CMeshUtilsContext NLMISC::CToolLogger ToolLogger; const aiScene *AssimpScene; - std::map Nodes; - std::map MeshNames; // Maps meshes to a node name ********************* todo *************** + CSceneMeta SceneMeta; + + TNodeContextMap Nodes; + // std::map MeshNames; // Maps meshes to a node name ********************* todo *************** }; -struct CNodeMeta -{ - // TODO -}; -static const CNodeMeta g_DefaultMeta; - -void importNode(CMeshUtilsContext &context, const aiNode *node, const CNodeMeta *meta) +void importNode(CMeshUtilsContext &context, const aiNode *node) { if (node->mNumMeshes) { @@ -78,7 +76,7 @@ void importNode(CMeshUtilsContext &context, const aiNode *node, const CNodeMeta } for (unsigned int i = 0; i < node->mNumChildren; ++i) - importNode(context, node->mChildren[i], &g_DefaultMeta); + importNode(context, node->mChildren[i]); } void validateAssimpNodeNames(CMeshUtilsContext &context, const aiNode *node) @@ -143,6 +141,77 @@ void flagAssimpBones(CMeshUtilsContext &context) } } +void flagRecursiveBones(CMeshUtilsContext &context, CNodeContext &nodeContext) +{ + nodeContext.IsBone = true; + const aiNode *node = nodeContext.AssimpNode; + nlassert(node); + for (unsigned int i = 0; i < node->mNumChildren; ++i) + flagRecursiveBones(context, context.Nodes[node->mName.C_Str()]); +} + +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.AssimpNode; +} + +void flagAllParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext) +{ + const aiNode *parent = nodeContext.AssimpNode; + while (parent = parent->mParent) if (parent->mName.length) + context.Nodes[parent->mName.C_Str()].IsBone = true; +} + +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) + { + + } + } + break; + case TSkelRoot: + for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it) + { + CNodeContext &nodeContext = it->second; + if (nodeContext.IsBone) + { + + } + } + break; + case TSkelFull: + for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it) + { + CNodeContext &nodeContext = it->second; + if (nodeContext.IsBone) + { + + } + } + break; + } +} + // TODO: Separate load scene and save scene functions int exportScene(const CMeshUtilsSettings &settings) { @@ -175,11 +244,15 @@ int exportScene(const CMeshUtilsSettings &settings) //scene->mRootNode->mMetaData context.AssimpScene = scene; + if (context.SceneMeta.load(context.Settings.SourceFilePath)) + context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.SceneMeta.metaFilePath().c_str()); // Meta input file validateAssimpNodeNames(context, context.AssimpScene->mRootNode); flagAssimpBones(context); + flagMetaBones(context); + flagExpandedBones(context); - importNode(context, scene->mRootNode, &g_DefaultMeta); + importNode(context, scene->mRootNode); return EXIT_SUCCESS; } diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.cpp b/code/nel/tools/3d/mesh_utils/scene_meta.cpp new file mode 100644 index 000000000..19260d201 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/scene_meta.cpp @@ -0,0 +1,84 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include +#include "scene_meta.h" + +#include +#include +#include + +using namespace std; +using namespace NLMISC; + +CNodeMeta::CNodeMeta() : + AddToIG(true), + ExportMesh(TMeshShape), + ExportBone(TBoneAuto) +{ + +} + +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); +} + +CSceneMeta::CSceneMeta() : + DefaultInstanceGroup(false), + SkeletonMode(TSkelLocal) +{ + +} + +bool CSceneMeta::load(const std::string &filePath) +{ + m_MetaFilePath = filePath + ".nelmeta"; + 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(DefaultInstanceGroup); + s.serial((uint32 &)SkeletonMode); + + s.serialCont(Nodes); +} + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h new file mode 100644 index 000000000..a3791bb50 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -0,0 +1,83 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include + +#include + +namespace NLMISC { + class IStream; +} + +enum TMesh +{ + TMeshDisabled = 0, + TMeshShape = 1, + TMeshCollisionInt = 2, + TMeshCollisionExt = 3, + TMeshZone = 4, +}; + +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; + + void serial(NLMISC::IStream &s); +}; + +enum TSkel +{ + TSkelLocal = 0, // Export smallest skeleton possible from connected bones + TSkelRoot = 1, // Export skeleton from direct child node in the scene root node + TSkelFull = 2, // Include all connected child nodes in the skeleton +}; + +struct CSceneMeta +{ + CSceneMeta(); + + bool DefaultInstanceGroup; // Export a default instance group from nodes the scene that do not have an instance group set + TSkel SkeletonMode; + std::map Nodes; + + 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; + +}; + +/* end of file */ From 33dd77d516875441a382fcb5ab9505b68aaecb60 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 17:03:44 +0200 Subject: [PATCH 09/21] Skeleton scan --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 63 +++++++++++++++------ code/nel/tools/3d/mesh_utils/scene_meta.cpp | 2 +- code/nel/tools/3d/mesh_utils/scene_meta.h | 2 +- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 57fea70e6..011ed36f6 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -64,7 +64,7 @@ struct CMeshUtilsContext const aiScene *AssimpScene; CSceneMeta SceneMeta; - TNodeContextMap Nodes; + TNodeContextMap Nodes; // Impl note: Should never end up containing the scene root node. // std::map MeshNames; // Maps meshes to a node name ********************* todo *************** }; @@ -139,15 +139,23 @@ void flagAssimpBones(CMeshUtilsContext &context) } } } + + // Find out which nodes are bones by checking the animation info + // TODO } -void flagRecursiveBones(CMeshUtilsContext &context, CNodeContext &nodeContext) +void flagRecursiveBones(CMeshUtilsContext &context, CNodeContext &nodeContext, bool autoStop = false) { nodeContext.IsBone = true; const aiNode *node = nodeContext.AssimpNode; nlassert(node); for (unsigned int i = 0; i < node->mNumChildren; ++i) - flagRecursiveBones(context, context.Nodes[node->mName.C_Str()]); + { + CNodeContext &ctx = context.Nodes[node->mName.C_Str()]; + if (autoStop && ctx.IsBone) + continue; + flagRecursiveBones(context, ctx); + } } void flagMetaBones(CMeshUtilsContext &context) @@ -168,11 +176,24 @@ void flagLocalParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext) const aiNode *node = nodeContext.AssimpNode; } -void flagAllParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext) +void flagAllParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext, bool autoStop = false) { const aiNode *parent = nodeContext.AssimpNode; - while (parent = parent->mParent) if (parent->mName.length) - context.Nodes[parent->mName.C_Str()].IsBone = true; + while (parent = parent->mParent) if (parent->mName.length && parent != context.AssimpScene->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.AssimpNode; + while (parent = parent->mParent) if (parent->mName.length && parent != context.AssimpScene->mRootNode) + if (context.Nodes[parent->mName.C_Str()].IsBone) return true; + return false; } void flagExpandedBones(CMeshUtilsContext &context) @@ -183,10 +204,8 @@ void flagExpandedBones(CMeshUtilsContext &context) for (TNodeContextMap::iterator it(context.Nodes.begin()), end(context.Nodes.end()); it != end; ++it) { CNodeContext &nodeContext = it->second; - if (nodeContext.IsBone) - { - - } + if (nodeContext.IsBone && hasIndirectParentBone(context, nodeContext)) + flagAllParentBones(context, nodeContext, true); } break; case TSkelRoot: @@ -194,9 +213,7 @@ void flagExpandedBones(CMeshUtilsContext &context) { CNodeContext &nodeContext = it->second; if (nodeContext.IsBone) - { - - } + flagAllParentBones(context, nodeContext, true); } break; case TSkelFull: @@ -204,9 +221,13 @@ void flagExpandedBones(CMeshUtilsContext &context) { 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; } @@ -248,9 +269,19 @@ int exportScene(const CMeshUtilsSettings &settings) context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.SceneMeta.metaFilePath().c_str()); // Meta input file validateAssimpNodeNames(context, context.AssimpScene->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 -- importNode(context, scene->mRootNode); diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.cpp b/code/nel/tools/3d/mesh_utils/scene_meta.cpp index 19260d201..4c3440819 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.cpp +++ b/code/nel/tools/3d/mesh_utils/scene_meta.cpp @@ -46,7 +46,7 @@ void CNodeMeta::serial(NLMISC::IStream &s) CSceneMeta::CSceneMeta() : DefaultInstanceGroup(false), - SkeletonMode(TSkelLocal) + SkeletonMode(TSkelRoot) { } diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h index a3791bb50..04c5660c0 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.h +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -57,7 +57,7 @@ struct CNodeMeta enum TSkel { TSkelLocal = 0, // Export smallest skeleton possible from connected bones - TSkelRoot = 1, // Export skeleton from direct child node in the scene root node + TSkelRoot = 1, // Export skeleton from a direct child node in the scene root node TSkelFull = 2, // Include all connected child nodes in the skeleton }; From e60a9747b8ffbe8892a39497859909ae08901d8e Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 17:22:24 +0200 Subject: [PATCH 10/21] Restructure --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 41 +++++++++ code/nel/tools/3d/mesh_utils/assimp_shape.h | 25 ++++++ code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 88 ++++++++----------- code/nel/tools/3d/mesh_utils/mesh_utils.h | 4 + .../nel/tools/3d/mesh_utils/scene_context.cpp | 30 +++++++ code/nel/tools/3d/mesh_utils/scene_context.h | 69 +++++++++++++++ code/nel/tools/3d/mesh_utils/scene_meta.h | 4 + 7 files changed, 208 insertions(+), 53 deletions(-) create mode 100644 code/nel/tools/3d/mesh_utils/assimp_shape.cpp create mode 100644 code/nel/tools/3d/mesh_utils/assimp_shape.h create mode 100644 code/nel/tools/3d/mesh_utils/scene_context.cpp create mode 100644 code/nel/tools/3d/mesh_utils/scene_context.h diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp new file mode 100644 index 000000000..5c43c3b38 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -0,0 +1,41 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include +#include "assimp_shape.h" + +#include +#include +#include + +#define NL_NODE_INTERNAL_TYPE aiNode +#define NL_SCENE_INTERNAL_TYPE aiScene +#include "scene_context.h" + +#include +#include +#include + +using namespace std; +using namespace NLMISC; + +void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext) +{ + +} + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.h b/code/nel/tools/3d/mesh_utils/assimp_shape.h new file mode 100644 index 000000000..5f1c3c817 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.h @@ -0,0 +1,25 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include + +struct CMeshUtilsContext; +struct CNodeContext; + +void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext); + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 011ed36f6..46ad1465e 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -29,6 +29,11 @@ #include #include +#define NL_NODE_INTERNAL_TYPE aiNode +#define NL_SCENE_INTERNAL_TYPE aiScene +#include "scene_context.h" +#include "assimp_shape.h" + CMeshUtilsSettings::CMeshUtilsSettings() { /*ShapeDirectory = "shape"; @@ -36,52 +41,29 @@ CMeshUtilsSettings::CMeshUtilsSettings() SkelDirectory = "skel";*/ } -struct CNodeContext +void importShapes(CMeshUtilsContext &context, const aiNode *node) { - CNodeContext() : - AssimpNode(NULL), - IsBone(false) + if (node != context.InternalScene->mRootNode) { - - } - - const aiNode *AssimpNode; - bool IsBone; -}; - -typedef std::map TNodeContextMap; -struct CMeshUtilsContext -{ - CMeshUtilsContext(const CMeshUtilsSettings &settings) : Settings(settings), AssimpScene(NULL) - { - - } - - const CMeshUtilsSettings &Settings; - - NLMISC::CToolLogger ToolLogger; - - const aiScene *AssimpScene; - CSceneMeta SceneMeta; - - TNodeContextMap Nodes; // Impl note: Should never end up containing the scene root node. - // std::map MeshNames; // Maps meshes to a node name ********************* todo *************** -}; - -void importNode(CMeshUtilsContext &context, const aiNode *node) -{ - if (node->mNumMeshes) - { - // TODO + CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()]; + CNodeMeta &nodeMeta = context.SceneMeta.Nodes[node->mName.C_Str()]; + if (nodeMeta.ExportMesh == TMeshShape) + { + 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) - importNode(context, node->mChildren[i]); + importShapes(context, node->mChildren[i]); } -void validateAssimpNodeNames(CMeshUtilsContext &context, const aiNode *node) +void validateInternalNodeNames(CMeshUtilsContext &context, const aiNode *node) { - if (!node->mParent || node == context.AssimpScene->mRootNode) + if (!node->mParent || node == context.InternalScene->mRootNode) { // do nothing } @@ -94,25 +76,25 @@ void validateAssimpNodeNames(CMeshUtilsContext &context, const aiNode *node) { CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()]; - if (nodeContext.AssimpNode && nodeContext.AssimpNode != node) + 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.AssimpNode = node; + nodeContext.InternalNode = node; } } for (unsigned int i = 0; i < node->mNumChildren; ++i) - validateAssimpNodeNames(context, node->mChildren[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.AssimpScene; + 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()); @@ -120,7 +102,7 @@ void flagAssimpBones(CMeshUtilsContext &context) for (unsigned int j = 0; j < mesh->mNumBones; ++j) { CNodeContext &nodeContext = context.Nodes[mesh->mBones[j]->mName.C_Str()]; - if (!nodeContext.AssimpNode) + if (!nodeContext.InternalNode) { tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), "Bone '%s' has no associated node", mesh->mBones[j]->mName.C_Str()); @@ -131,7 +113,7 @@ void flagAssimpBones(CMeshUtilsContext &context) nodeContext.IsBone = true; // Flag all parents as bones - /*const aiNode *parent = nodeContext.AssimpNode; + /*const aiNode *parent = nodeContext.InternalNode; while (parent = parent->mParent) if (parent->mName.length) { context.Nodes[parent->mName.C_Str()].IsBone = true; @@ -147,7 +129,7 @@ void flagAssimpBones(CMeshUtilsContext &context) void flagRecursiveBones(CMeshUtilsContext &context, CNodeContext &nodeContext, bool autoStop = false) { nodeContext.IsBone = true; - const aiNode *node = nodeContext.AssimpNode; + const aiNode *node = nodeContext.InternalNode; nlassert(node); for (unsigned int i = 0; i < node->mNumChildren; ++i) { @@ -173,13 +155,13 @@ void flagMetaBones(CMeshUtilsContext &context) void flagLocalParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext) { - const aiNode *node = nodeContext.AssimpNode; + const aiNode *node = nodeContext.InternalNode; } void flagAllParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext, bool autoStop = false) { - const aiNode *parent = nodeContext.AssimpNode; - while (parent = parent->mParent) if (parent->mName.length && parent != context.AssimpScene->mRootNode) + 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) @@ -190,8 +172,8 @@ void flagAllParentBones(CMeshUtilsContext &context, CNodeContext &nodeContext, b bool hasIndirectParentBone(CMeshUtilsContext &context, CNodeContext &nodeContext) { - const aiNode *parent = nodeContext.AssimpNode; - while (parent = parent->mParent) if (parent->mName.length && parent != context.AssimpScene->mRootNode) + 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; } @@ -264,11 +246,11 @@ int exportScene(const CMeshUtilsSettings &settings) // aiProcess_ImproveCacheLocality: TODO: Verify this does not modify vertex indices //scene->mRootNode->mMetaData - context.AssimpScene = scene; + context.InternalScene = scene; if (context.SceneMeta.load(context.Settings.SourceFilePath)) context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.SceneMeta.metaFilePath().c_str()); // Meta input file - validateAssimpNodeNames(context, context.AssimpScene->mRootNode); + validateInternalNodeNames(context, context.InternalScene->mRootNode); // -- SKEL FLAG -- flagAssimpBones(context); @@ -283,7 +265,7 @@ int exportScene(const CMeshUtilsSettings &settings) // ] // -- SKEL FLAG -- - importNode(context, scene->mRootNode); + importShapes(context, context.InternalScene->mRootNode); return EXIT_SUCCESS; } diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.h b/code/nel/tools/3d/mesh_utils/mesh_utils.h index de6dd63fc..18be11e60 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.h +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.h @@ -15,6 +15,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#ifndef NL_MESH_UTILS_H +#define NL_MESH_UTILS_H #include #include @@ -37,4 +39,6 @@ struct CMeshUtilsSettings int exportScene(const CMeshUtilsSettings &settings); +#endif /* NL_MESH_UTILS_H */ + /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/scene_context.cpp b/code/nel/tools/3d/mesh_utils/scene_context.cpp new file mode 100644 index 000000000..6812a312a --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/scene_context.cpp @@ -0,0 +1,30 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include +#include "scene_context.h" + +#include +#include +#include + +using namespace std; +using namespace NLMISC; + +void dummy_scene_context_cpp(); + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/scene_context.h b/code/nel/tools/3d/mesh_utils/scene_context.h new file mode 100644 index 000000000..0f23f9694 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/scene_context.h @@ -0,0 +1,69 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#ifndef NL_SCENE_CONTEXT_H +#define NL_SCENE_CONTEXT_H +#include + +#include "mesh_utils.h" +#include "scene_meta.h" + +#include +#include + +#ifndef NL_NODE_INTERNAL_TYPE +#define NL_NODE_INTERNAL_TYPE void +#endif +#ifndef NL_SCENE_INTERNAL_TYPE +#define NL_SCENE_INTERNAL_TYPE void +#endif + +struct CNodeContext +{ + CNodeContext() : + InternalNode(NULL), + IsBone(false) + { + + } + + const NL_NODE_INTERNAL_TYPE *InternalNode; + bool IsBone; +}; + +typedef std::map 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 MeshNames; // Maps meshes to a node name ********************* todo *************** +}; + +#endif /* NL_SCENE_CONTEXT_H */ + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h index 04c5660c0..98d8621f6 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.h +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -15,6 +15,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#ifndef NL_SCENE_META_H +#define NL_SCENE_META_H #include #include @@ -80,4 +82,6 @@ private: }; +#endif NL_SCENE_META_H + /* end of file */ From 9e20f83e481de54af8b9e0da39a584d4f5df4001 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 19:03:05 +0200 Subject: [PATCH 11/21] Shape import --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 197 +++++++++++++++++- code/nel/tools/3d/mesh_utils/assimp_shape.h | 2 +- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 11 +- code/nel/tools/3d/mesh_utils/scene_context.h | 9 + code/nel/tools/3d/mesh_utils/scene_meta.cpp | 4 +- code/nel/tools/3d/mesh_utils/scene_meta.h | 6 +- 6 files changed, 223 insertions(+), 6 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index 5c43c3b38..cef0b5605 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -30,12 +30,207 @@ #include #include +#include + using namespace std; using namespace NLMISC; +using namespace NL3D; -void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext) +// TODO: buildParticleSystem ?? +// TODO: buildWaveMakerShape ?? +// TODO: buildRemanence ?? +// TODO: buildFlare ?? +// Probably specific settings we can only do in meta editor on a dummy node.. + +// 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? + +void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CNodeContext &nodeContext) { + const aiNode *node = nodeContext.InternalNode; + // TODO: Reference CExportNel::buildBaseMeshInterface +} +inline CVector convVector(const aiVector3D &av) +{ + return CVector(-av.x, av.z, av.y); // 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); +} + +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; + + // 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() > 1) + { + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "(%s) mesh->GetNumUVChannels() > 1", 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; + } + } + + // TODO: UV Channels + 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 + 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 vertexIdentifiers; + vector > 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::iterator vecit = vertexIdentifiers.find(vec); + if (vecit == vertexIdentifiers.end()) + { + buildMesh.Vertices[numVertices] = vec; + vertexIdentifiers[vec] = numVertices; + 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; + for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) + { + const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]]; + unsigned int numColorChannels = mesh->GetNumColorChannels(); // TODO: Maybe needs to be same on all mesh parts + 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 + } + CMesh::CFace &face = buildMesh.Faces[numFaces]; + face.MaterialId = mi; + face.SmoothGroup = 0; // No smoothing group (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[0].Normal = convVector(mesh->mNormals[af.mIndices[1]]); + face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[2]]); + // TODO: UV + 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]]); + if (numColorChannels > 1) // TODO: Verify + { + face.Corner[0].Specular = convColor(mesh->mColors[0][af.mIndices[0]]); + face.Corner[1].Specular = convColor(mesh->mColors[0][af.mIndices[1]]); + face.Corner[2].Specular = convColor(mesh->mColors[0][af.mIndices[2]]); + } + } + // TODO: Color modulate, alpha, use color alpha for vp tree, etc + ++numFaces; + } + } + 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, 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 */ diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.h b/code/nel/tools/3d/mesh_utils/assimp_shape.h index 5f1c3c817..b4bdc2490 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.h +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.h @@ -20,6 +20,6 @@ struct CMeshUtilsContext; struct CNodeContext; -void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext); +bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext); /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 46ad1465e..f6313fc98 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -47,7 +47,7 @@ void importShapes(CMeshUtilsContext &context, const aiNode *node) { CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()]; CNodeMeta &nodeMeta = context.SceneMeta.Nodes[node->mName.C_Str()]; - if (nodeMeta.ExportMesh == TMeshShape) + if (nodeMeta.ExportMesh == TMeshShape && nodeMeta.InstanceName.empty()) { if (node->mNumMeshes) { @@ -232,7 +232,11 @@ int exportScene(const CMeshUtilsSettings &settings) CDatabaseConfig::initTextureSearchDirectories(); Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(settings.SourceFilePath, aiProcess_Triangulate | aiProcess_ValidateDataStructure); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights + 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(); @@ -265,6 +269,9 @@ int exportScene(const CMeshUtilsSettings &settings) // ] // -- SKEL FLAG -- + // TODO + // First import materials... + importShapes(context, context.InternalScene->mRootNode); return EXIT_SUCCESS; diff --git a/code/nel/tools/3d/mesh_utils/scene_context.h b/code/nel/tools/3d/mesh_utils/scene_context.h index 0f23f9694..737800d6f 100644 --- a/code/nel/tools/3d/mesh_utils/scene_context.h +++ b/code/nel/tools/3d/mesh_utils/scene_context.h @@ -24,6 +24,8 @@ #include #include +#include +#include #ifndef NL_NODE_INTERNAL_TYPE #define NL_NODE_INTERNAL_TYPE void @@ -32,6 +34,10 @@ #define NL_SCENE_INTERNAL_TYPE void #endif +namespace NL3D { + class IShape; +} + struct CNodeContext { CNodeContext() : @@ -43,6 +49,9 @@ struct CNodeContext const NL_NODE_INTERNAL_TYPE *InternalNode; bool IsBone; + + // NLMISC::CMatrix Transform; // TODO + NLMISC::CRefPtr Shape; }; typedef std::map TNodeContextMap; diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.cpp b/code/nel/tools/3d/mesh_utils/scene_meta.cpp index 4c3440819..a81505492 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.cpp +++ b/code/nel/tools/3d/mesh_utils/scene_meta.cpp @@ -28,7 +28,8 @@ using namespace NLMISC; CNodeMeta::CNodeMeta() : AddToIG(true), ExportMesh(TMeshShape), - ExportBone(TBoneAuto) + ExportBone(TBoneAuto), + AutoAnim(false) { } @@ -42,6 +43,7 @@ void CNodeMeta::serial(NLMISC::IStream &s) s.serial(InstanceShape); s.serial(InstanceName); s.serial(InstanceGroupName); + s.serial(AutoAnim); } CSceneMeta::CSceneMeta() : diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h index 98d8621f6..972132fbc 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.h +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -32,6 +32,8 @@ enum TMesh TMeshCollisionInt = 2, TMeshCollisionExt = 3, TMeshZone = 4, + TMeshPortal = 5, + TMeshCluster = 6, }; enum TBone @@ -47,12 +49,14 @@ struct CNodeMeta bool AddToIG; // Add this node to an instance group TMesh ExportMesh; - TBone ExportBone; + TBone ExportBone; std::string InstanceShape; std::string InstanceName; std::string InstanceGroupName; + bool AutoAnim; + void serial(NLMISC::IStream &s); }; From 3a7b5c6a4699ecbb97ad90d7e242b7bf2c2910e6 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 19:06:07 +0200 Subject: [PATCH 12/21] Note --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index cef0b5605..e1f1cf114 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -41,6 +41,7 @@ using namespace NL3D; // 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 From ac5d9272f1a34fec7842a209b60c82a0540bd6f3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 19:12:33 +0200 Subject: [PATCH 13/21] Adjust --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index e1f1cf114..c369b358b 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -87,10 +87,10 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu "(%s) mesh->GetNumColorChannels() > 2", node->mName.C_Str()); return false; } - if (mesh->GetNumUVChannels() > 1) + if (mesh->GetNumUVChannels() > CVertexBuffer::MaxStage) { tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), - "(%s) mesh->GetNumUVChannels() > 1", node->mName.C_Str()); + "(%s) mesh->GetNumUVChannels() > CVertexBuffer::MaxStage", node->mName.C_Str()); return false; } if (!mesh->HasNormals()) @@ -161,13 +161,14 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu } CMesh::CFace &face = buildMesh.Faces[numFaces]; face.MaterialId = mi; - face.SmoothGroup = 0; // No smoothing group (bitfield) + 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[0].Normal = convVector(mesh->mNormals[af.mIndices[1]]); face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[2]]); + // TODO: If we want normal maps, we need to add tangent vectors to CFace and build process // TODO: UV if (numColorChannels > 0) // TODO: Verify { @@ -176,9 +177,9 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu face.Corner[2].Color = convColor(mesh->mColors[0][af.mIndices[2]]); if (numColorChannels > 1) // TODO: Verify { - face.Corner[0].Specular = convColor(mesh->mColors[0][af.mIndices[0]]); - face.Corner[1].Specular = convColor(mesh->mColors[0][af.mIndices[1]]); - face.Corner[2].Specular = convColor(mesh->mColors[0][af.mIndices[2]]); + 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]]); } } // TODO: Color modulate, alpha, use color alpha for vp tree, etc From c9aef7b5a4dc3cd4784d02272985f705dad83888 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 21:08:30 +0200 Subject: [PATCH 14/21] Import basic material options --HG-- branch : feature-export-assimp --- .../tools/3d/mesh_utils/assimp_material.cpp | 143 ++++++++++++++++++ .../nel/tools/3d/mesh_utils/assimp_material.h | 24 +++ code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 7 +- code/nel/tools/3d/mesh_utils/scene_context.h | 5 +- code/nel/tools/3d/mesh_utils/scene_meta.cpp | 7 +- code/nel/tools/3d/mesh_utils/scene_meta.h | 13 +- 6 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 code/nel/tools/3d/mesh_utils/assimp_material.cpp create mode 100644 code/nel/tools/3d/mesh_utils/assimp_material.h diff --git a/code/nel/tools/3d/mesh_utils/assimp_material.cpp b/code/nel/tools/3d/mesh_utils/assimp_material.cpp new file mode 100644 index 000000000..6a786d63b --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/assimp_material.cpp @@ -0,0 +1,143 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include +#include "assimp_shape.h" + +#include +#include +#include + +#define NL_NODE_INTERNAL_TYPE aiNode +#define NL_SCENE_INTERNAL_TYPE aiScene +#include "scene_context.h" + +#include +#include +#include + +#include + +using namespace std; +using namespace NLMISC; +using namespace NL3D; + +// http://assimp.sourceforge.net/lib_html/materials.html + +inline CRGBA convColor(const aiColor3D &ac) +{ + 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); +} + +CSmartPtr assimpMaterial(CMeshUtilsContext &context, const aiMaterial *am) +{ + CSmartPtr matp = new CMaterial(); + CMaterial &mat = *matp; + 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; + } + + if (am->Get(AI_MATKEY_OPACITY, f) == aiReturn_SUCCESS) + mat.setOpacity(f * 255.99f); + + if (am->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) + mat.setShininess(f); // OR (float)pow(2.0, shininess * 10.0) * 4.f ?? + + if (am->Get(AI_MATKEY_COLOR_DIFFUSE, c3) == aiReturn_SUCCESS) + { + CRGBA diffuse = convColor(c3); + diffuse.A = mat.getOpacity(); + mat.setDiffuse(diffuse); + } + + if (am->Get(AI_MATKEY_COLOR_AMBIENT, c3) == aiReturn_SUCCESS) + mat.setAmbient(convColor(c3)); + + if (am->Get(AI_MATKEY_COLOR_SPECULAR, c3) == aiReturn_SUCCESS) + { + CRGBA specular = convColor(c3); + if (am->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) + { + CRGBAF fColor = specular; + fColor *= f; + uint8 a = specular.A; + specular = fColor; + specular.A = a; + } + mat.setSpecular(specular); + } + + if (am->Get(AI_MATKEY_COLOR_EMISSIVE, c3) == aiReturn_SUCCESS) + mat.setEmissive(convColor(c3)); + + return matp; +} + +void assimpMaterials(CMeshUtilsContext &context) +{ + 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 (context.SceneMeta.Materials.find(amname.C_Str()) + == context.SceneMeta.Materials.end()) + { + context.SceneMeta.Materials[amname.C_Str()] = assimpMaterial(context, am); + } + } +} + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/assimp_material.h b/code/nel/tools/3d/mesh_utils/assimp_material.h new file mode 100644 index 000000000..eac083573 --- /dev/null +++ b/code/nel/tools/3d/mesh_utils/assimp_material.h @@ -0,0 +1,24 @@ +// NeL - MMORPG Framework +// Copyright (C) 2015 Winch Gate Property Limited +// Author: Jan Boon +// +// 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 . + +#include + +struct CMeshUtilsContext; + +void assimpMaterials(CMeshUtilsContext &context); + +/* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index f6313fc98..66454c984 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -32,6 +32,8 @@ #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() @@ -269,9 +271,10 @@ int exportScene(const CMeshUtilsSettings &settings) // ] // -- SKEL FLAG -- - // TODO - // First import materials... + // First import materials + assimpMaterials(context); + // Import shapes importShapes(context, context.InternalScene->mRootNode); return EXIT_SUCCESS; diff --git a/code/nel/tools/3d/mesh_utils/scene_context.h b/code/nel/tools/3d/mesh_utils/scene_context.h index 737800d6f..714e3ff54 100644 --- a/code/nel/tools/3d/mesh_utils/scene_context.h +++ b/code/nel/tools/3d/mesh_utils/scene_context.h @@ -27,6 +27,8 @@ #include #include +#include + #ifndef NL_NODE_INTERNAL_TYPE #define NL_NODE_INTERNAL_TYPE void #endif @@ -36,6 +38,7 @@ namespace NL3D { class IShape; + class CMaterial; } struct CNodeContext @@ -51,7 +54,7 @@ struct CNodeContext bool IsBone; // NLMISC::CMatrix Transform; // TODO - NLMISC::CRefPtr Shape; + NLMISC::CSmartPtr Shape; }; typedef std::map TNodeContextMap; diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.cpp b/code/nel/tools/3d/mesh_utils/scene_meta.cpp index a81505492..3c72207cf 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.cpp +++ b/code/nel/tools/3d/mesh_utils/scene_meta.cpp @@ -22,6 +22,8 @@ #include #include +#include + using namespace std; using namespace NLMISC; @@ -47,7 +49,7 @@ void CNodeMeta::serial(NLMISC::IStream &s) } CSceneMeta::CSceneMeta() : - DefaultInstanceGroup(false), + ExportDefaultIG(false), SkeletonMode(TSkelRoot) { @@ -77,10 +79,11 @@ void CSceneMeta::serial(NLMISC::IStream &s) { uint version = s.serialVersion(1); - s.serial(DefaultInstanceGroup); + s.serial(ExportDefaultIG); s.serial((uint32 &)SkeletonMode); s.serialCont(Nodes); + s.serialPtrCont(Materials); } /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h index 972132fbc..43f309557 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.h +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -20,11 +20,18 @@ #include #include +#include + +#include namespace NLMISC { class IStream; } +namespace NL3D { + class CMaterial; +} + enum TMesh { TMeshDisabled = 0, @@ -56,6 +63,7 @@ struct CNodeMeta std::string InstanceGroupName; bool AutoAnim; + // std::vector 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); }; @@ -67,13 +75,16 @@ enum TSkel TSkelFull = 2, // Include all connected child nodes in the skeleton }; +typedef std::map > TMaterialMap; struct CSceneMeta { CSceneMeta(); - bool DefaultInstanceGroup; // Export a default instance group from nodes the scene that do not have an instance group set + bool ExportDefaultIG; // Export a default instance group from nodes the scene that do not have an instance group set TSkel SkeletonMode; + std::map Nodes; + TMaterialMap Materials; const std::string &metaFilePath() const { return m_MetaFilePath; } From efccbdd3afe55e188e88311aabc66b925d404da7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 21:41:53 +0200 Subject: [PATCH 15/21] Export shape --HG-- branch : feature-export-assimp --- .../tools/3d/mesh_utils/assimp_material.cpp | 20 +++++++++-- .../nel/tools/3d/mesh_utils/assimp_material.h | 6 +++- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 32 +++++++++++++++-- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 34 +++++++++++++++++++ 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_material.cpp b/code/nel/tools/3d/mesh_utils/assimp_material.cpp index 6a786d63b..b542ada14 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_material.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_material.cpp @@ -48,10 +48,8 @@ inline CRGBA convColor(const aiColor4D &ac) return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f, ac.a * 255.99f); } -CSmartPtr assimpMaterial(CMeshUtilsContext &context, const aiMaterial *am) +void assimpMaterial(NL3D::CMaterial &mat, CMeshUtilsContext &context, const aiMaterial *am) { - CSmartPtr matp = new CMaterial(); - CMaterial &mat = *matp; mat.initLighted(); mat.setShader(CMaterial::Normal); @@ -107,12 +105,20 @@ CSmartPtr assimpMaterial(CMeshUtilsContext &context, const aiMaterial if (am->Get(AI_MATKEY_COLOR_EMISSIVE, c3) == aiReturn_SUCCESS) mat.setEmissive(convColor(c3)); +} +CSmartPtr assimpMaterial(CMeshUtilsContext &context, const aiMaterial *am) +{ + CSmartPtr matp = new CMaterial(); + CMaterial &mat = *matp; + assimpMaterial(mat, context, am); return matp; } void assimpMaterials(CMeshUtilsContext &context) { + set materialNames; + const aiScene *scene = context.InternalScene; for (unsigned int mi = 0; mi < scene->mNumMaterials; ++mi) { @@ -131,10 +137,18 @@ void assimpMaterials(CMeshUtilsContext &context) "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); } } diff --git a/code/nel/tools/3d/mesh_utils/assimp_material.h b/code/nel/tools/3d/mesh_utils/assimp_material.h index eac083573..062ca95bb 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_material.h +++ b/code/nel/tools/3d/mesh_utils/assimp_material.h @@ -17,8 +17,12 @@ #include -struct CMeshUtilsContext; +namespace NL3D { + class CMaterial; +} +struct CMeshUtilsContext; +void assimpMaterial(NL3D::CMaterial &mat, CMeshUtilsContext &context, const aiMaterial *am); void assimpMaterials(CMeshUtilsContext &context); /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index c369b358b..a329f30ab 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -32,6 +32,8 @@ #include +#include "assimp_material.h" + using namespace std; using namespace NLMISC; using namespace NL3D; @@ -50,10 +52,31 @@ using namespace NL3D; // TODO: Skinned - reverse transform by skeleton root bone to align? -void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CNodeContext &nodeContext) +void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsContext &context, CNodeContext &nodeContext) { const aiNode *node = nodeContext.InternalNode; - // TODO: Reference CExportNel::buildBaseMeshInterface + // 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()]; + } + } } inline CVector convVector(const aiVector3D &av) @@ -101,6 +124,9 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu } } + // Default vertex flags + buildMesh.VertexFlags = CVertexBuffer::PositionFlag | CVertexBuffer::NormalFlag; + // TODO: UV Channels for (uint i = 0; i < CVertexBuffer::MaxStage; ++i) buildMesh.UVRouting[i] = i; @@ -208,7 +234,7 @@ bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext) // Fill the build interface of CMesh CMeshBase::CMeshBaseBuild buildBaseMesh; - assimpBuildBaseMesh(buildBaseMesh, nodeContext); + assimpBuildBaseMesh(buildBaseMesh, context, nodeContext); CMesh::CMeshBuild buildMesh; if (!assimpBuildMesh(buildMesh, buildBaseMesh, context, nodeContext)) diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 66454c984..3d9b6c42a 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -21,6 +21,9 @@ #include #include #include +#include + +#include #include "database_config.h" #include "scene_meta.h" @@ -217,6 +220,34 @@ void flagExpandedBones(CMeshUtilsContext &context) } } +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 = context.Settings.DestinationDirectoryPath + "/" + 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()); + } + } + } + } +} + // TODO: Separate load scene and save scene functions int exportScene(const CMeshUtilsSettings &settings) { @@ -277,6 +308,9 @@ int exportScene(const CMeshUtilsSettings &settings) // Import shapes importShapes(context, context.InternalScene->mRootNode); + // Export shapes + exportShapes(context); + return EXIT_SUCCESS; } From 5fe1f2c7117e5d3934ef30587ba77f4c535d14c8 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 20 Sep 2015 21:47:26 +0200 Subject: [PATCH 16/21] Fix typo --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index a329f30ab..b6b3163bb 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -192,8 +192,8 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu 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[0].Normal = convVector(mesh->mNormals[af.mIndices[1]]); - face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[2]]); + 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 // TODO: UV if (numColorChannels > 0) // TODO: Verify From f8bb35c449a78fb76d380ae9b46f420a6787197a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 21 Sep 2015 15:53:12 +0200 Subject: [PATCH 17/21] Adjust material import --HG-- branch : feature-export-assimp --- .../tools/3d/mesh_utils/assimp_material.cpp | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_material.cpp b/code/nel/tools/3d/mesh_utils/assimp_material.cpp index b542ada14..b2fd78868 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_material.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_material.cpp @@ -38,7 +38,7 @@ using namespace NL3D; // http://assimp.sourceforge.net/lib_html/materials.html -inline CRGBA convColor(const aiColor3D &ac) +inline CRGBA convColor(const aiColor3D &ac, uint8 a = 255) { return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f); } @@ -73,35 +73,29 @@ void assimpMaterial(NL3D::CMaterial &mat, CMeshUtilsContext &context, const aiMa 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_SHININESS, f) == aiReturn_SUCCESS) - mat.setShininess(f); // OR (float)pow(2.0, shininess * 10.0) * 4.f ?? - - if (am->Get(AI_MATKEY_COLOR_DIFFUSE, c3) == aiReturn_SUCCESS) - { - CRGBA diffuse = convColor(c3); - diffuse.A = mat.getOpacity(); - mat.setDiffuse(diffuse); - } - 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) - { - CRGBA specular = convColor(c3); - if (am->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) - { - CRGBAF fColor = specular; - fColor *= f; - uint8 a = specular.A; - specular = fColor; - specular.A = a; - } - mat.setSpecular(specular); - } + 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)); From 27e7095db934a229f37b9c6da62d09fc57786e19 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 21 Sep 2015 18:11:35 +0200 Subject: [PATCH 18/21] Import material texture --HG-- branch : feature-export-assimp --- .../tools/3d/mesh_utils/assimp_material.cpp | 77 +++++++++++++++++++ code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 35 ++++++++- code/nel/tools/3d/mesh_utils/scene_meta.cpp | 13 +++- code/nel/tools/3d/mesh_utils/scene_meta.h | 6 ++ 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_material.cpp b/code/nel/tools/3d/mesh_utils/assimp_material.cpp index b2fd78868..5dbe8643c 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_material.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_material.cpp @@ -31,6 +31,7 @@ #include #include +#include using namespace std; using namespace NLMISC; @@ -50,6 +51,10 @@ inline CRGBA convColor(const aiColor4D &ac) 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); @@ -99,6 +104,78 @@ void assimpMaterial(NL3D::CMaterial &mat, CMeshUtilsContext &context, const aiMa 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 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 assimpMaterial(CMeshUtilsContext &context, const aiMaterial *am) diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 3d9b6c42a..bfda6e63f 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -22,8 +22,11 @@ #include #include #include +#include #include +#include +#include #include "database_config.h" #include "scene_meta.h" @@ -227,7 +230,7 @@ void exportShapes(CMeshUtilsContext &context) CNodeContext &nodeContext = it->second; if (nodeContext.Shape) { - std::string shapePath = context.Settings.DestinationDirectoryPath + "/" + it->first + ".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)) @@ -244,6 +247,34 @@ void exportShapes(CMeshUtilsContext &context) "Shape '%s' serialization failed!", it->first.c_str()); } } + if (NL3D::CMesh *mesh = dynamic_cast(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(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()); + } + } + } + } + } + } } } } @@ -258,7 +289,7 @@ int exportScene(const CMeshUtilsSettings &settings) context.ToolLogger.initDepend(settings.ToolDependLog); if (!settings.ToolErrorLog.empty()) context.ToolLogger.initError(settings.ToolErrorLog); - context.ToolLogger.writeDepend(NLMISC::BUILD, "*", context.Settings.SourceFilePath.c_str()); // Base input file + context.ToolLogger.writeDepend(NLMISC::BUILD, "*", NLMISC::CPath::standardizePath(context.Settings.SourceFilePath, false).c_str()); // Base input file // Apply database configuration CDatabaseConfig::init(settings.SourceFilePath); diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.cpp b/code/nel/tools/3d/mesh_utils/scene_meta.cpp index 3c72207cf..dd7ea0686 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.cpp +++ b/code/nel/tools/3d/mesh_utils/scene_meta.cpp @@ -49,6 +49,11 @@ void CNodeMeta::serial(NLMISC::IStream &s) } CSceneMeta::CSceneMeta() : + ImportShape(true), + ImportSkel(true), + ImportAnim(true), + ImportCmb(true), + ImportIG(true), ExportDefaultIG(false), SkeletonMode(TSkelRoot) { @@ -57,7 +62,7 @@ CSceneMeta::CSceneMeta() : bool CSceneMeta::load(const std::string &filePath) { - m_MetaFilePath = filePath + ".nelmeta"; + m_MetaFilePath = NLMISC::CPath::standardizePath(filePath + ".nelmeta", false); if (CFile::fileExists(m_MetaFilePath)) { CIFile f(m_MetaFilePath); @@ -79,6 +84,12 @@ 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); diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h index 43f309557..62c185ee4 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.h +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -80,6 +80,12 @@ 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; From 7f31e640da491b7bb4a4b2473a111bc2e4f138f7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 21 Sep 2015 23:47:19 +0200 Subject: [PATCH 19/21] Initial work on UV --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 87 ++++++++++++++++--- code/nel/tools/3d/mesh_utils/mesh_utils.cpp | 2 +- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index b6b3163bb..d7eea11fb 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -89,6 +89,11 @@ 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 ?! +} + bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsContext &context, CNodeContext &nodeContext) { // TODO @@ -98,6 +103,7 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu // 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) @@ -127,12 +133,13 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu // Default vertex flags buildMesh.VertexFlags = CVertexBuffer::PositionFlag | CVertexBuffer::NormalFlag; - // TODO: UV Channels + // 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 = false; sint32 numVertices = 0; for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) numVertices += context.InternalScene->mMeshes[node->mMeshes[mi]]->mNumVertices; @@ -152,7 +159,7 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu if (vecit == vertexIdentifiers.end()) { buildMesh.Vertices[numVertices] = vec; - vertexIdentifiers[vec] = numVertices; + if (cleanupMesh) vertexIdentifiers[vec] = numVertices; // Don't remap if we don't wan't to lose vertex indices vertexRemapping[mi][vi] = numVertices; ++numVertices; } @@ -172,10 +179,45 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu 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]]; - unsigned int numColorChannels = mesh->GetNumColorChannels(); // TODO: Maybe needs to be same on all mesh parts + + // 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::TexCoord0 << 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]; @@ -195,18 +237,43 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu 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 - // TODO: UV + // 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]]); - 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].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; diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index bfda6e63f..2ea3800a7 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -247,7 +247,7 @@ void exportShapes(CMeshUtilsContext &context) "Shape '%s' serialization failed!", it->first.c_str()); } } - if (NL3D::CMesh *mesh = dynamic_cast(nodeContext.Shape.getPtr())) + if (NL3D::CMeshBase *mesh = dynamic_cast(nodeContext.Shape.getPtr())) { for (uint mi = 0; mi < mesh->getNbMaterial(); ++mi) { From 6edff36476c1f9010b369a19f7dc23e8544d7223 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 22 Sep 2015 16:00:07 +0200 Subject: [PATCH 20/21] Apply uv channel and transform --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 73 +++++++++++++++---- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index d7eea11fb..4a473d20f 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -52,6 +52,34 @@ using namespace NL3D; // 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; @@ -77,21 +105,24 @@ void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsCon buildBaseMesh.Materials[mi] = *context.SceneMeta.Materials[amname.C_Str()]; } } -} -inline CVector convVector(const aiVector3D &av) -{ - return CVector(-av.x, av.z, av.y); // COORDINATE CONVERSION -} + // 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); -inline CRGBA convColor(const aiColor4D &ac) -{ - return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f, ac.a * 255.99f); -} + // Meta + // dst.CollisionMeshGeneration = src.CollisionMeshGeneration; -inline CUVW convUvw(const aiVector3D &av) -{ - return CUVW(av.x, av.y, av.z); // UH OH COORDINATE CONVERSION ?! + // TODO: Morph } bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsContext &context, CNodeContext &nodeContext) @@ -139,7 +170,7 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu // 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 = false; + bool cleanupMesh = true; sint32 numVertices = 0; for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) numVertices += context.InternalScene->mMeshes[node->mMeshes[mi]]->mNumVertices; @@ -208,7 +239,7 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu numUVChannels = CVertexBuffer::MaxStage; } for (unsigned int ui = 0; ui < numUVChannels; ++ui) - buildMesh.VertexFlags |= (CVertexBuffer::TexCoord0 << ui); // TODO: Coord UV tex stage rerouting + 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) @@ -227,6 +258,13 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu "(%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) @@ -279,7 +317,12 @@ bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &bu ++numFaces; } } - buildMesh.Faces.resize(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(); From ea7942b09367f663862c3f1d461f449c165d0228 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 22 Sep 2015 16:18:00 +0200 Subject: [PATCH 21/21] Be helpful --HG-- branch : feature-export-assimp --- code/nel/tools/3d/mesh_utils/assimp_shape.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index 4a473d20f..7b404d1e2 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -118,6 +118,11 @@ void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsCon 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;