!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / Sandbox / Plugins / LodGeneratorPlugin / panel / LODInterface.cpp
blob29d0b438a03a47415d60d4461aaac46d9f12bcb1
1 /*************************************************************************
2 Crytek Source File.
3 Copyright (C), Crytek Studios, 2013.
4 *************************************************************************/
6 #include "StdAfx.h"
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 //////////////////////////////////////////////////////////////////////////
29 // Engine Interface
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] == '/');
37 #else
38 return path && (path[0] == '/');
39 #endif
42 inline bool IsRelative(const char *path)
44 return !IsAbsolute(path);
47 CLodGeneratorInteractionManager* CLodGeneratorInteractionManager::m_pInstance = NULL;
49 CLodGeneratorInteractionManager* CLodGeneratorInteractionManager::Instance()
51 if (m_pInstance)
52 return m_pInstance;
54 m_pInstance = new CLodGeneratorInteractionManager();
55 return m_pInstance;
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);
76 m_fRayLength=0.5f;
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");
87 m_bBakeAlpha=false;
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");
92 m_bBakeSpec=false;
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");
98 m_bSmoothCage=false;
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);
194 m_nViewsAround=12;
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.");
199 m_nViewElevations=3;
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");
220 m_bWireframe = true;
221 m_bWireframe->SetDescription("Toggles wireframe preview of the generated lod");
222 m_bWireframe->SetHumanName("Toggle Wireframe");
224 m_pGeometryVarBlock->AddVariable(m_bExportObj, "bExportObj");
225 m_bExportObj = true;
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();
253 if (!pObject)
254 return objectName;
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)))
262 // {
263 // CGeomEntity *pBrushObject = (CGeomEntity*)pObject;
264 // return CString(pBrushObject->GetGeometryFile());
265 // }
267 return objectName;
270 const string CLodGeneratorInteractionManager::GetSelectedBrushMaterial()
272 string materialName("");
273 CBaseObject * pObject = GetIEditor()->GetSelectedObject();
274 if (!pObject)
275 return materialName;
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)))
284 // {
285 // CGeomEntity* pBrushObject = (CGeomEntity*)pObject;
286 // pMaterial = pBrushObject->GetRenderMaterial();
287 // }
289 if (!pMaterial)
290 return materialName;
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();
303 if (pMaterial)
304 return string(pMaterial->GetName());
307 string tempMatFilename;
308 tempMatFilename.Format("%s",PathUtil::ReplaceExtension(filepath,"mtl"));
309 if ( CFileUtil::FileExists(tempMatFilename) )
310 matFilename = tempMatFilename;
312 return matFilename;
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));
336 LoadSettings();
337 UpdateFilename(filepath);
338 CreateChangelist();
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()
368 if(m_pLoadedStatObj)
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)
380 if ( nLod == 0 )
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)
397 m_pLODMaterial=pMat;
400 const string CLodGeneratorInteractionManager::GetLoadedFilename()
402 string path("");
403 if ( m_pLoadedStatObj )
404 path = string(m_pLoadedStatObj->GetFilePath());
405 return path;
408 const string CLodGeneratorInteractionManager::GetLoadedMaterialFilename()
410 string path("");
411 if ( m_pLoadedMaterial )
412 path = string(m_pLoadedMaterial->GetFilename());
413 return path;
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);
424 if ( pStatObjLod )
425 return pStatObjLod->GetAABB().GetRadius();
428 return 0.0f;
431 const IStatObj::SStatistics CLodGeneratorInteractionManager::GetLoadedStatistics()
433 IStatObj::SStatistics stats;
434 if (m_pLoadedStatObj)
435 m_pLoadedStatObj->GetStatistics(stats);
436 return stats;
439 int CLodGeneratorInteractionManager::NumberOfLods()
441 int nLods = 0;
442 for(int i=0; i<MAX_STATOBJ_LODS_NUM; ++i)
444 if ( m_pLoadedStatObj->GetLodObject(i) )
445 nLods += 1;
447 return nLods;
450 int CLodGeneratorInteractionManager::GetHighestLod()
452 int nHighestLod = 0;
453 if ( m_pLoadedStatObj )
454 nHighestLod = m_pLoadedStatObj->FindHighestLOD(0);
455 return nHighestLod;
458 int CLodGeneratorInteractionManager::GetMaxNumLods()
460 return MAX_STATOBJ_LODS_NUM;
463 float CLodGeneratorInteractionManager::GetLodsPercentage(int nLodIdx)
465 if (!m_pLoadedStatObj)
466 return 0.0f;
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;
478 return 0.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;
507 return true;
509 return false;
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)
533 return;
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
541 string extension;
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();
558 if (!pEditorMat)
559 continue;
561 int submatIdx = FindExistingLODMaterial(submatName);
562 if ( submatIdx == -1 )
563 continue;
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*/);
582 if (!checkedOut)
583 CheckoutOrExtract(fullpath);
587 bool CLodGeneratorInteractionManager::PrepareMaterial(const string& materialName, bool bAddToMultiMaterial)
589 CMaterial *pMaterial=NULL;
590 if (bAddToMultiMaterial)
592 pMaterial = GetLoadedMaterial();
594 if (!pMaterial)
595 return false;
597 //convert to multimaterial
598 if ( pMaterial->GetSubMaterialCount() == 0 )
600 pMaterial->SetFlags(MTL_FLAG_MULTI_SUBMTL);
603 else
605 pMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial(materialName, false);
606 if (!pMaterial)
608 pMaterial = GetIEditor()->GetMaterialManager()->CreateMaterial(materialName, XmlNodeRef(), MTL_FLAG_MULTI_SUBMTL);
610 if (!pMaterial)
612 return false;
614 pMaterial->SetSubMaterialCount(0);
616 pMaterial->UpdateFileAttributes();
617 SetLODMaterial(pMaterial);
619 CheckoutOrExtract(pMaterial->GetFilename());
621 return true;
624 int CLodGeneratorInteractionManager::FindExistingLODMaterial(const string& matName)
626 CMaterial* pMat = GetLoadedMaterial();
627 const int submatCount = pMat->GetSubMaterialCount();
628 int matId = -1;
629 //attempt to find existing material
630 int count = 0;
631 while (matId == -1 && count<submatCount)
633 CMaterial * pSubMat = pMat->GetSubMaterial(count);
634 if ( pSubMat )
636 string name(pSubMat->GetName());
637 int ret = name.CompareNoCase(matName);
638 if (ret == 0)
640 matId = count;
641 break;
644 count++;
646 return matId;
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);
656 if (!pNewMaterial)
657 pNewMaterial = GetIEditor()->GetMaterialManager()->DuplicateMaterial(matName,templateMaterial);
659 if (pNewMaterial)
660 pNewMaterial->SetFlags(pNewMaterial->GetFlags()|MTL_FLAG_PURE_CHILD);
662 templateMaterial->Release();
663 return pNewMaterial;
666 int CLodGeneratorInteractionManager::CreateLODMaterial(const string& submatName)
668 CMaterial *pEditorMat = GetLODMaterial();
669 if(!pEditorMat)
670 return -1;
672 int subMatId = FindExistingLODMaterial(submatName);
673 if (subMatId != -1)
674 return subMatId;
676 if (subMatId == -1)
677 subMatId = pEditorMat->GetSubMaterialCount();
679 //load material template
680 CMaterial * templateMat = CreateMaterialFromTemplate(submatName);
681 if (!templateMat)
682 return -1;
684 //set the submaterial correctly for the .mtl file
685 pEditorMat->SetSubMaterialCount(subMatId+1);
686 pEditorMat->SetSubMaterial(subMatId,templateMat);
687 pEditorMat->Save(true);
689 return subMatId;
692 void CLodGeneratorInteractionManager::SetLODMaterialId(IStatObj * pStatObj, int matId)
694 if (!pStatObj)
695 return;
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);
710 if ( !pSubObj )
711 continue;
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));
732 return true;
734 return false;
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;
745 if(bUseCageMesh)
747 bGenerated = GenerateLodFromCageMesh(&pLodStatObj);
748 if(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());
762 if(!bGenerated)
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;
773 if (bGenerated)
775 pLodStatObj->AddRef();
776 string relFile;
777 string lodId;
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*/);
796 if (!checkedOut)
797 CheckoutOrExtract(fullPath);
800 CryLogAlways("Exported lod file: %s",relFile);
801 if (!bExportObj)
802 return;
804 IExportManager * pExporter = GetIEditor()->GetExportManager();
805 if (!pExporter)
806 return;
808 string objPath(fullPath);
809 objPath = PathUtil::ReplaceExtension(objPath,"obj");
810 string filename(PathUtil::GetFile(objPath));
811 string exportpath;
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);
825 if (!pCageStatObj)
826 return 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);
845 else
847 pOutStatObj=gEnv->p3DEngine->CreateStatObjOptionalIndexedMesh(false);
850 pOutStatObj->Invalidate();
851 (*pLodStatObj) = pOutStatObj;
852 return true;
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;
864 return 0.0f;
867 void CLodGeneratorInteractionManager::SaveSettings()
869 if (!m_pLoadedStatObj)
870 return;
872 if (m_pGeometryVarBlock)
873 m_pGeometryVarBlock->Serialize(GetSettings("GeometryLodSettings"), false);
874 if (m_pMaterialVarBlock)
875 m_pMaterialVarBlock->Serialize(GetSettings("MaterialLodSettings"), false);
877 string settingsPath;
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));
883 if (m_xmlSettings)
884 m_xmlSettings->saveToFile(settingsPath);
886 CheckoutInSourceControl(settingsPath);
889 void CLodGeneratorInteractionManager::LoadSettings()
891 string settingsPath;
892 settingsPath.Format("%s/_lodsettings.xml",ExportPath());
893 m_xmlSettings = gEnv->pSystem->LoadXmlFromFile(settingsPath);
894 if (!m_xmlSettings)
895 CreateSettings();
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)
912 if (!m_xmlSettings)
913 CreateSettings();
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);
925 m_nViewsAround=12;
926 m_nViewElevations=3;
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());
938 return string("");
941 void CLodGeneratorInteractionManager::OpenExportPath()
943 CFileUtil::ShowInExplorer(CString(GetLoadedMaterialFilename()));
946 void CLodGeneratorInteractionManager::ShowLodInExplorer(const int nLodId)
948 string filename(GetLoadedFilename());
949 string extension;
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));
958 string ext;
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())
968 return false;
970 ISourceControl * pSControl = GetIEditor()->GetSourceControl();
971 string description("Sandbox LOD Generator: ");
972 char changeId[16];
973 if ( m_pLoadedStatObj )
974 description.Append(PathUtil::GetFileName(m_pLoadedStatObj->GetFilePath()));
976 bool ret = pSControl->DoesChangeListExist(description,changeId,sizeof(changeId));
977 if (!ret)
978 ret = pSControl->CreateChangeList(description,changeId,sizeof(changeId));
980 if (ret)
981 m_changeId = string(changeId);
983 return ret;
986 bool CLodGeneratorInteractionManager::CheckoutInSourceControl(const string& filename)
988 //try from source control
989 bool bCheckedOut = false;
990 ISourceControl * pSControl = GetIEditor()->GetSourceControl();
991 if(pSControl)
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);
1009 else
1011 CFileUtil::OverwriteFile(filename);
1014 return bCheckedOut;
1017 bool CLodGeneratorInteractionManager::CheckoutOrExtract(const char * filename)
1019 if(GetIEditor()->IsSourceControlAvailable())
1021 if(CheckoutInSourceControl(filename))
1022 return true;
1024 uint32 attribs = CFileUtil::GetAttributes(filename);
1025 if (attribs & SCC_FILE_ATTRIBUTE_CHECKEDOUT)
1027 return true;
1029 if (attribs & SCC_FILE_ATTRIBUTE_INPAK)
1031 string path(filename);
1032 CString st(path);
1033 return CFileUtil::ExtractFile(st,false);
1035 return true;
1038 int CLodGeneratorInteractionManager::GetSubMatId(const int nLodId)
1040 int nMatId = -1;
1041 if (!m_pLoadedStatObj)
1042 return nMatId;
1044 IStatObj* pStatObj = NULL;
1045 if (nLodId == 0)
1047 pStatObj = m_pLoadedStatObj;
1049 else
1051 pStatObj = m_pLoadedStatObj->GetLodObject(nLodId);
1054 if (!pStatObj)
1055 return nMatId;
1057 IIndexedMesh* pIdxMesh = pStatObj->GetIndexedMesh(true);
1058 if (!pIdxMesh)
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)
1070 return nMatId;
1072 pIdxMesh = pStatObj->GetSubObject(i)->pStatObj->GetIndexedMesh(true);
1077 if (!pIdxMesh)
1078 return nMatId;
1080 return pIdxMesh->GetSubSet(0).nMatID;
1083 int CLodGeneratorInteractionManager::GetSubMatCount(const int nLodId)
1085 int nMatId = -1;
1086 if (!m_pLoadedStatObj)
1087 return nMatId;
1089 IStatObj* pStatObj = NULL;
1090 if (nLodId == 0)
1092 pStatObj = m_pLoadedStatObj;
1094 else
1096 pStatObj = m_pLoadedStatObj->GetLodObject(nLodId);
1099 if (!pStatObj)
1100 return nMatId;
1102 IIndexedMesh* pIdxMesh = pStatObj->GetIndexedMesh(true);
1103 if (!pIdxMesh)
1104 return nMatId;
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();
1113 if (!pMat)
1114 return false;
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);
1151 if(mesh)
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;
1159 break;
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)
1179 return false;
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);
1202 if ( ret )
1204 m_resultsMap.insert(std::pair<int,SMeshBakingOutput>(nLod,pReturnValues));
1206 return ret;
1209 void CLodGeneratorInteractionManager::SaveTextures(const int nLod)
1211 if (m_resultsMap.find(nLod) == m_resultsMap.end())
1212 return;
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)
1222 continue;
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);
1232 if (!checkedOut)
1233 CheckoutOrExtract(cPath);
1235 AssignToMaterial(nLod,bBakeAlpha,bSaveSpec,exportPath,filenamePattern);
1236 ReloadModel();
1239 string CLodGeneratorInteractionManager::GetPreset(const int texturetype)
1241 string preset("");
1242 switch(texturetype)
1244 case eTextureType_Diffuse: preset = GetMaterialOption<string>("DiffuseTexturePreset");
1245 break;
1246 case eTextureType_Normal: preset = GetMaterialOption<string>("NormalTexturePreset");
1247 break;
1248 case eTextureType_Spec: preset = GetMaterialOption<string>("SpecularTexturePreset");
1249 break;
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");
1261 return preset;
1264 const SMeshBakingOutput* CLodGeneratorInteractionManager::GetResults(const int nLod)
1266 if ( m_resultsMap.find(nLod) == m_resultsMap.end())
1267 return NULL;
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"};
1303 string strLodId;
1304 strLodId.Format("%d",nLod);
1306 string outName;
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);
1312 return outName;
1315 bool CLodGeneratorInteractionManager::SaveTexture(ITexture* pTex,const bool bAlpha,const string& fileName,const string& texturePreset)
1317 if (!pTex)
1318 return false;
1320 bool bSaved = false;
1321 CImageTIF tif;
1322 byte *pStorage=new byte[pTex->GetWidth()*pTex->GetHeight()*4];
1323 if (pStorage)
1325 byte *pData=pTex->GetData32(0,0,pStorage);
1326 if (pData)
1328 if (!bAlpha)
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++)
1337 int pidx=(y*w+x);
1338 int pidx4=pidx*4;
1339 int pidx3=pidx*3;
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;
1348 else
1350 bSaved=tif.SaveRAW(fileName, pData, pTex->GetWidth(), pTex->GetHeight(), 1, 4, false, texturePreset);
1353 delete[] pStorage;
1355 return bSaved;
1358 void CLodGeneratorInteractionManager::AssignToMaterial(const int nLod, const bool bAlpha, const bool bSaveSpec, const string& exportPath, const string& fileNameTemplate)
1360 CMaterial* pMtl = GetLODMaterial();
1361 if(pMtl)
1363 bool checkedOut = CheckoutOrExtract(pMtl->GetFilename());
1364 if (!checkedOut)
1365 return;
1367 // Generate base name
1368 string relFile;
1369 string lodId;
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);
1377 float half=0.5f;
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;
1384 if(nSubMatId == -1)
1386 CryLog("Error in AssignToMaterial: nSubMatId is -1");
1387 return;
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);
1407 if (!pSubMat)
1409 //load material template
1410 pSubMat = CreateMaterialFromTemplate(submatName);
1412 if ( nSubMatId >= subMaterialCount )
1413 pMtl->SetSubMaterialCount(nSubMatId+1);
1414 pMtl->SetSubMaterial(nSubMatId,pSubMat);
1417 if (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);
1423 if ( bSaveSpec)
1424 sr.m_Textures[EFTT_SPECULAR].m_Name = GetDefaultTextureName(2, nLod, bAlpha, exportPath, fileNameTemplate);
1425 else
1426 sr.m_Textures[EFTT_SPECULAR].m_Name = "";
1428 if ( bSaveSpec )
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;
1438 sr.m_FurAmount=0;
1439 sr.m_VoxelCoverage=255;
1440 sr.m_HeatAmount=0;
1443 pMtl->Update();
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).");
1449 pMtl->Reload();
1453 bool CLodGeneratorInteractionManager::VerifyNoOverlap(const int nSubMatIdx)
1455 IStatObj *pCage=GetLoadedModel();
1456 if (pCage)
1458 IRenderMesh *pRM=pCage->GetRenderMesh();
1459 if (pRM)
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);
1467 int32 uvStride;
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);
1486 else
1488 const Vec2f16 *uv16=(Vec2f16*)&pUVs[uvStride*index];
1489 uvList.push_back(uv16->ToVec2());
1496 pRM->UnLockForThreadAccess();
1497 if (uvList.size())
1499 int width=1024;
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))
1507 bOverlap=true;
1508 break;
1511 delete[] buffer;
1512 return !bOverlap;
1517 return true;
1520 bool CLodGeneratorInteractionManager::RasteriseTriangle(Vec2 *tri, byte *buffer, const int width, const int height)
1522 int mx=width,Mx=0,my=width,My=0;
1523 Vec2 edgeNormal[3];
1524 for (int i=0; i<3; i++)
1526 float x=tri[i].x;
1527 float y=tri[i].y;
1528 int mxi=(int)floorf(x*width);
1529 int myi=(int)floorf(y*height);
1530 if (mxi<mx)
1531 mx=mxi;
1532 if (mxi>Mx)
1533 Mx=mxi;
1534 if (myi<my)
1535 my=myi;
1536 if (myi>My)
1537 My=myi;
1538 Vec2 edge=tri[(i+1)%3]-tri[i];
1539 edgeNormal[i]=Vec2(edge.y, -edge.x);
1542 if (Mx>=width)
1543 Mx=width-1;
1544 if (My>=height)
1545 My=height-1;
1546 if (mx<0)
1547 mx=0;
1548 if (my<0)
1549 my=0;
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;
1557 Vec2 point(xf, yf);
1559 float sign=0.0f;
1560 bool bHit=true;
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
1566 bHit=false;
1567 break;
1569 sign=dot;
1572 if (bHit)
1574 if (buffer[y*width+x])
1575 return true;
1576 buffer[y*width+x]=1;
1581 return false;
1584 namespace
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) )
1647 return 1.0f;
1648 return 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()");