1 /*************************************************************************
3 Copyright (C), Crytek Studios, 2013.
4 *************************************************************************/
7 #include "LODInterface.h"
8 #include "Util/PakFile.h"
9 #include "Objects/BrushObject.h"
10 #include "Objects/EntityObject.h"
11 #include "Objects/GeomEntity.h"
12 #include "Material/MaterialManager.h"
13 #include "Export/ExportManager.h"
14 #include "Util/Image.h"
15 #include "Util/ImageTIF.h"
16 #include "Util/BoostPythonHelpers.h"
17 #include "ISourceControl.h"
18 #include "../control/RampControl.h"
19 #include "../control/MeshBakerPopupPreview.h"
20 #include "Util/FileUtil.h"
21 #include "Controls/QuestionDialog.h"
23 namespace LODGenerator
26 #define MAX_MAT_SLOTS 127
28 //////////////////////////////////////////////////////////////////////////
30 //////////////////////////////////////////////////////////////////////////
32 //! Returns true when the given path is relative, otherwise false
33 inline bool IsAbsolute(const char* path
)
35 #if CRY_PLATFORM_WINDOWS
36 return path
&& isalpha(path
[0]) && (path
[1] == ':') && (path
[2] == '\\' || path
[2] == '/');
38 return path
&& (path
[0] == '/');
42 inline bool IsRelative(const char *path
)
44 return !IsAbsolute(path
);
47 CLodGeneratorInteractionManager
* CLodGeneratorInteractionManager::m_pInstance
= NULL
;
49 CLodGeneratorInteractionManager
* CLodGeneratorInteractionManager::Instance()
54 m_pInstance
= new CLodGeneratorInteractionManager();
58 void CLodGeneratorInteractionManager::DestroyInstance()
60 SAFE_DELETE(m_pInstance
);
63 CLodGeneratorInteractionManager::CLodGeneratorInteractionManager()
65 m_pLoadedStatObj
= NULL
;
66 memset(&m_results
, 0, sizeof(m_results
));
67 m_bAwaitingResults
= false;
68 m_parameterFilePath
= "";
69 m_changeId
= string("default");
71 m_pMaterialVarBlock
= new CVarBlock();
72 m_pMaterialVarBlock
->AddVariable(m_fRayLength
,"RayTestLength");
73 m_fRayLength
->SetHumanName("Ray Test Length");
74 m_fRayLength
->SetDescription("The length of the ray used to hit the mesh surface for sampling");
75 m_fRayLength
->SetLimits(0.001f
, 100.0f
, 0.25f
, true, false);
78 m_pMaterialVarBlock
->AddVariable(m_fRayStartDist
,"RayStartDist");
79 m_fRayStartDist
->SetHumanName("Ray Start Distance");
80 m_fRayStartDist
->SetDescription("The distance from the case to start the ray tracing");
81 m_fRayStartDist
->SetLimits(0.001f
, 100.0f
, 0.25f
, true, false);
82 m_fRayStartDist
=0.25f
;
84 m_pMaterialVarBlock
->AddVariable(m_bBakeAlpha
,"BakeAlpha");
85 m_bBakeAlpha
->SetHumanName("Bake the Alpha Channel");
86 m_bBakeAlpha
->SetDescription("Enables baking of alpha into the alpha channel");
89 m_pMaterialVarBlock
->AddVariable(m_bBakeSpec
,"BakeUniqueSpecularMap");
90 m_bBakeSpec
->SetHumanName("Bake A Unique Specular Map");
91 m_bBakeSpec
->SetDescription("toggles the baking of a full unique specular map per material lod");
95 m_pMaterialVarBlock
->AddVariable(m_bSmoothCage
,"SmoothCage");
96 m_bSmoothCage
->SetHumanName("Smooth Cage");
97 m_bSmoothCage
->SetDescription("Uses averge face normal instead of vertex normal for ray casting");
100 m_pMaterialVarBlock
->AddVariable(m_bDilationPass
,"DilationPass");
101 m_bDilationPass
->SetHumanName("Dilation Pass");
102 m_bDilationPass
->SetDescription("Reduces filtering errors by filling in none baked areas");
103 m_bDilationPass
=true;
105 m_pMaterialVarBlock
->AddVariable(m_cBackgroundColour
,"BackgroundColour");
106 m_cBackgroundColour
->SetHumanName("Background Colour");
107 m_cBackgroundColour
->SetDescription("Used to fill any areas of the texture that didn't get filled by the ray casting");
108 m_cBackgroundColour
->SetDataType(IVariable::DT_COLOR
);
109 m_cBackgroundColour
= Vec3(0.0f
, 0.0f
, 0.0f
);
111 m_pMaterialVarBlock
->AddVariable(m_strExportPath
,"ExportPath");
112 m_strExportPath
->SetHumanName("Export Path");
113 m_strExportPath
->SetDescription("Where to export the baked textures");
115 m_pMaterialVarBlock
->AddVariable(m_strFilename
,"FilenameTemplate");
116 m_strFilename
->SetHumanName("Filename Pattern");
117 m_strFilename
->SetDescription("Filename pattern template to use for the newly created texture filenames");
119 m_pMaterialVarBlock
->AddVariable(m_strPresetDiffuse
,"DiffuseTexturePreset");
120 m_strPresetDiffuse
->SetHumanName("Diffuse Preset");
121 m_strPresetDiffuse
->SetDescription("The RC preset for the Albedo Map texture");
123 m_pMaterialVarBlock
->AddVariable(m_strPresetNormal
,"NormalTexturePreset");
124 m_strPresetNormal
->SetHumanName("Normal Preset");
125 m_strPresetNormal
->SetDescription("The RC preset for the Normal Map texture");
127 m_pMaterialVarBlock
->AddVariable(m_strPresetSpecular
,"SpecularTexturePreset");
128 m_strPresetSpecular
->SetHumanName("Specular Preset");
129 m_strPresetSpecular
->SetDescription("The RC preset for the Reflectance Map texture");
131 m_pMaterialVarBlock
->AddVariable(m_useAutoTextureSize
,"UseAutoTextureSize");
132 m_useAutoTextureSize
->SetHumanName("Use Auto Texture Size");
133 m_useAutoTextureSize
->SetDescription("Choose the texture size based on the size of the object");
134 m_useAutoTextureSize
->SetFlags(IVariable::UI_INVISIBLE
);
136 m_pMaterialVarBlock
->AddVariable(m_autoTextureRadius1
,"AutoTextureRadius1");
137 m_autoTextureRadius1
->SetHumanName("Auto Texture Radius 1");
138 m_autoTextureRadius1
->SetDescription("The radius for auto size option 1");
139 m_autoTextureRadius1
->SetFlags(IVariable::UI_INVISIBLE
);
140 m_autoTextureRadius1
=8.0f
;
141 m_pMaterialVarBlock
->AddVariable(m_autoTextureSize1
,"AutoTextureSize1");
142 m_autoTextureSize1
->SetHumanName("Auto Texture Size 1");
143 m_autoTextureSize1
->SetDescription("The texture size for auto size option 1");
144 m_autoTextureSize1
->SetFlags(IVariable::UI_INVISIBLE
);
145 m_autoTextureSize1
=1024;
147 m_pMaterialVarBlock
->AddVariable(m_autoTextureRadius2
,"AutoTextureRadius2");
148 m_autoTextureRadius2
->SetHumanName("Auto Texture Radius 2");
149 m_autoTextureRadius2
->SetDescription("The radius for auto size option 2");
150 m_autoTextureRadius2
->SetFlags(IVariable::UI_INVISIBLE
);
151 m_autoTextureRadius2
=4.0f
;
152 m_pMaterialVarBlock
->AddVariable(m_autoTextureSize2
,"AutoTextureSize2");
153 m_autoTextureSize2
->SetHumanName("Auto Texture Size 2");
154 m_autoTextureSize2
->SetDescription("The texture size for auto size option 2");
155 m_autoTextureSize2
->SetFlags(IVariable::UI_INVISIBLE
);
156 m_autoTextureSize2
=512;
158 m_pMaterialVarBlock
->AddVariable(m_autoTextureRadius3
,"AutoTextureRadius3");
159 m_autoTextureRadius3
->SetHumanName("Auto Texture Radius 3");
160 m_autoTextureRadius3
->SetDescription("The radius for auto size option 3");
161 m_autoTextureRadius3
->SetFlags(IVariable::UI_INVISIBLE
);
162 m_autoTextureRadius3
=0.0f
;
163 m_pMaterialVarBlock
->AddVariable(m_autoTextureSize3
,"AutoTextureSize3");
164 m_autoTextureSize3
->SetHumanName("Auto Texture Size 3");
165 m_autoTextureSize3
->SetDescription("The texture size for auto size option 3");
166 m_autoTextureSize3
->SetFlags(IVariable::UI_INVISIBLE
);
167 m_autoTextureSize3
=128;
169 m_strPresetDiffuse
= "Albedo /reduce=1";
170 m_strPresetNormal
= "NormalsWithSmoothness /reduce=1";
171 m_strPresetSpecular
= "Reflectance /reduce=1";
173 m_pGeometryVarBlock
= new CVarBlock();
175 m_pGeometryVarBlock
->AddVariable(m_nSourceLod
,"nSourceLod");
176 m_nSourceLod
->SetHumanName("Source Lod");
177 m_nSourceLod
->SetDescription("Source Lod to generate the Lod chain from");
179 m_pGeometryVarBlock
->AddVariable(m_bObjectHasBase
, "bObjectHasBase");
180 m_bObjectHasBase
->SetHumanName("Object has base");
181 m_bObjectHasBase
->SetDescription("If object doesn't have a base then we won't have use any views looking up at it when calculating error.");
182 m_bObjectHasBase
=false;
184 m_pGeometryVarBlock
->AddVariable(m_fViewResolution
, "fViewResolution");
185 m_fViewResolution
->SetHumanName("Pixels per metre");
186 m_fViewResolution
->SetDescription("The LOD generator judges if a change is good by considering what the object looks like from many views. This alters the resolution of these views. A low value means quicker results but potentially worst accuracy. This is in pixels per meter.");
187 m_fViewResolution
=25;
188 m_fViewResolution
->SetLimits(0.0001f
, 1024.0f
, 1.0f
, true, false);
190 m_pGeometryVarBlock
->AddVariable(m_nViewsAround
, "nViewsAround");
191 m_nViewsAround
->SetHumanName("Views around");
192 m_nViewsAround
->SetDescription("The LOD generator judges if a change is good by considering what the object looks like from many views. This alters the number of views around the object. Each elevation has a ring of this many views. Less views means quicker results but potentially worst accuracy.");
193 m_nViewsAround
->SetLimits(1, 128);
196 m_pGeometryVarBlock
->AddVariable(m_nViewElevations
, "nViewElevations");
197 m_nViewElevations
->SetHumanName("View elevations");
198 m_nViewElevations
->SetDescription("The LOD generator judges if a change is good by considering what the object looks like from many views. This alters the number of levels at which views are taken. Less views means quicker results but potentially worst accuracy.");
200 m_nViewElevations
->SetLimits(1, 128);
202 m_pGeometryVarBlock
->AddVariable(m_fSilhouetteWeight
, "fSilhouetteWeight");
203 m_fSilhouetteWeight
->SetHumanName("Silhouette weighting");
204 m_fSilhouetteWeight
->SetDescription("Changes the weighting given to silhouette changes. A low value means a silhouette change isn't considered much than other changes in depth. A high value means the silhouette is preserved above all else.");
205 m_fSilhouetteWeight
=5.0f
;
206 m_fSilhouetteWeight
->SetLimits(0.0f
, 1000.0f
, 1.0f
, true, false);
208 m_pGeometryVarBlock
->AddVariable(m_fVertexWelding
, "fVertexWelding");
209 m_fVertexWelding
->SetHumanName("Vertex weld distance");
210 m_fVertexWelding
->SetDescription("Before starting the LOD process vertices within this this distance will be welded together.");
211 m_fVertexWelding
=0.001f
;
212 m_fVertexWelding
->SetLimits(0.0f
, 0.1f
, 0.001f
, true, false);
214 m_pGeometryVarBlock
->AddVariable(m_bCheckTopology
, "bCheckTopology");
215 m_bCheckTopology
->SetHumanName("Check topology is correct");
216 m_bCheckTopology
->SetDescription("If a move makes a bad bit topology (a bow tie or more than two triangles sharing a single end) then that move is rejected. This can cause problems if the topology is bad to begin with (or caused by vertex welding).");
217 m_bCheckTopology
=true;
219 m_pGeometryVarBlock
->AddVariable(m_bWireframe
, "bWireframe");
221 m_bWireframe
->SetDescription("Toggles wireframe preview of the generated lod");
222 m_bWireframe
->SetHumanName("Toggle Wireframe");
224 m_pGeometryVarBlock
->AddVariable(m_bExportObj
, "bExportObj");
226 m_bExportObj
->SetDescription("Toggles saving the generated geometry and uvs to an obj file");
227 m_bExportObj
->SetHumanName("Toggle obj export");
229 m_pGeometryVarBlock
->AddVariable(m_bAddToParentMaterial
, "bAddToParentMaterial");
230 m_bAddToParentMaterial
= true;
231 m_bAddToParentMaterial
->SetDescription("If true, adds the new lod material to the parent else creates a separate lod material");
232 m_bAddToParentMaterial
->SetHumanName("Add to parent material");
234 m_pGeometryVarBlock
->AddVariable(m_bUseCageMesh
, "bUseCageMesh");
235 m_bUseCageMesh
= false;
236 m_bUseCageMesh
->SetDescription("If true, will use a lod cage for generating the lod");
237 m_bUseCageMesh
->SetHumanName("Use lod cage");
239 m_pGeometryVarBlock
->AddVariable(m_bPreviewSourceLod
, "bSourceLod");
240 m_bPreviewSourceLod
->SetHumanName("PreviewSourceLod");
241 m_bPreviewSourceLod
->SetDescription("Toggles on the preview of the Source Lod");
244 CLodGeneratorInteractionManager::~CLodGeneratorInteractionManager()
246 m_pLoadedStatObj
= NULL
;
249 const string
CLodGeneratorInteractionManager::GetSelectedBrushFilepath()
251 string
objectName("");
252 CBaseObject
* pObject
= GetIEditor()->GetSelectedObject();
256 if (pObject
->IsKindOf(RUNTIME_CLASS(CBrushObject
)))
258 CBrushObject
*pBrushObject
= (CBrushObject
*)pObject
;
259 return string(pBrushObject
->GetIStatObj()->GetFilePath());
261 // else if(pObject->IsKindOf(RUNTIME_CLASS(CGeomEntity)))
263 // CGeomEntity *pBrushObject = (CGeomEntity*)pObject;
264 // return CString(pBrushObject->GetGeometryFile());
270 const string
CLodGeneratorInteractionManager::GetSelectedBrushMaterial()
272 string
materialName("");
273 CBaseObject
* pObject
= GetIEditor()->GetSelectedObject();
277 IEditorMaterial
* pMaterial
= NULL
;
278 if (pObject
->IsKindOf(RUNTIME_CLASS(CBrushObject
)))
280 CBrushObject
* pBrushObject
= (CBrushObject
*)pObject
;
281 pMaterial
= pBrushObject
->GetRenderMaterial();
283 // else if (pObject->IsKindOf(RUNTIME_CLASS(CGeomEntity)))
285 // CGeomEntity* pBrushObject = (CGeomEntity*)pObject;
286 // pMaterial = pBrushObject->GetRenderMaterial();
292 string
convertedPath(PathUtil::AbsolutePathToGamePath(pMaterial
->GetFullName()));
293 //PathUtil::Sl(convertedPath);
294 return convertedPath
;
297 const string
CLodGeneratorInteractionManager::GetDefaultBrushMaterial(const string
& filepath
)
299 string
matFilename("");
300 if (m_pLoadedStatObj
)
302 IMaterial
* pMaterial
= m_pLoadedStatObj
->GetMaterial();
304 return string(pMaterial
->GetName());
307 string tempMatFilename
;
308 tempMatFilename
.Format("%s",PathUtil::ReplaceExtension(filepath
,"mtl"));
309 if ( CFileUtil::FileExists(tempMatFilename
) )
310 matFilename
= tempMatFilename
;
315 void CLodGeneratorInteractionManager::OpenMaterialEditor()
317 //bug where it doesnt select it first time so do it twice meh
318 GetIEditor()->GetMaterialManager()->GotoMaterial(m_pLoadedMaterial
);
319 GetIEditor()->GetMaterialManager()->GotoMaterial(m_pLoadedMaterial
);
322 bool CLodGeneratorInteractionManager::LoadStatObj(const string
& filepath
)
324 if (stricmp(PathUtil::GetExt(filepath
),CRY_GEOMETRY_FILE_EXT
) == 0)
326 m_pLoadedStatObj
= GetIEditor()->Get3DEngine()->LoadStatObj(filepath
, NULL
, NULL
, false);
327 if (m_pLoadedStatObj
)
329 const int nLods
= NumberOfLods();
330 m_pGeometryVarBlock
->EnableUpdateCallbacks(false);
331 m_pMaterialVarBlock
->EnableUpdateCallbacks(false);
333 m_nSourceLod
->SetLimits(0,nLods
-1,1,true,true);
334 m_fViewResolution
=128.0f
/(2.0f
*GetRadius(m_nSourceLod
));
337 UpdateFilename(filepath
);
340 m_pGeometryVarBlock
->EnableUpdateCallbacks(true);
341 m_pMaterialVarBlock
->EnableUpdateCallbacks(true);
346 return m_pLoadedStatObj
!= NULL
;
349 bool CLodGeneratorInteractionManager::LoadMaterial(string strMaterial
)
351 m_pLoadedMaterial
= GetIEditor()->GetMaterialManager()->LoadMaterial(strMaterial
);
353 if (NumberOfLods() > 1)
355 m_pLODMaterial
= m_pLoadedMaterial
;
358 return m_pLoadedMaterial
!= NULL
;
361 CMaterial
* CLodGeneratorInteractionManager::LoadSpecificMaterial(string strMaterial
)
363 return GetIEditor()->GetMaterialManager()->LoadMaterial(strMaterial
);
366 void CLodGeneratorInteractionManager::ReloadModel()
369 m_pLoadedStatObj
->Refresh(FRO_GEOMETRY
|FRO_FORCERELOAD
);
372 void CLodGeneratorInteractionManager::ReloadMaterial()
374 if (m_pLoadedMaterial
)
375 m_pLoadedMaterial
->Reload();
378 IStatObj
* CLodGeneratorInteractionManager::GetLoadedModel(int nLod
)
381 return m_pLoadedStatObj
;
382 return m_pLoadedStatObj
->GetLodObject(nLod
);
385 CMaterial
* CLodGeneratorInteractionManager::GetLoadedMaterial()
387 return m_pLoadedMaterial
;
390 CMaterial
* CLodGeneratorInteractionManager::GetLODMaterial()
392 return m_pLODMaterial
;
395 void CLodGeneratorInteractionManager::SetLODMaterial(CMaterial
*pMat
)
400 const string
CLodGeneratorInteractionManager::GetLoadedFilename()
403 if ( m_pLoadedStatObj
)
404 path
= string(m_pLoadedStatObj
->GetFilePath());
408 const string
CLodGeneratorInteractionManager::GetLoadedMaterialFilename()
411 if ( m_pLoadedMaterial
)
412 path
= string(m_pLoadedMaterial
->GetFilename());
416 float CLodGeneratorInteractionManager::GetRadius(int nLod
)
418 if (m_pLoadedStatObj
)
420 const int numLods
= NumberOfLods();
421 if ( nLod
< numLods
)
423 IStatObj
* pStatObjLod
= m_pLoadedStatObj
->GetLodObject(nLod
);
425 return pStatObjLod
->GetAABB().GetRadius();
431 const IStatObj::SStatistics
CLodGeneratorInteractionManager::GetLoadedStatistics()
433 IStatObj::SStatistics stats
;
434 if (m_pLoadedStatObj
)
435 m_pLoadedStatObj
->GetStatistics(stats
);
439 int CLodGeneratorInteractionManager::NumberOfLods()
442 for(int i
=0; i
<MAX_STATOBJ_LODS_NUM
; ++i
)
444 if ( m_pLoadedStatObj
->GetLodObject(i
) )
450 int CLodGeneratorInteractionManager::GetHighestLod()
453 if ( m_pLoadedStatObj
)
454 nHighestLod
= m_pLoadedStatObj
->FindHighestLOD(0);
458 int CLodGeneratorInteractionManager::GetMaxNumLods()
460 return MAX_STATOBJ_LODS_NUM
;
463 float CLodGeneratorInteractionManager::GetLodsPercentage(int nLodIdx
)
465 if (!m_pLoadedStatObj
)
468 const IStatObj::SStatistics sourceStats
= GetLoadedStatistics();
469 float maintris
= (float)sourceStats
.nIndices
/ 3;
470 if ( IStatObj
* pStatObjLod
= m_pLoadedStatObj
->GetLodObject(nLodIdx
) )
472 IStatObj::SStatistics lodstats
;
473 pStatObjLod
->GetStatistics(lodstats
);
474 float lodtris
= (float)lodstats
.nIndices
/ 3;
475 return (lodtris
/maintris
) * 100.0f
;
481 bool CLodGeneratorInteractionManager::LodGenGenerate()
483 int nSourceLod
= GetGeometryOption
<int>("nSourceLod");
484 float fViewResolution
= GetGeometryOption
<float>("fViewResolution");
485 int nViewsAround
= GetGeometryOption
<float>("nViewsAround");
486 int nViewElevations
= GetGeometryOption
<float>("nViewElevations");
487 float fSilhouetteWeight
= GetGeometryOption
<float>("fSilhouetteWeight");
488 float fVertexWelding
= GetGeometryOption
<float>("fVertexWelding");
489 bool bCheckTopology
= GetGeometryOption
<float>("bCheckTopology");
490 bool bObjectHasBase
= GetGeometryOption
<bool>("bObjectHasBase");
492 CLODGeneratorLib::SLODSequenceGenerationInputParams inputParams
;
493 inputParams
.pInputMesh
= nSourceLod
== 0 ? m_pLoadedStatObj
: m_pLoadedStatObj
->GetLodObject(nSourceLod
);
494 inputParams
.metersPerPixel
=1.0f
/fViewResolution
;
495 inputParams
.numViewElevations
=nViewElevations
;
496 inputParams
.numViewsAround
=nViewsAround
;
497 inputParams
.bObjectHasBase
=bObjectHasBase
;
498 inputParams
.silhouetteWeight
=fSilhouetteWeight
;
499 inputParams
.vertexWeldingDistance
=fVertexWelding
;
500 inputParams
.bCheckTopology
=bCheckTopology
;
502 CLODGeneratorLib::FreeLODSequence(&m_results
);
503 m_bAwaitingResults
= false;
504 if (CLODGeneratorLib::GenerateLODSequence(&inputParams
, &m_results
, true))
506 m_bAwaitingResults
= true;
512 bool CLodGeneratorInteractionManager::LodGenGenerateTick(float* fProgress
)
514 if (m_bAwaitingResults
)
516 if (CLODGeneratorLib::GetAsyncLODSequenceResults(&m_results
, fProgress
))
518 m_bAwaitingResults
=false;
521 return !m_bAwaitingResults
;
524 void CLodGeneratorInteractionManager::LogGenCancel()
526 CLODGeneratorLib::CancelLODGeneration(&m_results
);
527 m_bAwaitingResults
= false;
530 void CLodGeneratorInteractionManager::ClearUnusedLods(const int nNewLods
)
532 if (!m_pLoadedStatObj
)
535 string
filepath(m_pLoadedStatObj
->GetFilePath());
537 int nSourceLod
= GetGeometryOption
<int>("nSourceLod");
538 for ( int idx
= nSourceLod
+nNewLods
+1; idx
< MAX_STATOBJ_LODS_NUM
; ++idx
)
540 // remove old lods when deleted
542 string
lodFilepath(filepath
);
543 extension
.Format("_lod%d.cgf",idx
);
544 lodFilepath
.Replace(".cgf",extension
);
546 // check if the file exists on disk if it does remove it.
547 if ( CFileUtil::FileExists(lodFilepath
) )
549 string gamefolder
= PathUtil::GetGameFolder();
550 string fullpath
= gamefolder
+ string("/") + lodFilepath
;
551 CFileUtil::DeleteFile(CString(fullpath
));
554 // remove lod material from material file
555 string
submatName(PathUtil::GetFileName(lodFilepath
));
557 CMaterial
*pEditorMat
= GetLoadedMaterial();
561 int submatIdx
= FindExistingLODMaterial(submatName
);
562 if ( submatIdx
== -1 )
565 pEditorMat
->SetSubMaterial(submatIdx
,NULL
);
566 pEditorMat
->Save(true);
569 // strip embedded lods
571 for (int idx
= 1; idx
< MAX_STATOBJ_LODS_NUM
; ++idx
)
573 m_pLoadedStatObj
->SetLodObject(idx
, 0);
576 string gamefolder
= PathUtil::GetGameFolder();
577 string fullpath
= gamefolder
+ string("/") + filepath
;
578 bool checkedOut
= CheckoutOrExtract(fullpath
);
580 bool bMergeAllNode
= m_pLoadedStatObj
->GetSubObjectCount() > 0 ? false : true;
581 m_pLoadedStatObj
->SaveToCGF(fullpath
,NULL
,false/*,bMergeAllNode*/);
583 CheckoutOrExtract(fullpath
);
587 bool CLodGeneratorInteractionManager::PrepareMaterial(const string
& materialName
, bool bAddToMultiMaterial
)
589 CMaterial
*pMaterial
=NULL
;
590 if (bAddToMultiMaterial
)
592 pMaterial
= GetLoadedMaterial();
597 //convert to multimaterial
598 if ( pMaterial
->GetSubMaterialCount() == 0 )
600 pMaterial
->SetFlags(MTL_FLAG_MULTI_SUBMTL
);
605 pMaterial
= GetIEditor()->GetMaterialManager()->LoadMaterial(materialName
, false);
608 pMaterial
= GetIEditor()->GetMaterialManager()->CreateMaterial(materialName
, XmlNodeRef(), MTL_FLAG_MULTI_SUBMTL
);
614 pMaterial
->SetSubMaterialCount(0);
616 pMaterial
->UpdateFileAttributes();
617 SetLODMaterial(pMaterial
);
619 CheckoutOrExtract(pMaterial
->GetFilename());
624 int CLodGeneratorInteractionManager::FindExistingLODMaterial(const string
& matName
)
626 CMaterial
* pMat
= GetLoadedMaterial();
627 const int submatCount
= pMat
->GetSubMaterialCount();
629 //attempt to find existing material
631 while (matId
== -1 && count
<submatCount
)
633 CMaterial
* pSubMat
= pMat
->GetSubMaterial(count
);
636 string
name(pSubMat
->GetName());
637 int ret
= name
.CompareNoCase(matName
);
649 CMaterial
* CLodGeneratorInteractionManager::CreateMaterialFromTemplate(const string
& matName
)
651 CMaterial
* templateMaterial
= GetIEditor()->GetMaterialManager()->LoadMaterial("%EDITOR%/Materials/lodgen_template.mtl");
652 if (templateMaterial
)
653 templateMaterial
->AddRef();
655 CMaterial
* pNewMaterial
= (CMaterial
*)GetIEditor()->GetMaterialManager()->FindItemByName(matName
);
657 pNewMaterial
= GetIEditor()->GetMaterialManager()->DuplicateMaterial(matName
,templateMaterial
);
660 pNewMaterial
->SetFlags(pNewMaterial
->GetFlags()|MTL_FLAG_PURE_CHILD
);
662 templateMaterial
->Release();
666 int CLodGeneratorInteractionManager::CreateLODMaterial(const string
& submatName
)
668 CMaterial
*pEditorMat
= GetLODMaterial();
672 int subMatId
= FindExistingLODMaterial(submatName
);
677 subMatId
= pEditorMat
->GetSubMaterialCount();
679 //load material template
680 CMaterial
* templateMat
= CreateMaterialFromTemplate(submatName
);
684 //set the submaterial correctly for the .mtl file
685 pEditorMat
->SetSubMaterialCount(subMatId
+1);
686 pEditorMat
->SetSubMaterial(subMatId
,templateMat
);
687 pEditorMat
->Save(true);
692 void CLodGeneratorInteractionManager::SetLODMaterialId(IStatObj
* pStatObj
, int matId
)
697 if (IIndexedMesh
* pIMesh
= pStatObj
->GetIndexedMesh())
699 const int subsetCount
= pIMesh
->GetSubSetCount();
700 for (int subsetIdx
= 0; subsetIdx
< subsetCount
; ++subsetIdx
)
702 pIMesh
->SetSubsetMaterialId(subsetIdx
,matId
);
706 const int subObjCount
= pStatObj
->GetSubObjectCount();
707 for (int subObjIdx
= 0; subObjIdx
< subObjCount
; ++subObjIdx
)
709 IStatObj::SSubObject
* pSubObj
= pStatObj
->GetSubObject(subObjIdx
);
712 SetLODMaterialId(pSubObj
->pStatObj
, matId
);
716 bool CLodGeneratorInteractionManager::GenerateTemporaryLod(const float fPercentage
, CCMeshBakerPopupPreview
* ctrl
, CCRampControl
* ramp
)
718 CLODGeneratorLib::SLODGenerationInputParams lodParams
;
719 CLODGeneratorLib::SLODGenerationOutput lodReturn
;
720 lodParams
.pSequence
=&m_results
;
721 lodParams
.nPercentage
=(int)fPercentage
;
722 lodParams
.bUnwrap
=false;
723 if (ctrl
&& ramp
&& CLODGeneratorLib::GenerateLOD(&lodParams
, &lodReturn
))
725 ctrl
->ReleaseObject();
726 ctrl
->SetObject(lodReturn
.pStatObj
);
728 IStatObj::SStatistics stats
;
729 lodReturn
.pStatObj
->GetStatistics(stats
);
730 ramp
->SetStats(RampControlMeshInfo(stats
.nIndices
,stats
.nVertices
));
737 void CLodGeneratorInteractionManager::GenerateLod(const int nLodId
, const float fPercentage
)
739 const bool bExportObj
= GetGeometryOption
<bool>("bExportObj");
740 const bool bAddToParentMaterial
= GetGeometryOption
<bool>("bAddToParentMaterial");
741 const bool bUseCageMesh
= GetGeometryOption
<bool>("bUseCageMesh");
743 IStatObj
*pLodStatObj
;
744 bool bGenerated
= false;
747 bGenerated
= GenerateLodFromCageMesh(&pLodStatObj
);
750 for(int i
=0; i
< m_pLoadedStatObj
->GetSubObjectCount(); i
++)
752 if(m_pLoadedStatObj
->GetSubObject(i
)->nType
== STATIC_SUB_OBJECT_MESH
&&
753 m_pLoadedStatObj
->GetSubObject(i
)->pStatObj
&&
754 m_pLoadedStatObj
->GetSubObject(i
)->pStatObj
->GetRenderMesh())
756 pLodStatObj
->SetGeoName(m_pLoadedStatObj
->GetSubObject(i
)->pStatObj
->GetGeoName());
764 CLODGeneratorLib::SLODGenerationInputParams lodParams
;
765 CLODGeneratorLib::SLODGenerationOutput lodReturn
;
766 lodParams
.pSequence
=&m_results
;
767 lodParams
.nPercentage
=(int)fPercentage
;
768 lodParams
.bUnwrap
=true;
769 bGenerated
= CLODGeneratorLib::GenerateLOD(&lodParams
, &lodReturn
);
770 pLodStatObj
= lodReturn
.pStatObj
;
775 pLodStatObj
->AddRef();
779 lodId
.Format("_lod%d.", nLodId
);
780 relFile
=m_pLoadedStatObj
->GetFilePath();
781 relFile
.Replace(".", lodId
);
782 string
submatName(PathUtil::GetFileName(relFile
));
783 string
fullPath(relFile
);
784 string
newMaterialName(fullPath
);
785 PathUtil::RemoveExtension(newMaterialName
);
787 if ( PrepareMaterial(newMaterialName
, bAddToParentMaterial
) )
789 int matId
= CreateLODMaterial(submatName
);
790 SetLODMaterialId(pLodStatObj
,matId
);
791 pLodStatObj
->SetMaterial(GetLODMaterial()->GetMatInfo());
792 bool checkedOut
= CheckoutOrExtract(fullPath
);
794 bool bMergeAllNode
= m_pLoadedStatObj
->GetSubObjectCount() > 0 ? false : true;
795 pLodStatObj
->SaveToCGF(fullPath
,NULL
,false/*,bMergeAllNode*/);
797 CheckoutOrExtract(fullPath
);
800 CryLogAlways("Exported lod file: %s",relFile
);
804 IExportManager
* pExporter
= GetIEditor()->GetExportManager();
808 string
objPath(fullPath
);
809 objPath
= PathUtil::ReplaceExtension(objPath
,"obj");
810 string
filename(PathUtil::GetFile(objPath
));
812 exportpath
.Format("%s\\_obj\\%s", PathUtil::GetParentDirectory(string(objPath
)).c_str(),filename
);
813 CFileUtil::CreateDirectory(exportpath
);
814 pExporter
->ExportSingleStatObj(pLodStatObj
,exportpath
/*,false*/);
818 bool CLodGeneratorInteractionManager::GenerateLodFromCageMesh(IStatObj
**pLodStatObj
)
820 string cageMeshFilename
;
821 string cageMeshString
= "_autolodcage.";
822 cageMeshFilename
= m_pLoadedStatObj
->GetFilePath();
823 cageMeshFilename
.Replace(".", cageMeshString
);
824 _smart_ptr
<IStatObj
> pCageStatObj
= GetIEditor()->Get3DEngine()->LoadStatObj(cageMeshFilename
, NULL
, NULL
, false);
827 pCageStatObj
->GetIndexedMesh(true); // This causes the index mesh to be created.
829 bool genIndexedMesh
= false;
830 if ( pCageStatObj
->GetRenderMesh() && !pCageStatObj
->GetIndexedMesh(false))
831 genIndexedMesh
= true;
833 IStatObj
*pOutStatObj
=NULL
;
834 if ( pCageStatObj
->GetIndexedMesh(genIndexedMesh
) )
836 IIndexedMesh
*pIndexedMesh
= pCageStatObj
->GetIndexedMesh();
837 CMesh
*pMesh
= pIndexedMesh
->GetMesh();
839 Vec3
*pPositions
= &(pMesh
->m_pPositions
[0]);
840 vtx_idx
*pIndices
= &(pMesh
->m_pIndices
[0]);
841 int newFaces
= pMesh
->GetIndexCount() / 3;
843 pOutStatObj
= CLODGeneratorLib::GenerateUVs(pPositions
, pIndices
, newFaces
);
847 pOutStatObj
=gEnv
->p3DEngine
->CreateStatObjOptionalIndexedMesh(false);
850 pOutStatObj
->Invalidate();
851 (*pLodStatObj
) = pOutStatObj
;
855 int CLodGeneratorInteractionManager::GetNumMoves()
857 return m_results
.numMoves
;
860 float CLodGeneratorInteractionManager::GetErrorAtMove(const int nIndex
)
862 if ( nIndex
> 0 && nIndex
< m_results
.numMoves
)
863 return m_results
.moveList
[nIndex
].error
;
867 void CLodGeneratorInteractionManager::SaveSettings()
869 if (!m_pLoadedStatObj
)
872 if (m_pGeometryVarBlock
)
873 m_pGeometryVarBlock
->Serialize(GetSettings("GeometryLodSettings"), false);
874 if (m_pMaterialVarBlock
)
875 m_pMaterialVarBlock
->Serialize(GetSettings("MaterialLodSettings"), false);
878 settingsPath
.Format("%s/_lodsettings.xml", ExportPath());
879 if (IsRelative(settingsPath
.c_str()))
880 settingsPath
= PathUtil::Make(PathUtil::GetGameProjectAssetsPath(), settingsPath
);
881 CFileUtil::CreatePath(PathUtil::GetParentDirectory(settingsPath
));
884 m_xmlSettings
->saveToFile(settingsPath
);
886 CheckoutInSourceControl(settingsPath
);
889 void CLodGeneratorInteractionManager::LoadSettings()
892 settingsPath
.Format("%s/_lodsettings.xml",ExportPath());
893 m_xmlSettings
= gEnv
->pSystem
->LoadXmlFromFile(settingsPath
);
897 if (m_pGeometryVarBlock
)
898 m_pGeometryVarBlock
->Serialize(GetSettings("GeometryLodSettings"),true);
899 if (m_pMaterialVarBlock
)
900 m_pMaterialVarBlock
->Serialize(GetSettings("MaterialLodSettings"),true);
903 void CLodGeneratorInteractionManager::CreateSettings()
905 m_xmlSettings
= gEnv
->pSystem
->CreateXmlNode("GlobalLodSettings");
906 m_xmlSettings
->addChild(m_xmlSettings
->createNode("GeometryLodSettings"));
907 m_xmlSettings
->addChild(m_xmlSettings
->createNode("MaterialLodSettings"));
910 XmlNodeRef
CLodGeneratorInteractionManager::GetSettings(const string
& settings
)
914 return m_xmlSettings
->findChild(settings
);
917 void CLodGeneratorInteractionManager::ResetSettings()
919 m_pGeometryVarBlock
->EnableUpdateCallbacks(false);
920 m_pMaterialVarBlock
->EnableUpdateCallbacks(false);
921 m_bObjectHasBase
=false;
922 m_fViewResolution
=25;
923 float fRadius
= CLodGeneratorInteractionManager::Instance()->GetRadius(m_nSourceLod
);
924 m_fViewResolution
=128.0f
/(2.0f
*fRadius
);
927 m_fSilhouetteWeight
=5.0f
;
928 m_fVertexWelding
=0.001f
;
929 m_bCheckTopology
=true;
930 m_pGeometryVarBlock
->EnableUpdateCallbacks(true);
931 m_pMaterialVarBlock
->EnableUpdateCallbacks(true);
934 string
CLodGeneratorInteractionManager::ExportPath()
936 if (m_pLoadedStatObj
)
937 return PathUtil::GetParentDirectory(m_pLoadedStatObj
->GetFilePath());
941 void CLodGeneratorInteractionManager::OpenExportPath()
943 CFileUtil::ShowInExplorer(CString(GetLoadedMaterialFilename()));
946 void CLodGeneratorInteractionManager::ShowLodInExplorer(const int nLodId
)
948 string
filename(GetLoadedFilename());
950 extension
.Format("_lod%d.cgf",nLodId
);
951 filename
.Replace(".cgf",extension
);
952 CFileUtil::ShowInExplorer(CString(filename
));
955 void CLodGeneratorInteractionManager::UpdateFilename(string filename
)
957 string
path(PathUtil::GetParentDirectory(filename
));
959 ext
.Format("%s_lod[LodId]_[AlphaBake][BakedTextureType].tif", PathUtil::GetFileName(filename
));
961 SetMaterialOption("ExportPath", path
);
962 SetMaterialOption("FilenameTemplate", ext
);
965 bool CLodGeneratorInteractionManager::CreateChangelist()
967 if (!GetIEditor()->IsSourceControlAvailable())
970 ISourceControl
* pSControl
= GetIEditor()->GetSourceControl();
971 string
description("Sandbox LOD Generator: ");
973 if ( m_pLoadedStatObj
)
974 description
.Append(PathUtil::GetFileName(m_pLoadedStatObj
->GetFilePath()));
976 bool ret
= pSControl
->DoesChangeListExist(description
,changeId
,sizeof(changeId
));
978 ret
= pSControl
->CreateChangeList(description
,changeId
,sizeof(changeId
));
981 m_changeId
= string(changeId
);
986 bool CLodGeneratorInteractionManager::CheckoutInSourceControl(const string
& filename
)
988 //try from source control
989 bool bCheckedOut
= false;
990 ISourceControl
* pSControl
= GetIEditor()->GetSourceControl();
993 string
path(filename
);
994 int eFileAttribs
= pSControl
->GetFileAttributes(path
);
995 if (!(eFileAttribs
& SCC_FILE_ATTRIBUTE_MANAGED
) && (eFileAttribs
& SCC_FILE_ATTRIBUTE_NORMAL
))
997 char * changeId
= (char*)m_changeId
.GetBuffer();
998 bCheckedOut
= pSControl
->Add(path
, "LOD Tool Generation", ADD_WITHOUT_SUBMIT
|ADD_CHANGELIST
, changeId
);
1000 else if (!(eFileAttribs
& SCC_FILE_ATTRIBUTE_BYANOTHER
) && !(eFileAttribs
& SCC_FILE_ATTRIBUTE_CHECKEDOUT
) && (eFileAttribs
& SCC_FILE_ATTRIBUTE_MANAGED
))
1002 if (pSControl
->GetLatestVersion(path
))
1004 char * changeId
= (char*)m_changeId
.GetBuffer();
1005 bCheckedOut
= pSControl
->CheckOut(path
, ADD_CHANGELIST
, changeId
);
1011 CFileUtil::OverwriteFile(filename
);
1017 bool CLodGeneratorInteractionManager::CheckoutOrExtract(const char * filename
)
1019 if(GetIEditor()->IsSourceControlAvailable())
1021 if(CheckoutInSourceControl(filename
))
1024 uint32 attribs
= CFileUtil::GetAttributes(filename
);
1025 if (attribs
& SCC_FILE_ATTRIBUTE_CHECKEDOUT
)
1029 if (attribs
& SCC_FILE_ATTRIBUTE_INPAK
)
1031 string
path(filename
);
1033 return CFileUtil::ExtractFile(st
,false);
1038 int CLodGeneratorInteractionManager::GetSubMatId(const int nLodId
)
1041 if (!m_pLoadedStatObj
)
1044 IStatObj
* pStatObj
= NULL
;
1047 pStatObj
= m_pLoadedStatObj
;
1051 pStatObj
= m_pLoadedStatObj
->GetLodObject(nLodId
);
1057 IIndexedMesh
* pIdxMesh
= pStatObj
->GetIndexedMesh(true);
1060 // validate that only one subobject has a mesh
1061 IStatObj
*subobj
= NULL
;
1062 for(int i
=0; i
< pStatObj
->GetSubObjectCount(); i
++)
1064 if(pStatObj
->GetSubObject(i
)->nType
== STATIC_SUB_OBJECT_MESH
&&
1065 pStatObj
->GetSubObject(i
)->pStatObj
&&
1066 pStatObj
->GetSubObject(i
)->pStatObj
->GetRenderMesh())
1068 // NOTE: we fail if more than one sub object has a renderable mesh.
1069 if(pIdxMesh
!= NULL
)
1072 pIdxMesh
= pStatObj
->GetSubObject(i
)->pStatObj
->GetIndexedMesh(true);
1080 return pIdxMesh
->GetSubSet(0).nMatID
;
1083 int CLodGeneratorInteractionManager::GetSubMatCount(const int nLodId
)
1086 if (!m_pLoadedStatObj
)
1089 IStatObj
* pStatObj
= NULL
;
1092 pStatObj
= m_pLoadedStatObj
;
1096 pStatObj
= m_pLoadedStatObj
->GetLodObject(nLodId
);
1102 IIndexedMesh
* pIdxMesh
= pStatObj
->GetIndexedMesh(true);
1106 return pIdxMesh
->GetSubSetCount();
1109 bool CLodGeneratorInteractionManager::RunProcess(const int nLod
, int nWidth
, const int nHeight
)
1111 SMeshBakingMaterialParams params
[MAX_MAT_SLOTS
];
1112 CMaterial
* pMat
= GetLoadedMaterial();
1116 const int nSubMatId
= GetSubMatId(nLod
);
1117 const int nSourceLod
= GetGeometryOption
<int>("nSourceLod");
1118 const float fRayLength
= GetMaterialOption
<float>("RayTestLength");
1119 const float fRayIndent
= GetMaterialOption
<float>("RayStartDist");
1120 const bool bAlpha
= GetMaterialOption
<bool>("BakeAlpha");
1121 const bool bSmoothCage
= GetMaterialOption
<bool>("SmoothCage");
1122 const bool bDilationPass
= GetMaterialOption
<bool>("DilationPass");
1123 const Vec3 nBackgroundColour
= GetMaterialOption
<Vec3
>("BackgroundColour");
1124 const bool bSaveSpec
= GetMaterialOption
<bool>("BakeUniqueSpecularMap");
1125 const bool bUseAutoTextureSize
= GetMaterialOption
<bool>("UseAutoTextureSize");
1127 int nTWidth
= nWidth
;
1128 int nTHeight
= nHeight
;
1129 if(bUseAutoTextureSize
)
1131 CryLog("Using AutoTextureSize...");
1132 // Fill out the list with the size options
1133 std::vector
< std::pair
<float,int> > textureSizeList
;
1134 const float autoRadius1
= GetMaterialOption
<float>("AutoTextureRadius1");
1135 const int autoSize1
= GetMaterialOption
<int>("AutoTextureSize1");
1136 textureSizeList
.push_back(std::pair
<float,int>(autoRadius1
,autoSize1
));
1138 const float autoRadius2
= GetMaterialOption
<float>("AutoTextureRadius2");
1139 const int autoSize2
= GetMaterialOption
<int>("AutoTextureSize2");
1140 textureSizeList
.push_back(std::pair
<float,int>(autoRadius2
,autoSize2
));
1142 const float autoRadius3
= GetMaterialOption
<float>("AutoTextureRadius3");
1143 const int autoSize3
= GetMaterialOption
<int>("AutoTextureSize3");
1144 textureSizeList
.push_back(std::pair
<float,int>(autoRadius3
,autoSize3
));
1146 std::sort(textureSizeList
.begin(),textureSizeList
.end(),std::greater
< std::pair
<float,int> >());
1148 // Find the best setting to use on this model
1149 int textureSize
= nWidth
; // Width and height should always be the same! There's only one size option in the UI.
1150 IStatObj
*mesh
= GetLoadedModel(nLod
);
1153 float objectSize
= mesh
->GetAABB().GetRadius();
1154 for(int i
= 0;i
<textureSizeList
.size();i
++)
1156 if(objectSize
> textureSizeList
[i
].first
)
1158 textureSize
= textureSizeList
[i
].second
;
1162 CryLog("AutoTextureSize Object Radius: %f, Texture Size: %d",objectSize
,textureSize
);
1164 nTWidth
= textureSize
;
1165 nTHeight
= textureSize
;
1168 const int submatCount
= pMat
->GetSubMaterialCount();
1169 for ( int idx
= 0; idx
< submatCount
&& idx
< MAX_MAT_SLOTS
; ++idx
)
1171 params
[idx
].rayLength
=fRayLength
;
1172 params
[idx
].rayIndent
=fRayIndent
;
1173 params
[idx
].bAlphaCutout
=bAlpha
;
1174 params
[idx
].bIgnore
= (idx
!= nSubMatId
);
1176 if (!params
[idx
].bIgnore
&& !VerifyNoOverlap(nSubMatId
))
1178 if (CQuestionDialog::SQuestion("UV Layout", "Detected overlapping UVs in target mesh. This could cause artifacts. Continue anyway?") == QDialogButtonBox::StandardButton::No
)
1183 SMeshBakingInputParams inParams
;
1184 inParams
.pInputMesh
=GetLoadedModel(nSourceLod
);
1185 inParams
.pInputCharacter
=NULL
;
1186 inParams
.pCageMesh
=GetLoadedModel(nLod
);
1187 inParams
.pCageCharacter
=NULL
;
1188 inParams
.outputTextureWidth
=2*nTWidth
; // Bake out at twice resolution, we'll half it during conversion so it'll match the selected resolution
1189 inParams
.outputTextureHeight
=2*nTHeight
;
1190 inParams
.pMaterialParams
=params
;
1191 inParams
.numMaterialParams
=submatCount
;
1192 inParams
.nLodId
= nLod
;
1193 inParams
.bDoDilationPass
=bDilationPass
;
1194 inParams
.bSaveSpecular
= bSaveSpec
;
1195 inParams
.dilateMagicColour
=ColorF(-16.0f
, -16.0f
, -16.0f
, -16.0f
); // Use the fact it's a floating point target to use a colour that'll never occur
1196 inParams
.defaultBackgroundColour
= ColorF(nBackgroundColour
.x
, nBackgroundColour
.y
, nBackgroundColour
.z
, 1.0f
);
1197 inParams
.bSmoothNormals
=bSmoothCage
;
1198 inParams
.pMaterial
=pMat
->GetMatInfo();
1200 SMeshBakingOutput pReturnValues
;
1201 bool ret
= gEnv
->pRenderer
->BakeMesh(&inParams
, &pReturnValues
);
1204 m_resultsMap
.insert(std::pair
<int,SMeshBakingOutput
>(nLod
,pReturnValues
));
1209 void CLodGeneratorInteractionManager::SaveTextures(const int nLod
)
1211 if (m_resultsMap
.find(nLod
) == m_resultsMap
.end())
1214 string exportPath
= GetMaterialOption
<string
>("ExportPath");
1215 bool bBakeAlpha
= GetMaterialOption
<bool>("BakeAlpha");
1216 string filenamePattern
= GetMaterialOption
<string
>("FilenameTemplate");
1217 bool bSaveSpec
= GetMaterialOption
<bool>("BakeUniqueSpecularMap");
1219 for (int i
= eTextureType_Diffuse
; i
< eTextureType_Max
; i
++)
1221 if (i
== eTextureType_Spec
&& !bSaveSpec
)
1224 string cPath
= GetDefaultTextureName(i
,nLod
,bBakeAlpha
,exportPath
,filenamePattern
);
1225 if (IsRelative(cPath
.c_str()))
1226 cPath
= PathUtil::Make(PathUtil::GetGameProjectAssetsPath(), cPath
);
1227 CFileUtil::CreatePath(PathUtil::AddSlash(PathUtil::GetParentDirectory(cPath
)));
1228 const bool checkedOut
= CheckoutOrExtract(cPath
);
1229 const string preset
= GetPreset(i
);
1230 const bool bSaveAlpha
= (i
== eTextureType_Diffuse
&& bBakeAlpha
) || (i
== eTextureType_Normal
);
1231 SaveTexture(m_resultsMap
[nLod
].ppOuputTexture
[i
], bSaveAlpha
, cPath
, preset
);
1233 CheckoutOrExtract(cPath
);
1235 AssignToMaterial(nLod
,bBakeAlpha
,bSaveSpec
,exportPath
,filenamePattern
);
1239 string
CLodGeneratorInteractionManager::GetPreset(const int texturetype
)
1244 case eTextureType_Diffuse
: preset
= GetMaterialOption
<string
>("DiffuseTexturePreset");
1246 case eTextureType_Normal
: preset
= GetMaterialOption
<string
>("NormalTexturePreset");
1248 case eTextureType_Spec
: preset
= GetMaterialOption
<string
>("SpecularTexturePreset");
1252 // NOTE: backwards-compatibility for old lod-settings
1253 preset
.Replace("Normalmap_lowQ", "NormalsWithSmoothness");
1254 preset
.Replace("Normalmap_highQ", "NormalsWithSmoothness");
1255 preset
.Replace("NormalmapWithGlossInAlpha_highQ", "NormalsWithSmoothness");
1256 preset
.Replace("Diffuse_lowQ", "Albedo");
1257 preset
.Replace("Diffuse_highQ", "Albedo");
1258 preset
.Replace("Specular_highQ", "Reflectance");
1259 preset
.Replace("Specular_lowQ", "Reflectance");
1264 const SMeshBakingOutput
* CLodGeneratorInteractionManager::GetResults(const int nLod
)
1266 if ( m_resultsMap
.find(nLod
) == m_resultsMap
.end())
1269 return &m_resultsMap
[nLod
];
1272 void CLodGeneratorInteractionManager::ClearResults()
1274 for (int idx
= MAX_STATOBJ_LODS_NUM
-1; idx
>= 1; --idx
)
1276 std::map
<int,SMeshBakingOutput
>::iterator iter
= m_resultsMap
.find(idx
);
1277 if ( iter
!= m_resultsMap
.end() )
1279 for (int i
=eTextureType_Diffuse
; i
<eTextureType_Max
; i
++)
1281 if ( m_resultsMap
[idx
].ppOuputTexture
[i
] != NULL
)
1283 m_resultsMap
[idx
].ppOuputTexture
[i
]->Release();
1284 m_resultsMap
[idx
].ppOuputTexture
[i
] = NULL
;
1287 if ( m_resultsMap
[idx
].ppIntermediateTexture
[i
] != NULL
)
1289 m_resultsMap
[idx
].ppIntermediateTexture
[i
]->Release();
1290 m_resultsMap
[idx
].ppIntermediateTexture
[i
] = NULL
;
1293 m_resultsMap
.erase(iter
);
1296 m_resultsMap
.clear();
1299 string
CLodGeneratorInteractionManager::GetDefaultTextureName(const int i
, const int nLod
, const bool bAlpha
, const string
& exportPath
, const string
& fileNameTemplate
)
1301 char *defaultSubName
[]={"diff", "ddna", "spec"};
1304 strLodId
.Format("%d",nLod
);
1307 outName
=PathUtil::AddSlash(exportPath
+"textures")+fileNameTemplate
;
1308 outName
.Replace("[BakedTextureType]", defaultSubName
[i
]);
1309 outName
.Replace("[LodId]", strLodId
);
1310 outName
.Replace("[AlphaBake]", !bAlpha
? "" : "alpha_");
1311 outName
=PathUtil::AbsolutePathToGamePath(outName
);
1315 bool CLodGeneratorInteractionManager::SaveTexture(ITexture
* pTex
,const bool bAlpha
,const string
& fileName
,const string
& texturePreset
)
1320 bool bSaved
= false;
1322 byte
*pStorage
=new byte
[pTex
->GetWidth()*pTex
->GetHeight()*4];
1325 byte
*pData
=pTex
->GetData32(0,0,pStorage
);
1330 int h
=pTex
->GetHeight();
1331 int w
=pTex
->GetWidth();
1332 byte
*pThreeChannelData
=new byte
[w
*h
*3];
1333 for (int y
=0; y
<h
; y
++)
1335 for (int x
=0; x
<w
; x
++)
1340 pThreeChannelData
[pidx3
+0]=pData
[pidx4
+0];
1341 pThreeChannelData
[pidx3
+1]=pData
[pidx4
+1];
1342 pThreeChannelData
[pidx3
+2]=pData
[pidx4
+2];
1345 bSaved
=tif
.SaveRAW(fileName
, pThreeChannelData
, pTex
->GetWidth(), pTex
->GetHeight(), 1, 3, false, texturePreset
);
1346 delete[] pThreeChannelData
;
1350 bSaved
=tif
.SaveRAW(fileName
, pData
, pTex
->GetWidth(), pTex
->GetHeight(), 1, 4, false, texturePreset
);
1358 void CLodGeneratorInteractionManager::AssignToMaterial(const int nLod
, const bool bAlpha
, const bool bSaveSpec
, const string
& exportPath
, const string
& fileNameTemplate
)
1360 CMaterial
* pMtl
= GetLODMaterial();
1363 bool checkedOut
= CheckoutOrExtract(pMtl
->GetFilename());
1367 // Generate base name
1370 lodId
.Format("_lod%d.", nLod
);
1371 relFile
=m_pLoadedStatObj
->GetFilePath();
1372 relFile
.Replace(".", lodId
);
1373 string
submatName(PathUtil::GetFileName(relFile
));
1375 // const int nSubMatId = FindExistingLODMaterial(submatName);
1376 const int nSubMatId
= GetSubMatId(nLod
);
1378 ColorF
diffuse(half
, half
, half
, 1.0f
);
1379 ColorF
specular(half
, half
, half
, 1.0f
);
1380 ColorF
emittance(half
, half
, half
, 0.0f
);
1381 float opacity
= 1.0f
;
1382 float smoothness
= 1.0f
;
1386 CryLog("Error in AssignToMaterial: nSubMatId is -1");
1390 CMaterial
* pTemplateMaterial
= GetIEditor()->GetMaterialManager()->LoadMaterial("%EDITOR%/Materials/lodgen_template.mtl");
1391 if (pTemplateMaterial
)
1393 pTemplateMaterial
->AddRef();
1394 SInputShaderResources
&sr
=pTemplateMaterial
->GetShaderResources();
1396 diffuse
= sr
.m_LMaterial
.m_Diffuse
;
1397 specular
= sr
.m_LMaterial
.m_Specular
;
1398 emittance
= sr
.m_LMaterial
.m_Emittance
;
1399 opacity
= sr
.m_LMaterial
.m_Opacity
;
1400 smoothness
= sr
.m_LMaterial
.m_Smoothness
;
1402 pTemplateMaterial
->Release();
1405 int subMaterialCount
=pMtl
->GetSubMaterialCount();
1406 CMaterial
*pSubMat
=pMtl
->GetSubMaterial(nSubMatId
);
1409 //load material template
1410 pSubMat
= CreateMaterialFromTemplate(submatName
);
1412 if ( nSubMatId
>= subMaterialCount
)
1413 pMtl
->SetSubMaterialCount(nSubMatId
+1);
1414 pMtl
->SetSubMaterial(nSubMatId
,pSubMat
);
1419 SInputShaderResources
&sr
=pSubMat
->GetShaderResources();
1420 sr
.m_Textures
[EFTT_DIFFUSE
].m_Name
= GetDefaultTextureName(0, nLod
, bAlpha
, exportPath
, fileNameTemplate
);
1421 sr
.m_Textures
[EFTT_NORMALS
].m_Name
= GetDefaultTextureName(1, nLod
, bAlpha
, exportPath
, fileNameTemplate
);
1424 sr
.m_Textures
[EFTT_SPECULAR
].m_Name
= GetDefaultTextureName(2, nLod
, bAlpha
, exportPath
, fileNameTemplate
);
1426 sr
.m_Textures
[EFTT_SPECULAR
].m_Name
= "";
1429 specular
= ColorF(1.0f
,1.0f
,1.0f
,1.0f
);
1431 sr
.m_LMaterial
.m_Diffuse
= diffuse
;
1432 sr
.m_LMaterial
.m_Specular
= specular
;
1433 sr
.m_LMaterial
.m_Emittance
= emittance
;
1434 sr
.m_LMaterial
.m_Opacity
= opacity
;
1435 sr
.m_LMaterial
.m_Smoothness
= smoothness
;
1437 sr
.m_AlphaRef
=bAlpha
?0.5f
:0.0f
;
1439 sr
.m_VoxelCoverage
=255;
1444 if(!pMtl
->Save(false))
1446 CQuestionDialog::SWarning("Material File Save Failed", "The material file cannot be saved. The file is located in a PAK archive or access is denied (The mtl file is read-only).");
1453 bool CLodGeneratorInteractionManager::VerifyNoOverlap(const int nSubMatIdx
)
1455 IStatObj
*pCage
=GetLoadedModel();
1458 IRenderMesh
*pRM
=pCage
->GetRenderMesh();
1461 InputLayoutHandle fmt
=pRM
->GetVertexFormat();
1462 if (fmt
==EDefaultInputLayouts::P3F_C4B_T2F
|| fmt
==EDefaultInputLayouts::P3S_C4B_T2S
|| fmt
==EDefaultInputLayouts::P3S_N4B_C4B_T2S
)
1464 std::vector
<Vec2
> uvList
;
1465 pRM
->LockForThreadAccess();
1466 const vtx_idx
*pIndices
=pRM
->GetIndexPtr(FSL_READ
);
1469 const byte
*pUVs
=pRM
->GetUVPtr(uvStride
, FSL_READ
);
1470 if (pIndices
&& pUVs
)
1472 TRenderChunkArray
& chunkList
=pRM
->GetChunks();
1473 for (int j
=0; j
<chunkList
.size(); j
++)
1475 CRenderChunk
&c
=chunkList
[j
];
1476 if (c
.m_nMatID
==nSubMatIdx
&& c
.nNumVerts
>0)
1478 for (int idx
=c
.nFirstIndexId
; idx
<c
.nFirstIndexId
+c
.nNumIndices
; idx
++)
1480 vtx_idx index
=pIndices
[idx
];
1481 if (fmt
==EDefaultInputLayouts::P3F_C4B_T2F
)
1483 const Vec2
*uv
=(Vec2
*)&pUVs
[uvStride
*index
];
1484 uvList
.push_back(*uv
);
1488 const Vec2f16
*uv16
=(Vec2f16
*)&pUVs
[uvStride
*index
];
1489 uvList
.push_back(uv16
->ToVec2());
1496 pRM
->UnLockForThreadAccess();
1500 byte
*buffer
=new byte
[width
*width
];
1501 memset(buffer
, 0, sizeof(byte
)*width
*width
);
1502 bool bOverlap
=false;
1503 for (int i
=0; i
<uvList
.size(); i
+=3)
1505 if (RasteriseTriangle(&uvList
[i
], buffer
, width
, width
))
1520 bool CLodGeneratorInteractionManager::RasteriseTriangle(Vec2
*tri
, byte
*buffer
, const int width
, const int height
)
1522 int mx
=width
,Mx
=0,my
=width
,My
=0;
1524 for (int i
=0; i
<3; i
++)
1528 int mxi
=(int)floorf(x
*width
);
1529 int myi
=(int)floorf(y
*height
);
1538 Vec2 edge
=tri
[(i
+1)%3]-tri
[i
];
1539 edgeNormal
[i
]=Vec2(edge
.y
, -edge
.x
);
1551 for (int y
=my
; y
<=My
; y
++)
1553 float yf
=(y
+0.5f
)/(float)height
;
1554 for (int x
=mx
; x
<=Mx
; x
++)
1556 float xf
=(x
+0.5f
)/(float)width
;
1561 for (int z
=0; z
<3; z
++)
1563 float dot
=edgeNormal
[z
].Dot(point
-tri
[z
]);
1564 if (dot
==0.0f
|| sign
*dot
<0.0f
) // To improve. Detect if we're on the left edge when dot==0
1574 if (buffer
[y
*width
+x
])
1576 buffer
[y
*width
+x
]=1;
1586 const string
PyGetSelectedBrushFilepath()
1588 return CLodGeneratorInteractionManager::Instance()->GetSelectedBrushFilepath();
1591 const string
PyGetSelectedBrushMaterial()
1593 return CLodGeneratorInteractionManager::Instance()->GetSelectedBrushMaterial();
1596 const string
PyGetDefaultBrushMaterial(const string
& filepath
)
1598 return CLodGeneratorInteractionManager::Instance()->GetDefaultBrushMaterial(filepath
);
1601 bool PyLoadStatObj(const string
& filepath
)
1603 return CLodGeneratorInteractionManager::Instance()->LoadStatObj(filepath
);
1606 bool PyLoadMaterial(const string
& filepath
)
1608 return CLodGeneratorInteractionManager::Instance()->LoadMaterial(filepath
);
1611 void PySetGeometryOptions(int viewElevations
, int viewsAround
, float viewResolution
)
1613 CLodGeneratorInteractionManager::Instance()->SetGeometryOption
<int>("nViewElevations", viewElevations
);
1614 CLodGeneratorInteractionManager::Instance()->SetGeometryOption
<int>("nViewsAround", viewsAround
);
1615 CLodGeneratorInteractionManager::Instance()->SetGeometryOption
<float>("fViewResolution", viewResolution
);
1618 void PySetMaterialGenOptions(bool bAddToParentMaterial
)
1620 CLodGeneratorInteractionManager::Instance()->SetGeometryOption
<bool>("bAddToParentMaterial", bAddToParentMaterial
);
1623 void PyReloadModel()
1625 CLodGeneratorInteractionManager::Instance()->ReloadModel();
1628 const string
PyGetLoadedFilename()
1630 return CLodGeneratorInteractionManager::Instance()->GetLoadedFilename();
1633 const string
PyGetLoadedMaterialFilename()
1635 return CLodGeneratorInteractionManager::Instance()->GetLoadedMaterialFilename();
1638 bool PyGenerateLodChain()
1640 return CLodGeneratorInteractionManager::Instance()->LodGenGenerate();
1643 float PyLodChainGenerationTick()
1645 float fProgress
= 0.0f
;
1646 if ( CLodGeneratorInteractionManager::Instance()->LodGenGenerateTick(&fProgress
) )
1651 void PyCancelLodChainGeneration()
1653 CLodGeneratorInteractionManager::Instance()->LogGenCancel();
1656 void PyGenerateGeometryLod(const int nLodId
, const float fPercentage
)
1658 CLodGeneratorInteractionManager::Instance()->GenerateLod(nLodId
,fPercentage
);
1661 bool PyGenerateMaterialLod(const int nLod
, int nWidth
, const int nHeight
)
1663 return CLodGeneratorInteractionManager::Instance()->RunProcess(nLod
,nWidth
,nHeight
);
1666 void PySaveTextures(const int nLod
)
1668 CLodGeneratorInteractionManager::Instance()->SaveTextures(nLod
);
1671 bool PyCreateChangelist()
1673 return CLodGeneratorInteractionManager::Instance()->CreateChangelist();
1676 void PySaveSettings()
1678 CLodGeneratorInteractionManager::Instance()->SaveSettings();
1681 void PyOpenGeneratorWithParameter(const char* filepath
)
1683 CLodGeneratorInteractionManager::Instance()->SetParameterFilePath(filepath
);
1684 GetIEditor()->ExecuteCommand("general.open_pane 'LOD Generator'");
1690 REGISTER_PYTHON_COMMAND(PyOpenGeneratorWithParameter, lodtools, loadcgfintool,
1691 "opens up the LOD Generator tool with given path");
1693 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGetSelectedBrushFilepath, lodtools, getselected,
1694 "returns the path to the editor selected brush object",
1695 "lodtools.getselected()");
1697 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGetSelectedBrushMaterial, lodtools, getselectedmaterial,
1698 "returns the path to the selected brush material",
1699 "lodtools.getselectedmaterial()");
1701 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGetDefaultBrushMaterial, lodtools, getdefaultmaterial,
1702 "returns the path to the brush's material",
1703 "lodtools.getselectedmaterial(str cgfpath)");
1705 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyLoadStatObj, lodtools, loadcgf,
1706 "loads a cgf given the filepath",
1707 "lodtools.loadcgf(str cgfpath)");
1709 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyLoadMaterial, lodtools, loadmaterial,
1710 "loads a material given the filepath ",
1711 "lodtools.loadmaterial(str materialpath)");
1713 REGISTER_PYTHON_COMMAND_WITH_EXAMPLE(PyReloadModel, lodtools, reloadmodel,
1714 "reloads the currently loaded cgf into the lod tool, usefull to reload after generating lods and materials",
1715 "lodtools.reload()");
1717 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGetLoadedFilename, lodtools, getloadedfilename,
1718 "gets the file name of the currently loaded model",
1719 "lodtools.getloadedfilename()");
1721 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGetLoadedMaterialFilename, lodtools, getloadedmaterialfilename,
1722 "gets the file name of the currently loaded material",
1723 "lodtools.getloadedmaterialfilename()");
1725 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGenerateLodChain, lodtools, generatelodchain,
1726 "generates a lod chain for the currently loaded model",
1727 "lodtools.generatelodchain()");
1729 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyLodChainGenerationTick, lodtools, generatetick,
1730 "tick function for the background lod chain generation task should be called every second",
1731 "lodtools.generatetick()");
1733 REGISTER_PYTHON_COMMAND_WITH_EXAMPLE(PyCancelLodChainGeneration, lodtools, generatecancel,
1734 "cancels the current lod chain generation task",
1735 "lodtools.generatecancel()");
1737 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGenerateGeometryLod, lodtools, createlod,
1738 "create a new lod mesh given its id and percentage",
1739 "lodtools.createlod(int nLodId, float fPercentage)");
1741 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PySetGeometryOptions, lodtools, set_geometry_options,
1742 "set the parameters for the lod chain generation",
1743 "lodtools.set_geometry_options(int viewElevations, int viewsAround, float viewResolution)");
1745 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PySetMaterialGenOptions, lodtools, set_material_gen_options,
1746 "set the parameters for the generating the lods material",
1747 "lodtools.set_material_gen_options(bool bAddToParentMaterial)");
1749 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PyGenerateMaterialLod, lodtools, generatematerials,
1750 "generates the material textures for the given lod",
1751 "lodtools.generatematerials(int nLod, int nWidth, int nHeight)");
1753 REGISTER_ONLY_PYTHON_COMMAND_WITH_EXAMPLE(PySaveTextures, lodtools, savetextures,
1754 "saves the generated material textures to disk",
1755 "lodtools.savetextures(int nLod)");
1757 REGISTER_PYTHON_COMMAND_WITH_EXAMPLE(PyCreateChangelist, lodtools, createchangelist,
1758 "creates a new changelist to add any lod generated files too",
1759 "lodtools.createchangelist()");
1761 REGISTER_PYTHON_COMMAND_WITH_EXAMPLE(PySaveSettings, lodtools, savesettings,
1762 "saves out the lod settings file for future use",
1763 "lodtools.savesettings()");