!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / Sandbox / Plugins / LodGeneratorPlugin / panel / AutoGeneratorTextureCreator.cpp
blob90159f78c9f61435be3ab626b31498b23f9ad0d5
1 #include "StdAfx.h"
2 #include "AutoGeneratorTextureCreator.h"
3 #include "AutoGeneratorHelper.h"
4 #include "AutoGeneratorDataBase.h"
5 #include "Material/Material.h"
6 #include "IEditor.h"
7 #include "Material/MaterialManager.h"
8 #include <CryRenderer/IMeshBaking.h>
9 #include <CryRenderer/ITexture.h>
10 #include "Util/Image.h"
11 #include "Util/ImageTIF.h"
13 #include "Controls/QuestionDialog.h"
14 #include "Util/FileUtil.h"
15 #include "FilePathUtil.h"
17 #define MAX_MAT_SLOTS 127
19 namespace LODGenerator
21 CAutoGeneratorTextureCreator::CAutoGeneratorTextureCreator()
23 m_iWidth = 512;
24 m_iHeight = 512;
27 CAutoGeneratorTextureCreator::~CAutoGeneratorTextureCreator()
32 void CAutoGeneratorTextureCreator::SetWidth(int iWidth)
34 m_iWidth = iWidth;
37 void CAutoGeneratorTextureCreator::SetHeight(int iHeight)
39 m_iHeight = iHeight;
42 bool CAutoGeneratorTextureCreator::Generate(int level)
44 if (!GenerateTexture(level))
45 return false;
46 if (!SaveTextures(level))
47 return false;
48 return true;
51 bool CAutoGeneratorTextureCreator::GenerateTexture(int level)
53 CAutoGeneratorDataBase* pAutoGeneratorDataBase = CAutoGeneratorDataBase::Instance();
54 CAutoGeneratorParams& autoGeneratorParams = CAutoGeneratorDataBase::Instance()->GetParams();
56 SMeshBakingMaterialParams params[MAX_MAT_SLOTS];
57 CMaterial* pMat = pAutoGeneratorDataBase->GetLoadedMaterial();
58 if (!pMat)
59 return false;
61 const int nSubMatId = pAutoGeneratorDataBase->GetSubMatId(level);
62 const int nSourceLod = autoGeneratorParams.GetGeometryOption<int>("nSourceLod");
63 const float fRayLength = autoGeneratorParams.GetMaterialOption<float>("RayTestLength");
64 const float fRayIndent = autoGeneratorParams.GetMaterialOption<float>("RayStartDist");
65 const bool bAlpha = autoGeneratorParams.GetMaterialOption<bool>("BakeAlpha");
66 const bool bSmoothCage = autoGeneratorParams.GetMaterialOption<bool>("SmoothCage");
67 // visn test
68 const bool bDilationPass = false;//autoGeneratorParams.GetMaterialOption<bool>("DilationPass");
69 const int nBackgroundColour = autoGeneratorParams.GetMaterialOption<int>("BackgroundColour");
70 const bool bSaveSpec = autoGeneratorParams.GetMaterialOption<bool>("BakeUniqueSpecularMap");
71 const bool bUseAutoTextureSize = autoGeneratorParams.GetMaterialOption<bool>("UseAutoTextureSize");
73 int nTWidth = m_iWidth;
74 int nTHeight = m_iHeight;
75 if(bUseAutoTextureSize)
77 CryLog("Using AutoTextureSize...");
78 // Fill out the list with the size options
79 std::vector< std::pair<float,int> > textureSizeList;
80 const float autoRadius1 = autoGeneratorParams.GetMaterialOption<float>("AutoTextureRadius1");
81 const int autoSize1 = autoGeneratorParams.GetMaterialOption<int>("AutoTextureSize1");
82 textureSizeList.push_back(std::pair<float,int>(autoRadius1,autoSize1));
84 const float autoRadius2 = autoGeneratorParams.GetMaterialOption<float>("AutoTextureRadius2");
85 const int autoSize2 = autoGeneratorParams.GetMaterialOption<int>("AutoTextureSize2");
86 textureSizeList.push_back(std::pair<float,int>(autoRadius2,autoSize2));
88 const float autoRadius3 = autoGeneratorParams.GetMaterialOption<float>("AutoTextureRadius3");
89 const int autoSize3 = autoGeneratorParams.GetMaterialOption<int>("AutoTextureSize3");
90 textureSizeList.push_back(std::pair<float,int>(autoRadius3,autoSize3));
92 std::sort(textureSizeList.begin(),textureSizeList.end(),std::greater< std::pair<float,int> >());
94 // Find the best setting to use on this model
95 int textureSize = m_iWidth; // Width and height should always be the same! There's only one size option in the UI.
96 IStatObj *mesh = pAutoGeneratorDataBase->GetLoadedModel(level);
97 if(mesh)
99 float objectSize = mesh->GetAABB().GetRadius();
100 for(int i = 0;i<textureSizeList.size();i++)
102 if(objectSize > textureSizeList[i].first)
104 textureSize = textureSizeList[i].second;
105 break;
108 CryLog("AutoTextureSize Object Radius: %f, Texture Size: %d",objectSize,textureSize);
110 nTWidth = textureSize;
111 nTHeight = textureSize;
114 const int submatCount = pMat->GetSubMaterialCount();
115 for ( int idx = 0; idx < submatCount && idx < MAX_MAT_SLOTS; ++idx)
117 params[idx].rayLength=fRayLength;
118 params[idx].rayIndent=fRayIndent;
119 params[idx].bAlphaCutout=bAlpha;
120 params[idx].bIgnore = (idx != nSubMatId);
122 if (!params[idx].bIgnore && !VerifyNoOverlap(nSubMatId))
124 if (QDialogButtonBox::StandardButton::No == CQuestionDialog::SQuestion("UV Layout", "Detected overlapping UVs in target mesh. This could cause artifacts. Continue anyway?"))
125 return false;
129 SMeshBakingInputParams inParams;
130 inParams.pInputMesh = pAutoGeneratorDataBase->GetLoadedModel(nSourceLod);
131 inParams.pInputCharacter=NULL;
132 inParams.pCageMesh = pAutoGeneratorDataBase->GetLoadedModel(level);
133 inParams.pCageCharacter=NULL;
134 inParams.outputTextureWidth=2*nTWidth; // Bake out at twice resolution, we'll half it during conversion so it'll match the selected resolution
135 inParams.outputTextureHeight=2*nTHeight;
136 inParams.pMaterialParams=params;
137 inParams.numMaterialParams=submatCount;
138 inParams.nLodId = level;
139 inParams.bDoDilationPass=bDilationPass;
140 inParams.bSaveSpecular = bSaveSpec;
141 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
142 inParams.defaultBackgroundColour=ColorF(GetRValue(nBackgroundColour)/255.0f, GetGValue(nBackgroundColour)/255.0f, GetBValue(nBackgroundColour)/255.0f, 1.0f);
143 inParams.bSmoothNormals=bSmoothCage;
144 inParams.pMaterial=pMat->GetMatInfo();
146 SMeshBakingOutput pReturnValues;
147 bool ret = gEnv->pRenderer->BakeMesh(&inParams, &pReturnValues);
148 if ( ret )
150 pAutoGeneratorDataBase->GetBakeTextureResult().insert(std::pair<int,SMeshBakingOutput>(level,pReturnValues));
152 return ret;
155 bool CAutoGeneratorTextureCreator::SaveTextures(const int level)
157 CAutoGeneratorDataBase* pAutoGeneratorDataBase = CAutoGeneratorDataBase::Instance();
158 std::map<int,SMeshBakingOutput>& resultsMap = pAutoGeneratorDataBase->GetBakeTextureResult();
159 CAutoGeneratorParams& autoGeneratorParams = CAutoGeneratorDataBase::Instance()->GetParams();
161 if ( resultsMap.find(level) == resultsMap.end())
162 return false;
164 string exportPath = autoGeneratorParams.GetMaterialOption<string>("ExportPath");
165 bool bBakeAlpha = autoGeneratorParams.GetMaterialOption<bool>("BakeAlpha");
166 string filenamePattern = autoGeneratorParams.GetMaterialOption<string>("FilenameTemplate");
167 bool bSaveSpec = autoGeneratorParams.GetMaterialOption<bool>("BakeUniqueSpecularMap");
169 for (int i=CAutoGeneratorDataBase::eTextureType_Diffuse; i<CAutoGeneratorDataBase::eTextureType_Max; i++)
171 if ( i == CAutoGeneratorDataBase::eTextureType_Spec && !bSaveSpec )
172 continue;
174 string cPath = CAutoGeneratorHelper::GetDefaultTextureName(i,level,bBakeAlpha,exportPath,filenamePattern);
175 cPath = PathUtil::Make(PathUtil::GetGameProjectAssetsPath(), cPath);
176 CFileUtil::CreatePath(PathUtil::GetParentDirectory(cPath));
177 bool checkedOut = CAutoGeneratorHelper::CheckoutOrExtract(cPath);
178 string preset = GetPreset(i);
179 bool bSaveAlpha = (i==CAutoGeneratorDataBase::eTextureType_Diffuse && bBakeAlpha) || (i==CAutoGeneratorDataBase::eTextureType_Normal);
180 SaveTexture(resultsMap[level].ppOuputTexture[i], bSaveAlpha, cPath, preset);
181 if (!checkedOut)
182 CAutoGeneratorHelper::CheckoutOrExtract(cPath);
184 AssignToMaterial(level,bBakeAlpha,bSaveSpec,exportPath,filenamePattern);
185 pAutoGeneratorDataBase->ReloadModel();
187 return true;
190 string CAutoGeneratorTextureCreator::GetPreset(const int texturetype)
192 CAutoGeneratorParams& autoGeneratorParams = CAutoGeneratorDataBase::Instance()->GetParams();
194 string preset("");
195 switch(texturetype)
197 case CAutoGeneratorDataBase::eTextureType_Diffuse: preset = autoGeneratorParams.GetMaterialOption<string>("DiffuseTexturePreset");
198 break;
199 case CAutoGeneratorDataBase::eTextureType_Normal: preset = autoGeneratorParams.GetMaterialOption<string>("NormalTexturePreset");
200 break;
201 case CAutoGeneratorDataBase::eTextureType_Spec: preset = autoGeneratorParams.GetMaterialOption<string>("SpecularTexturePreset");
202 break;
205 // NOTE: backwards-compatibility for old lod-settings
206 preset.Replace("Normalmap_lowQ", "NormalsWithSmoothness");
207 preset.Replace("Normalmap_highQ", "NormalsWithSmoothness");
208 preset.Replace("NormalmapWithGlossInAlpha_highQ", "NormalsWithSmoothness");
209 preset.Replace("Diffuse_lowQ", "Albedo");
210 preset.Replace("Diffuse_highQ", "Albedo");
211 preset.Replace("Specular_highQ", "Reflectance");
212 preset.Replace("Specular_lowQ", "Reflectance");
214 return preset;
218 void CAutoGeneratorTextureCreator::AssignToMaterial(const int nLod, const bool bAlpha, const bool bSaveSpec, const string& exportPath, const string& fileNameTemplate)
220 CAutoGeneratorDataBase* pAutoGeneratorDataBase = CAutoGeneratorDataBase::Instance();
221 CAutoGeneratorParams& autoGeneratorParams = CAutoGeneratorDataBase::Instance()->GetParams();
223 CMaterial* pMtl = pAutoGeneratorDataBase->GetLODMaterial();
224 if(pMtl)
226 bool checkedOut = CAutoGeneratorHelper::CheckoutOrExtract(pMtl->GetFilename());
227 if (!checkedOut)
228 return;
230 // Generate base name
231 string relFile;
232 string lodId;
233 lodId.Format("_lod%d.", nLod);
234 relFile=pAutoGeneratorDataBase->GetLoadedModel()->GetFilePath();
235 relFile.Replace(".", lodId);
236 string submatName(PathUtil::GetFileName(relFile));
238 // const int nSubMatId = FindExistingLODMaterial(submatName);
239 const int nSubMatId = pAutoGeneratorDataBase->GetSubMatId(nLod);
240 float half=0.5f;
241 ColorF diffuse(half, half, half, 1.0f);
242 ColorF specular(half, half, half, 1.0f);
243 ColorF emittance(half, half, half, 0.0f);
244 float opacity = 1.0f;
245 float smoothness = 1.0f;
247 if(nSubMatId == -1)
249 CryLog("Error in AssignToMaterial: nSubMatId is -1");
250 return;
253 CMaterial * pTemplateMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial("%EDITOR%/Materials/lodgen_template.mtl");
254 if (pTemplateMaterial)
256 pTemplateMaterial->AddRef();
257 SInputShaderResources &sr=pTemplateMaterial->GetShaderResources();
259 diffuse = sr.m_LMaterial.m_Diffuse;
260 specular = sr.m_LMaterial.m_Specular;
261 emittance = sr.m_LMaterial.m_Emittance;
262 opacity = sr.m_LMaterial.m_Opacity;
263 smoothness = sr.m_LMaterial.m_Smoothness;
265 pTemplateMaterial->Release();
268 int subMaterialCount=pMtl->GetSubMaterialCount();
269 CMaterial *pSubMat=pMtl->GetSubMaterial(nSubMatId);
270 if (!pSubMat)
272 //load material template
273 pSubMat = CAutoGeneratorHelper::CreateMaterialFromTemplate(submatName);
275 if ( nSubMatId >= subMaterialCount )
276 pMtl->SetSubMaterialCount(nSubMatId+1);
277 pMtl->SetSubMaterial(nSubMatId,pSubMat);
280 if (pSubMat)
282 SInputShaderResources &sr=pSubMat->GetShaderResources();
283 sr.m_Textures[EFTT_DIFFUSE].m_Name = CAutoGeneratorHelper::GetDefaultTextureName(0, nLod, bAlpha, exportPath, fileNameTemplate);
284 sr.m_Textures[EFTT_NORMALS].m_Name = CAutoGeneratorHelper::GetDefaultTextureName(1, nLod, bAlpha, exportPath, fileNameTemplate);
285 if ( bSaveSpec)
286 sr.m_Textures[EFTT_SPECULAR].m_Name = CAutoGeneratorHelper::GetDefaultTextureName(2, nLod, bAlpha, exportPath, fileNameTemplate);
287 else
288 sr.m_Textures[EFTT_SPECULAR].m_Name = "";
290 if ( bSaveSpec )
291 specular = ColorF(1.0f,1.0f,1.0f,1.0f);
293 sr.m_LMaterial.m_Diffuse = diffuse;
294 sr.m_LMaterial.m_Specular = specular;
295 sr.m_LMaterial.m_Emittance = emittance;
296 sr.m_LMaterial.m_Opacity = opacity;
297 sr.m_LMaterial.m_Smoothness = smoothness;
299 sr.m_AlphaRef=bAlpha?0.5f:0.0f;
300 sr.m_FurAmount=0;
301 sr.m_VoxelCoverage=255;
302 sr.m_HeatAmount=0;
305 pMtl->Update();
306 if(!pMtl->Save(false))
308 CQuestionDialog::SWarning("Material File Save Failed", "The material file cannot be saved. The file is located in a PAK archive or access is denied");
311 pMtl->Reload();
315 bool CAutoGeneratorTextureCreator::SaveTexture(ITexture* pTex,const bool bAlpha,const string& fileName,const string& texturePreset)
317 if (!pTex)
318 return false;
320 bool bSaved = false;
321 CImageTIF tif;
322 byte *pStorage=new byte[pTex->GetWidth()*pTex->GetHeight()*4];
323 if (pStorage)
325 byte *pData=pTex->GetData32(0,0,pStorage);
326 if (pData)
328 if (!bAlpha)
330 int h=pTex->GetHeight();
331 int w=pTex->GetWidth();
332 byte *pThreeChannelData=new byte[w*h*3];
333 for (int y=0; y<h; y++)
335 for (int x=0; x<w; x++)
337 int pidx=(y*w+x);
338 int pidx4=pidx*4;
339 int pidx3=pidx*3;
340 pThreeChannelData[pidx3+0]=pData[pidx4+0];
341 pThreeChannelData[pidx3+1]=pData[pidx4+1];
342 pThreeChannelData[pidx3+2]=pData[pidx4+2];
345 bSaved=tif.SaveRAW(fileName, pThreeChannelData, pTex->GetWidth(), pTex->GetHeight(), 1, 3, false, texturePreset);
346 delete[] pThreeChannelData;
348 else
350 bSaved=tif.SaveRAW(fileName, pData, pTex->GetWidth(), pTex->GetHeight(), 1, 4, false, texturePreset);
353 delete[] pStorage;
355 return bSaved;
359 bool CAutoGeneratorTextureCreator::VerifyNoOverlap(const int nSubMatIdx)
361 CAutoGeneratorDataBase* pAutoGeneratorDataBase = CAutoGeneratorDataBase::Instance();
363 IStatObj *pCage = pAutoGeneratorDataBase->GetLoadedModel();
364 if (pCage)
366 IRenderMesh *pRM = pCage->GetRenderMesh();
367 if (pRM)
369 InputLayoutHandle fmt = pRM->GetVertexFormat();
370 if (fmt==EDefaultInputLayouts::P3F_C4B_T2F || fmt==EDefaultInputLayouts::P3S_C4B_T2S || fmt== EDefaultInputLayouts::P3S_N4B_C4B_T2S)
372 std::vector<Vec2> uvList;
373 pRM->LockForThreadAccess();
374 const vtx_idx *pIndices = pRM->GetIndexPtr(FSL_READ);
375 int32 uvStride;
377 const byte *pUVs = pRM->GetUVPtr(uvStride, FSL_READ);
378 if (pIndices && pUVs)
380 TRenderChunkArray& chunkList=pRM->GetChunks();
381 for (int j=0; j<chunkList.size(); j++)
383 CRenderChunk &c = chunkList[j];
384 if (c.m_nMatID == nSubMatIdx && c.nNumVerts>0)
386 for (int idx = c.nFirstIndexId; idx<c.nFirstIndexId+c.nNumIndices; idx++)
388 vtx_idx index = pIndices[idx];
389 if (fmt == EDefaultInputLayouts::P3F_C4B_T2F)
391 const Vec2 *uv = (Vec2*)&pUVs[uvStride*index];
392 uvList.push_back(*uv);
394 else
396 const Vec2f16 *uv16 = (Vec2f16*)&pUVs[uvStride*index];
397 uvList.push_back(uv16->ToVec2());
404 pRM->UnLockForThreadAccess();
405 if (uvList.size())
407 int width=1024;
408 byte *buffer = new byte[width*width];
409 memset(buffer, 0, sizeof(byte)*width*width);
410 bool bOverlap=false;
411 for (int i=0; i<uvList.size(); i+=3)
413 if (CAutoGeneratorHelper::RasteriseTriangle(&uvList[i], buffer, width, width))
415 bOverlap=true;
416 break;
419 delete[] buffer;
420 return !bOverlap;
425 return true;