1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2015 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // Author: Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
22 #include <nel/misc/types_nl.h>
23 #include "assimp_shape.h"
25 #include <assimp/postprocess.h>
26 #include <assimp/scene.h>
27 #include <assimp/Importer.hpp>
29 #define NL_NODE_INTERNAL_TYPE aiNode
30 #define NL_SCENE_INTERNAL_TYPE aiScene
31 #include "scene_context.h"
33 #include <nel/misc/debug.h>
34 #include <nel/misc/path.h>
35 #include <nel/pipeline/tool_logger.h>
37 #include <nel/3d/mesh.h>
38 #include <nel/3d/texture_file.h>
41 using namespace NLMISC
;
44 // http://assimp.sourceforge.net/lib_html/materials.html
46 inline CRGBA
convColor(const aiColor3D
&ac
, uint8 a
= 255)
48 return CRGBA(ac
.r
* 255.99f
, ac
.g
* 255.99f
, ac
.b
* 255.99f
);
51 inline CRGBA
convColor(const aiColor4D
&ac
)
53 return CRGBA(ac
.r
* 255.99f
, ac
.g
* 255.99f
, ac
.b
* 255.99f
, ac
.a
* 255.99f
);
56 void assimpMaterial(NL3D::CMaterial
&mat
, CMeshUtilsContext
&context
, const aiMaterial
*am
)
59 if (am
->Get(AI_MATKEY_NAME
, amname
) != aiReturn_SUCCESS
)
63 mat
.setShader(CMaterial::Normal
);
70 if (am
->Get(AI_MATKEY_TWOSIDED
, i
) == aiReturn_SUCCESS
)
71 mat
.setDoubleSided(i
!= 0);
73 if (am
->Get(AI_MATKEY_BLEND_FUNC
, i
) == aiReturn_SUCCESS
) switch ((aiBlendMode
)i
)
75 case aiBlendMode_Default
:
76 mat
.setSrcBlend(CMaterial::srcalpha
);
77 mat
.setDstBlend(CMaterial::invsrcalpha
);
79 case aiBlendMode_Additive
:
80 mat
.setSrcBlend(CMaterial::one
);
81 mat
.setDstBlend(CMaterial::one
);
85 // Colors follow GL convention
86 // "While the ambient, diffuse, specular and emission
87 // "material parameters all have alpha components, only the diffuse"
88 // "alpha component is used in the lighting computation."
89 if (am
->Get(AI_MATKEY_COLOR_DIFFUSE
, c3
) == aiReturn_SUCCESS
)
90 mat
.setDiffuse(convColor(c3
));
92 if (am
->Get(AI_MATKEY_OPACITY
, f
) == aiReturn_SUCCESS
)
93 mat
.setOpacity(f
* 255.99f
);
95 if (am
->Get(AI_MATKEY_COLOR_AMBIENT
, c3
) == aiReturn_SUCCESS
)
96 mat
.setAmbient(convColor(c3
));
98 if (am
->Get(AI_MATKEY_SHININESS
, f
) == aiReturn_SUCCESS
)
99 mat
.setShininess(f
); // (float)pow(2.0, f * 10.0) * 4.f;
101 if (am
->Get(AI_MATKEY_COLOR_SPECULAR
, c3
) == aiReturn_SUCCESS
)
102 mat
.setSpecular(convColor(c3
));
104 if (am
->Get(AI_MATKEY_SHININESS_STRENGTH
, f
) == aiReturn_SUCCESS
)
105 mat
.setSpecular(CRGBAF(mat
.getSpecular()) * f
);
107 mat
.setSpecular(NLMISC::CRGBA::Black
);
109 if (am
->Get(AI_MATKEY_COLOR_EMISSIVE
, c3
) == aiReturn_SUCCESS
)
110 mat
.setEmissive(convColor(c3
));
113 unsigned int texCount
= am
->GetTextureCount(aiTextureType_DIFFUSE
);
114 if (texCount
> IDRV_MAT_MAXTEXTURES
)
116 tlwarning(context
.ToolLogger
, context
.Settings
.SourceFilePath
.c_str(),
117 "Material '%s' has more than %i textures (%i textures found)", amname
.C_Str(), IDRV_MAT_MAXTEXTURES
, texCount
);
118 texCount
= IDRV_MAT_MAXTEXTURES
;
121 for (unsigned int ti
= 0; ti
< texCount
; ++ti
)
124 aiTextureMapping mapping
;
125 unsigned int uvindex
;
126 float blend
; // Partially supported
128 aiTextureMapMode mapmode
;
129 if (am
->GetTexture(aiTextureType_DIFFUSE
, ti
, &path
, &mapping
, &uvindex
, &blend
, &op
, &mapmode
) != aiReturn_SUCCESS
)
131 tlerror(context
.ToolLogger
, context
.Settings
.SourceFilePath
.c_str(),
132 "Failed to get texture %i in material '%s'", ti
, amname
.C_Str());
136 std::string fileName
= CFile::getFilename(CPath::standardizePath(path
.C_Str(), false));
137 std::string knownPath
= CPath::lookup(fileName
, false, false, false);
138 if (knownPath
.empty())
140 tlwarning(context
.ToolLogger
, context
.Settings
.SourceFilePath
.c_str(),
141 "Texture '%s' referenced in material '%s' but not found in the database search paths", fileName
.c_str(), amname
.C_Str());
144 // NeL supports bitmap and cubemap, but we import only basic bitmap here. Cubemap can be inserted from the mesh editor tool
145 // NeL also has fancy multi-bitmap thing to switch between summer and winter and so on. Same story
146 CSmartPtr
<CTextureFile
> tex
= new CTextureFile();
147 tex
->setFileName(fileName
);
148 tex
->setWrapS(mapmode
== aiTextureMapMode_Clamp
? ITexture::Clamp
: ITexture::Repeat
);
149 tex
->setWrapT(mapmode
== aiTextureMapMode_Clamp
? ITexture::Clamp
: ITexture::Repeat
);
150 mat
.setTexture(ti
, tex
);
152 // 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)
154 // TODO aiTextureMapping texcoordgen if useful to import
156 mat
.texEnvArg0Alpha(ti
, CMaterial::Texture
, CMaterial::SrcAlpha
);
157 mat
.texEnvArg0RGB(ti
, CMaterial::Texture
, CMaterial::SrcColor
);
158 mat
.texEnvArg1Alpha(ti
, ti
== 0 ? CMaterial::Diffuse
: CMaterial::Previous
, CMaterial::SrcAlpha
);
159 mat
.texEnvArg1RGB(ti
, ti
== 0 ? CMaterial::Diffuse
: CMaterial::Previous
, CMaterial::SrcColor
);
162 case aiTextureOp_Multiply
:
164 mat
.texEnvOpAlpha(ti
, CMaterial::Modulate
);
165 mat
.texEnvOpRGB(ti
, CMaterial::Modulate
);
167 case aiTextureOp_Add
:
168 mat
.texEnvOpAlpha(ti
, CMaterial::Add
);
169 mat
.texEnvOpRGB(ti
, CMaterial::Add
);
171 case aiTextureOp_Subtract
:
172 mat
.texEnvArg0Alpha(ti
, CMaterial::Texture
, CMaterial::InvSrcAlpha
);
173 mat
.texEnvArg0RGB(ti
, CMaterial::Texture
, CMaterial::InvSrcColor
);
174 mat
.texEnvOpAlpha(ti
, CMaterial::Add
);
175 mat
.texEnvOpRGB(ti
, CMaterial::Add
);
177 case aiTextureOp_SignedAdd
:
178 mat
.texEnvOpAlpha(ti
, CMaterial::AddSigned
);
179 mat
.texEnvOpRGB(ti
, CMaterial::AddSigned
);
185 CSmartPtr
<CMaterial
> assimpMaterial(CMeshUtilsContext
&context
, const aiMaterial
*am
)
187 CSmartPtr
<CMaterial
> matp
= new CMaterial();
188 CMaterial
&mat
= *matp
;
189 assimpMaterial(mat
, context
, am
);
193 void assimpMaterials(CMeshUtilsContext
&context
)
195 set
<CSString
> materialNames
;
197 const aiScene
*scene
= context
.InternalScene
;
198 for (unsigned int mi
= 0; mi
< scene
->mNumMaterials
; ++mi
)
200 const aiMaterial
*am
= scene
->mMaterials
[mi
];
202 for (unsigned int pi
= 0; pi
< am
->mNumProperties
; ++pi
) // DEBUG
204 const aiMaterialProperty
*amp
= am
->mProperties
[pi
];
205 printf("%s\n", amp
->mKey
.C_Str());
209 if (am
->Get(AI_MATKEY_NAME
, amname
) != aiReturn_SUCCESS
)
211 tlerror(context
.ToolLogger
, context
.Settings
.SourceFilePath
.c_str(),
212 "Material has no name");
216 if (materialNames
.find(amname
.C_Str()) != materialNames
.end())
218 tlerror(context
.ToolLogger
, context
.Settings
.SourceFilePath
.c_str(),
219 "Material name '%s' used more than once", amname
.C_Str());
223 if (context
.SceneMeta
.Materials
.find(amname
.C_Str())
224 == context
.SceneMeta
.Materials
.end())
226 materialNames
.insert(amname
.C_Str());
227 context
.SceneMeta
.Materials
[amname
.C_Str()] = assimpMaterial(context
, am
);