// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdafx.h" #include #include "export_nel.h" #include "export_appdata.h" #include "../nel_patch_lib/rpo.h" #include "nel/misc/time_nl.h" #include "nel/misc/file.h" #include "nel/misc/triangle.h" #include "nel/misc/bsphere.h" #include "nel/misc/path.h" #include "nel/3d/quad_tree.h" #include "nel/3d/scene_group.h" #include "nel/3d/skeleton_shape.h" #include "nel/3d/texture_file.h" #include "nel/3d/light.h" #include "nel/3d/bsp_tree.h" #include "nel/3d/quad_grid.h" using namespace std; using namespace NL3D; using namespace NLMISC; #include "calc_lm.h" #include "calc_lm_plane.h" #include "calc_lm_rt.h" // TOOLS // ***** // Substract to all to get more precision CVector vGlobalPos; // *********************************************************************************************** // SLightBuild // *********************************************************************************************** // ----------------------------------------------------------------------------------------------- SLightBuild::SLightBuild() { Type = LightPoint; Position = CVector(0.0, 0.0, 0.0); Direction = CVector(1.0, 0.0, 0.0); rRadiusMin = 1.0f; rRadiusMax = 2.0f; Ambient = CRGBA(0, 0, 0, 0); Diffuse = CRGBA(0, 0, 0, 0); Specular = CRGBA(0, 0, 0, 0); bCastShadow = false; rMult = 1.0f; LightGroup = 0; rDirRadius = 0.0f; } // ----------------------------------------------------------------------------------------------- bool SLightBuild::canConvertFromMaxLight (INode *node, TimeValue tvTime) { // Get a pointer on the object's node Object *obj = node->EvalWorldState(tvTime).obj; // Check if there is an object if (!obj) return false; // Get a GenLight from the node if (!(obj->SuperClassID()==LIGHT_CLASS_ID)) return false; GenLight *maxLight = (GenLight *) obj; bool deleteIt=false; if (obj != maxLight) deleteIt = true; Interval valid=NEVER; LightState ls; if (maxLight->EvalLightState(tvTime, valid, &ls)!=REF_SUCCEED) return false; if( deleteIt ) maxLight->DeleteMe(); return true; } // ----------------------------------------------------------------------------------------------- void SLightBuild::convertFromMaxLight (INode *node,TimeValue tvTime) { // Get a pointer on the object's node Object *obj = node->EvalWorldState(tvTime).obj; // Check if there is an object if (!obj) return; // Get a GenLight from the node if (!(obj->SuperClassID()==LIGHT_CLASS_ID)) return ; GenLight *maxLight = (GenLight *) obj; bool deleteIt = false; if (obj != maxLight) deleteIt = true; Interval valid=NEVER; LightState ls; if (maxLight->EvalLightState(tvTime, valid, &ls)!=REF_SUCCEED) return; this->Name = node->GetName(); // Retrieve the correct light Group Name this->AnimatedLight = CExportNel::getAnimatedLight (node); this->LightGroup = CExportNel::getLightGroup (node); // Eval the light state fot this tvTime // Set the light mode switch (maxLight->Type()) { case OMNI_LIGHT: this->Type = SLightBuild::LightPoint; break; case TSPOT_LIGHT: case FSPOT_LIGHT: this->Type = SLightBuild::LightSpot; break; case DIR_LIGHT: case TDIR_LIGHT: this->Type = SLightBuild::LightDir; break; default: // Not initialized break; } // *** Set the light color // Get the color CRGBA nelColor; Point3 maxColor = maxLight->GetRGBColor(tvTime); // Mul by multiply CRGBAF nelFColor; nelFColor.R = maxColor.x; nelFColor.G = maxColor.y; nelFColor.B = maxColor.z; nelFColor.A = 1.f; // nelFColor *= maxLight->GetIntensity(tvTime); nelColor = nelFColor; // Affect the ambiant color ? this->Ambient = CRGBA (0,0,0); this->Diffuse = CRGBA (0,0,0); this->Specular = CRGBA (0,0,0); if (maxLight->GetAmbientOnly()) { this->bAmbientOnly= true; this->Ambient = nelColor; } else { this->bAmbientOnly= false; // Affect the diffuse color ? if( maxLight->GetAffectDiffuse() ) this->Diffuse = nelColor; // Affect the specular color ? if (maxLight->GetAffectSpecular()) this->Specular = nelColor; } // Set the light position Point3 pos = node->GetNodeTM(tvTime).GetTrans (); CVector position; position.x=pos.x; position.y=pos.y; position.z=pos.z; // Set the position this->Position = position - vGlobalPos; // Set the light direction CVector direction; INode* target = node->GetTarget (); if (target) { // Get the position of the target Point3 posTarget=target->GetNodeTM (tvTime).GetTrans (); CVector positionTarget; positionTarget.x=posTarget.x; positionTarget.y=posTarget.y; positionTarget.z=posTarget.z; // Direction direction=positionTarget-position; direction.normalize (); } else // No target { // Get orientation of the source as direction CMatrix nelMatrix; CExportNel::convertMatrix (nelMatrix, node->GetNodeTM(tvTime)); // Direction is -Z direction=-nelMatrix.getK(); direction.normalize (); } // Set the direction this->Direction = direction; this->rHotspot = (float)(Pi * maxLight->GetHotspot(tvTime) /(2.0*180.0)); this->rFallof = (float)(Pi * maxLight->GetFallsize(tvTime)/(2.0*180.0)); if (maxLight->GetUseAtten()) { this->rRadiusMin = maxLight->GetAtten (tvTime, ATTEN_START); this->rRadiusMax = maxLight->GetAtten (tvTime, ATTEN_END); } else { // Limit this->rRadiusMin = 10.0; this->rRadiusMax = 10.0; } this->bCastShadow = ( maxLight->GetShadow() != 0 ); this->rMult = maxLight->GetIntensity (tvTime); // Construct the bitmap projector if there is a projector if (maxLight->GetProjector() != 0) { Texmap* tm = maxLight->GetProjMap(); CExportNel::convertMatrix (this->mProj, node->GetNodeTM(tvTime)); if (CExportNel::isClassIdCompatible( *tm, Class_ID (BMTEX_CLASS_ID,0))) { BitmapTex* bmt = (BitmapTex*)tm; Bitmap *pProjMap = bmt->GetBitmap(tvTime); // Construct the projector bitmap if some if( pProjMap != NULL ) { ProjBitmap.resize (pProjMap->Width(), pProjMap->Height(), CBitmap::RGBA); // Copy the bitmap CObjectVector &rBitmap = ProjBitmap.getPixels(); BMM_Color_64 OnePixel; for( uint32 k = 0; k < ProjBitmap.getHeight(); ++k ) for( uint32 j = 0; j < ProjBitmap.getWidth(); ++j ) { pProjMap->GetPixels( j, k, 1, &OnePixel ); rBitmap[(j+k*ProjBitmap.getWidth())*4+0] = OnePixel.r>>8; rBitmap[(j+k*ProjBitmap.getWidth())*4+1] = OnePixel.g>>8; rBitmap[(j+k*ProjBitmap.getWidth())*4+2] = OnePixel.b>>8; rBitmap[(j+k*ProjBitmap.getWidth())*4+3] = OnePixel.a>>8; } ProjBitmap.buildMipMaps(); } } } #if (MAX_RELEASE < 4000) // Convert exclusion list NameTab& ntExclu = maxLight->GetExclusionList(); for( sint i = 0; i < ntExclu.Count(); ++i ) { string tmp = *ntExclu.Addr(i); this->setExclusion.insert( tmp ); } #endif // (MAX_RELEASE < 4000) // Get Soft Shadow informations string sTmp = CExportNel::getScriptAppData (node, NEL3D_APPDATA_SOFTSHADOW_RADIUS, toString(NEL3D_APPDATA_SOFTSHADOW_RADIUS_DEFAULT)); this->rSoftShadowRadius = (float)atof(sTmp.c_str()); sTmp = CExportNel::getScriptAppData (node, NEL3D_APPDATA_SOFTSHADOW_CONELENGTH, toString(NEL3D_APPDATA_SOFTSHADOW_CONELENGTH_DEFAULT)); this->rSoftShadowConeLength = (float)atof(sTmp.c_str()); if( deleteIt ) maxLight->DeleteMe(); } // *********************************************************************************************** // SGradient // *********************************************************************************************** struct SGradient { // Vertex gradient double InitPx, InitPy, InitPz; double GraduPx, GradvPx; double GraduPy, GradvPy; double GraduPz, GradvPz; // Normal gradient double InitNx, InitNy, InitNz; double GraduNx, GradvNx; double GraduNy, GradvNy; double GraduNz, GradvNz; // Color gradient double InitR, InitG, InitB; double GraduR, GradvR; double GraduG, GradvG; double GraduB, GradvB; // Initial u,v double InitU, InitV; // ----------------------------------------------------------------------------------------------- void init( CMesh::CFace *pF, vector& vVertices, CVector &n1, CVector &n2, CVector &n3 ) { double u1 = pF->Corner[0].Uvws[1].U, v1 = pF->Corner[0].Uvws[1].V, u2 = pF->Corner[1].Uvws[1].U, v2 = pF->Corner[1].Uvws[1].V, u3 = pF->Corner[2].Uvws[1].U, v3 = pF->Corner[2].Uvws[1].V; CVector p1 = vVertices[pF->Corner[0].Vertex], p2 = vVertices[pF->Corner[1].Vertex], p3 = vVertices[pF->Corner[2].Vertex]; CRGBA c1 = pF->Corner[0].Color, c2 = pF->Corner[1].Color, c3 = pF->Corner[2].Color; double GradDen = ( (u3-u1)*(v2-v1) - (u2-u1)*(v3-v1) ); if ( fabs (GradDen) > 0.000001 ) GradDen = 1.0 / GradDen; this->InitU = u1; this->InitV = v1; this->InitPx = p1.x; this->InitPy = p1.y; this->InitPz = p1.z; this->InitNx = n1.x; this->InitNy = n1.y; this->InitNz = n1.z; this->InitR = c1.R; this->InitG = c1.G; this->InitB = c1.B; // Gradients for the vertex this->GraduPx = ( (p3.x-p1.x)*(v2-v1)-(p2.x-p1.x)*(v3-v1) ) * GradDen; this->GradvPx = ( (p2.x-p1.x)*(u3-u1)-(p3.x-p1.x)*(u2-u1) ) * GradDen; this->GraduPy = ( (p3.y-p1.y)*(v2-v1)-(p2.y-p1.y)*(v3-v1) ) * GradDen; this->GradvPy = ( (p2.y-p1.y)*(u3-u1)-(p3.y-p1.y)*(u2-u1) ) * GradDen; this->GraduPz = ( (p3.z-p1.z)*(v2-v1)-(p2.z-p1.z)*(v3-v1) ) * GradDen; this->GradvPz = ( (p2.z-p1.z)*(u3-u1)-(p3.z-p1.z)*(u2-u1) ) * GradDen; // The same for the normal this->GraduNx = ( (n3.x-n1.x)*(v2-v1)-(n2.x-n1.x)*(v3-v1) ) * GradDen; this->GradvNx = ( (n2.x-n1.x)*(u3-u1)-(n3.x-n1.x)*(u2-u1) ) * GradDen; this->GraduNy = ( (n3.y-n1.y)*(v2-v1)-(n2.y-n1.y)*(v3-v1) ) * GradDen; this->GradvNy = ( (n2.y-n1.y)*(u3-u1)-(n3.y-n1.y)*(u2-u1) ) * GradDen; this->GraduNz = ( (n3.z-n1.z)*(v2-v1)-(n2.z-n1.z)*(v3-v1) ) * GradDen; this->GradvNz = ( (n2.z-n1.z)*(u3-u1)-(n3.z-n1.z)*(u2-u1) ) * GradDen; // The same for the color this->GraduR = ( (c3.R-c1.R)*(v2-v1)-(c2.R-c1.R)*(v3-v1) ) * GradDen; this->GradvR = ( (c2.R-c1.R)*(u3-u1)-(c3.R-c1.R)*(u2-u1) ) * GradDen; this->GraduG = ( (c3.G-c1.G)*(v2-v1)-(c2.G-c1.G)*(v3-v1) ) * GradDen; this->GradvG = ( (c2.G-c1.G)*(u3-u1)-(c3.G-c1.G)*(u2-u1) ) * GradDen; this->GraduB = ( (c3.B-c1.B)*(v2-v1)-(c2.B-c1.B)*(v3-v1) ) * GradDen; this->GradvB = ( (c2.B-c1.B)*(u3-u1)-(c3.B-c1.B)*(u2-u1) ) * GradDen; } // ----------------------------------------------------------------------------------------------- CVector getInterpolatedVertex( double u, double v ) { CVector vRet; vRet.x = (float)(this->GraduPx*(u-this->InitU) + this->GradvPx*(v-this->InitV) + this->InitPx); vRet.y = (float)(this->GraduPy*(u-this->InitU) + this->GradvPy*(v-this->InitV) + this->InitPy); vRet.z = (float)(this->GraduPz*(u-this->InitU) + this->GradvPz*(v-this->InitV) + this->InitPz); return vRet; } // ----------------------------------------------------------------------------------------------- CVector getInterpolatedNormal( double u, double v ) { CVector vRet; vRet.x = (float)(this->GraduNx*(u-this->InitU) + this->GradvNx*(v-this->InitV) + this->InitNx); vRet.y = (float)(this->GraduNy*(u-this->InitU) + this->GradvNy*(v-this->InitV) + this->InitNy); vRet.z = (float)(this->GraduNz*(u-this->InitU) + this->GradvNz*(v-this->InitV) + this->InitNz); vRet.normalize(); return vRet; } // ----------------------------------------------------------------------------------------------- CRGBAF getInterpolatedColor( double u, double v ) { CRGBAF vRet; vRet.R = (float)(this->GraduR*(u-this->InitU) + this->GradvR*(v-this->InitV) + this->InitR); vRet.G = (float)(this->GraduG*(u-this->InitU) + this->GradvG*(v-this->InitV) + this->InitG); vRet.B = (float)(this->GraduB*(u-this->InitU) + this->GradvB*(v-this->InitV) + this->InitB); if (vRet.R < 0.0f) vRet.R = 0.0f; if (vRet.G < 0.0f) vRet.G = 0.0f; if (vRet.B < 0.0f) vRet.B = 0.0f; if (vRet.R > 255.0f) vRet.R = 255.0f; if (vRet.G > 255.0f) vRet.G = 255.0f; if (vRet.B > 255.0f) vRet.B = 255.0f; return vRet; } // ----------------------------------------------------------------------------------------------- // Uin and Vin are out of the face pF so calculate a U,V in face CVector getInterpolatedVertexInFace( double Uin, double Vin, CMesh::CFace *pF ) { double Uout, Vout; double Utmp, Vtmp; double u1 = pF->Corner[0].Uvws[1].U, v1 = pF->Corner[0].Uvws[1].V; double u2 = pF->Corner[1].Uvws[1].U, v2 = pF->Corner[1].Uvws[1].V; double u3 = pF->Corner[2].Uvws[1].U, v3 = pF->Corner[2].Uvws[1].V; double rDist = 10000000.0f, rDistTmp, factor; // Get the nearest point from (Uin,Vin) to the face pF rDistTmp = sqrt( (Uin-u1)*(Uin-u1) + (Vin-v1)*(Vin-v1) ); if( rDistTmp < rDist ) { rDist = rDistTmp; Uout = u1; Vout = v1; } rDistTmp = sqrt( (Uin-u2)*(Uin-u2) + (Vin-v2)*(Vin-v2) ); if( rDistTmp < rDist ) { rDist = rDistTmp; Uout = u2; Vout = v2; } rDistTmp = sqrt( (Uin-u3)*(Uin-u3) + (Vin-v3)*(Vin-v3) ); if( rDistTmp < rDist ) { rDist = rDistTmp; Uout = u3; Vout = v3; } factor = ( (Uin-u1)*(u2-u1) + (Vin-v1)*(v2-v1) ) / ( (u2-u1)*(u2-u1) + (v2-v1)*(v2-v1) ); if( ( factor >= 0.0 ) && ( factor <= 1.0 ) ) { Utmp = u1+(u2-u1)*factor; Vtmp = v1+(v2-v1)*factor; rDistTmp = sqrt( (Uin-Utmp)*(Uin-Utmp) + (Vin-Vtmp)*(Vin-Vtmp) ); if( rDistTmp < rDist ) { rDist = rDistTmp; Uout = Utmp; Vout = Vtmp; } } factor = ( (Uin-u2)*(u3-u2) + (Vin-v2)*(v3-v2) ) / ( (u3-u2)*(u3-u2) + (v3-v2)*(v3-v2) ); if( ( factor >= 0.0 ) && ( factor <= 1.0 ) ) { Utmp = u2+(u3-u2)*factor; Vtmp = v2+(v3-v2)*factor; rDistTmp = sqrt( (Uin-Utmp)*(Uin-Utmp) + (Vin-Vtmp)*(Vin-Vtmp) ); if( rDistTmp < rDist ) { rDist = rDistTmp; Uout = Utmp; Vout = Vtmp; } } factor = ( (Uin-u3)*(u1-u3) + (Vin-v3)*(v1-v3) ) / ( (u1-u3)*(u1-u3) + (v1-v3)*(v1-v3) ); if( ( factor >= 0.0 ) && ( factor <= 1.0 ) ) { Utmp = u3+(u1-u3)*factor; Vtmp = v3+(v1-v3)*factor; rDistTmp = sqrt( (Uin-Utmp)*(Uin-Utmp) + (Vin-Vtmp)*(Vin-Vtmp) ); if( rDistTmp < rDist ) { rDist = rDistTmp; Uout = Utmp; Vout = Vtmp; } } // Calculate the 3d point return getInterpolatedVertex( Uout, Vout ); } }; // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- CExportNelOptions gOptions; // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- /* void SortFaceByTextureName(vector &AllFaces, CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB) { int i, j; int nNbFace = AllFaces.size(); for( i = 0; i < nNbFace-1; ++i ) for( j = i+1; j < nNbFace; ++j ) { ITexture *pT = pMBB->Materials[AllFaces[i]->MaterialId].getTexture(0); CTextureFile *pTF = dynamic_cast(pT); string namei = "Default"; if( pTF != NULL ) namei = pTF->getFileName(); pT = pMBB->Materials[AllFaces[j]->MaterialId].getTexture(0); pTF = dynamic_cast(pT); string namej = "Default"; if( pTF != NULL ) namej = pTF->getFileName(); if( namei < namej ) { CMesh::CFace *pFaceTemp = AllFaces[i]; AllFaces[i] = AllFaces[j]; AllFaces[j] = pFaceTemp; } } } // ----------------------------------------------------------------------------------------------- // TextureNames is an array which indicates the number of faces that follows which have the same texture name void ComputeAreaOfTextureName(vector &TextureNames, vector &AllFaces, CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB) { int i, nNbFace = AllFaces.size(); TextureNames.resize(nNbFace); ITexture *pT = pMBB->Materials[AllFaces[0]->MaterialId].getTexture(0); CTextureFile *pTF = dynamic_cast(pT); string CurrentName = "Default"; sint32 lastface = 0, nNbTexName = 0; if( pTF != NULL ) CurrentName = pTF->getFileName(); for( i = 0; i < nNbFace; ++i ) { ITexture *pT = pMBB->Materials[AllFaces[i]->MaterialId].getTexture(0); CTextureFile *pTF = dynamic_cast(pT); string namei = "Default"; if( pTF != NULL ) namei = pTF->getFileName(); if( ( namei != CurrentName ) || ( i == (nNbFace-1) ) ) { CurrentName = namei; TextureNames[nNbTexName] = i-lastface; nNbTexName++; lastface = i; } } TextureNames[nNbTexName-1] += 1; TextureNames.resize( nNbTexName ); } */ // ----------------------------------------------------------------------------------------------- void ClearFaceWithNoLM( CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB, vector &ZeFaces ) { sint32 i; vector::iterator ItParseI = ZeFaces.begin(); sint32 nNbFace = ZeFaces.size(); for( i = 0; i < nNbFace; ++i ) { CMesh::CFace *pF = *ItParseI; if( pMBB->Materials[pF->MaterialId].getShader() != CMaterial::LightMap ) { ItParseI = ZeFaces.erase( ItParseI ); nNbFace--; i--; } else { ++ItParseI; } } } // ----------------------------------------------------------------------------------------------- void SortFaceByMaterialId( vector &FaceGroup, vector::iterator ItFaces, sint32 nNbFace ) { int i, j; sint32 nMatID; // Bubble sort face vector::iterator ItParseI = ItFaces; for( i = 0; i < nNbFace-1; ++i ) { vector::iterator ItParseJ = ItParseI; ++ItParseJ; for( j = i+1; j < nNbFace; ++j ) { if( (*ItParseI)->MaterialId < (*ItParseJ)->MaterialId ) { CMesh::CFace *pFaceTemp = *ItParseI; *ItParseI = *ItParseJ; *ItParseJ = pFaceTemp; } ++ItParseJ; } ++ItParseI; } // Indicates the groups FaceGroup.resize( nNbFace ); ItParseI = ItFaces; j = 0; nMatID = (*ItParseI)->MaterialId; ++ItParseI; FaceGroup[j] = 1; for( i = 1; i < nNbFace; ++i ) { if( (*ItParseI)->MaterialId != nMatID ) { nMatID = (*ItParseI)->MaterialId; ++j; FaceGroup[j] = 1; } else { FaceGroup[j] ++; } ++ItParseI; } FaceGroup.resize( j+1 ); } // ----------------------------------------------------------------------------------------------- // Test if the 2 faces are continuous (same vertex, same normal (if wanted), same uv (if wanted)) bool FaceContinuous( CMesh::CFace *pF1, CMesh::CFace *pF2, bool bTestUV = true, bool bTestNormal = true ) { sint8 i, j, inext, jnext; sint8 F1c[2] = { -1, -1 }; sint8 F2c[2] = { -1, -1 }; // Is there a vertices continuity for( j = 0; j < 3; ++j ) for( i = 0; i < 3; ++i ) { inext = (i == 2) ? 0 : (i+1); jnext = (j == 2) ? 0 : (j+1); if( (pF1->Corner[j].Vertex == pF2->Corner[i].Vertex) && (pF1->Corner[jnext].Vertex == pF2->Corner[inext].Vertex) ) { F1c[0] = j; F1c[1] = jnext; F2c[0] = i; F2c[1] = inext; break; } if( (pF1->Corner[j].Vertex == pF2->Corner[inext].Vertex) && (pF1->Corner[jnext].Vertex == pF2->Corner[i].Vertex) ) { F1c[0] = jnext; F1c[1] = j; F2c[0] = i; F2c[1] = inext; break; } } // No -> out if( F1c[0] == -1 ) return false; // Here we get the vertex continuity between F1c[0] and F2c[0], and, F1c[1] and F2c[1] // Is there a normal continuity if( bTestNormal ) for( i = 0; i < 2; ++i ) { CVector n1 = pF1->Corner[F1c[i]].Normal; CVector n2 = pF2->Corner[F2c[i]].Normal; // is n1 equal to n2 ? double epsilon = 1.0 - (n1*n2); // theorically n1*n2 equal to 1.0 but epsilon error if( epsilon > 0.001 ) return false; } // Is there a mapping continuity if( bTestUV ) for( i = 0; i < 2; ++i ) { if((fabs( pF1->Corner[F1c[i]].Uvws[1].U - pF2->Corner[F2c[i]].Uvws[1].U) > 0.001) || (fabs( pF1->Corner[F1c[i]].Uvws[1].V - pF2->Corner[F2c[i]].Uvws[1].V) > 0.001) ) return false; } return true; } struct FaceNext { CMesh::CFace *face; vector cont; uint8 nNbCont; bool added; }; // ----------------------------------------------------------------------------------------------- void SortFaceBySMoothGroup( vector &FaceGroup, vector::iterator ItFaces, uint32 nNbFace ) { uint32 i, j, k, nCurSG_NbFace, nCurSG_NbFaceLeft, nGroupNb; vector FacesNext; vector FacesFinal; if (gOptions.FeedBack != NULL) { string sTmp = "Grouping (1/2)"; gOptions.FeedBack->setLine (4, sTmp); gOptions.FeedBack->update (); } vector::iterator ItParseI = ItFaces; FacesNext.resize (nNbFace); FacesFinal.resize (nNbFace); for (i = 0; i < nNbFace; ++i, ++ItParseI) { FacesNext[i].face = *ItParseI; FacesNext[i].cont.reserve(3); FacesNext[i].added = false; } vector::iterator ItParseJ = ItFaces; for (j = 0; j < nNbFace; ++j, ++ItParseJ) { ItParseI = ItParseJ; for (i = j; i < nNbFace; ++i, ++ItParseI) if (i != j) { if (FaceContinuous (*ItParseI, *ItParseJ, false)) { FacesNext[j].cont.resize (FacesNext[j].cont.size()+1); FacesNext[j].cont[FacesNext[j].cont.size()-1] = i; FacesNext[i].cont.resize (FacesNext[i].cont.size()+1); FacesNext[i].cont[FacesNext[i].cont.size()-1] = j; } } } if (gOptions.FeedBack != NULL) { string sTmp = "Grouping (2/2)"; gOptions.FeedBack->setLine (4, sTmp); gOptions.FeedBack->update (); } FaceGroup.resize (nNbFace); for( j = 0; j < nNbFace; ++j ) FaceGroup[j] = 1; nGroupNb = 0; i = 0; for (j = 0; j < nNbFace; ++j) { if (FacesNext[j].added == false) { FacesFinal[i] = FacesNext[j]; FacesNext[j].added = true; nCurSG_NbFace = 1; for (nCurSG_NbFaceLeft = 1; nCurSG_NbFaceLeft > 0; --nCurSG_NbFaceLeft) { for (k = 0; k < FacesFinal[i].cont.size(); ++k) if (FacesNext[FacesFinal[i].cont[k]].added == false) { FacesNext[FacesFinal[i].cont[k]].added = true; FacesFinal[i+nCurSG_NbFaceLeft] = FacesNext[FacesFinal[i].cont[k]]; ++nCurSG_NbFace; ++nCurSG_NbFaceLeft; } ++i; } FaceGroup[nGroupNb] = nCurSG_NbFace; ++nGroupNb; } } FaceGroup.resize(nGroupNb); ItParseI = ItFaces; for (i = 0; i < nNbFace; ++i, ++ItParseI) *ItParseI = FacesFinal[i].face; } // ----------------------------------------------------------------------------------------------- void SortFaceByTextureSurface( int offset, int nNbFace, vector &AllFaces ) { int i, j; for( i = 0; i < nNbFace-1; ++i ) for( j = i+1; j < nNbFace; ++j ) if( AllFaces[i]->MaterialId == AllFaces[j]->MaterialId ) { // Texture surface of the i face = .5*|(u1-u0)*(v2-v0)-(v1-v0)*(u2-u0)| // in fact this is lightmap mapping surface double surfacei = 0.5*fabs( (AllFaces[i]->Corner[1].Uvws[1].U - AllFaces[i]->Corner[0].Uvws[1].U)* (AllFaces[i]->Corner[2].Uvws[1].V - AllFaces[i]->Corner[0].Uvws[1].V)- (AllFaces[i]->Corner[1].Uvws[1].V - AllFaces[i]->Corner[0].Uvws[1].V)* (AllFaces[i]->Corner[2].Uvws[1].U - AllFaces[i]->Corner[0].Uvws[1].U) ); double surfacej = 0.5*fabs( (AllFaces[j]->Corner[1].Uvws[1].U - AllFaces[j]->Corner[0].Uvws[1].U)* (AllFaces[j]->Corner[2].Uvws[1].V - AllFaces[j]->Corner[0].Uvws[1].V)- (AllFaces[j]->Corner[1].Uvws[1].V - AllFaces[j]->Corner[0].Uvws[1].V)* (AllFaces[j]->Corner[2].Uvws[1].U - AllFaces[j]->Corner[0].Uvws[1].U) ); if( surfacei < surfacej ) { CMesh::CFace *pFaceTemp = AllFaces[i]; AllFaces[i] = AllFaces[j]; AllFaces[j] = pFaceTemp; } } } // ----------------------------------------------------------------------------------------------- bool isInTriangleOrEdge(double x, double y, double xt1, double yt1, double xt2, double yt2, double xt3, double yt3) { // Test vector T1X and T1T2 double sign1 = ((xt2-xt1)*(y-yt1) - (yt2-yt1)*(x-xt1)); // Test vector T2X and T2T3 double sign2 = ((xt3-xt2)*(y-yt2) - (yt3-yt2)*(x-xt2)); // Test vector T3X and T3T1 double sign3 = ((xt1-xt3)*(y-yt3) - (yt1-yt3)*(x-xt3)); if( (sign1 <= 0.0)&&(sign2 <= 0.0)&&(sign3 <= 0.0) ) return true; if( (sign1 >= 0.0)&&(sign2 >= 0.0)&&(sign3 >= 0.0) ) return true; return false; } // ----------------------------------------------------------------------------------------------- bool isInTriangle(double x, double y, double xt1, double yt1, double xt2, double yt2, double xt3, double yt3) { // Test vector T1X and T1T2 double sign1 = ((xt2-xt1)*(y-yt1) - (yt2-yt1)*(x-xt1)); // Test vector T2X and T2T3 double sign2 = ((xt3-xt2)*(y-yt2) - (yt3-yt2)*(x-xt2)); // Test vector T3X and T3T1 double sign3 = ((xt1-xt3)*(y-yt3) - (yt1-yt3)*(x-xt3)); if( (sign1 < 0.0)&&(sign2 < 0.0)&&(sign3 < 0.0) ) return true; if( (sign1 > 0.0)&&(sign2 > 0.0)&&(sign3 > 0.0) ) return true; return false; } // Segment line intersection P1P2 and P3P4 // ----------------------------------------------------------------------------------------------- bool segmentIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double denominator = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1); if( denominator == 0.0 ) return false; // The segment are colinear double k = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3) ) / denominator; if( (k<=0.0) || (k>=1.0) ) return false; k = ( (x2-x1)*(y1-y3) - (y2-y1)*(x1-x3) ) / denominator; if( (k<=0.0) || (k>=1.0) ) return false; return true; } // ----------------------------------------------------------------------------------------------- bool vertexInSquare(double xs, double ys, double ws, double hs, double xv, double yv) { if ((fabs(xv - xs) < ws) && (fabs(yv - ys) < hs)) return true; else return false; } // ----------------------------------------------------------------------------------------------- // Automatic mapping of a face (dont remove) void MapFace( CMesh::CFace *pFace, vector &Vertices, float rRatio ) { CVector V01 = Vertices[pFace->Corner[1].Vertex] - Vertices[pFace->Corner[0].Vertex]; CVector V02 = Vertices[pFace->Corner[2].Vertex] - Vertices[pFace->Corner[0].Vertex]; CVector n = V01 ^ V02; n.normalize(); // Quantize the normal // Table of unitary vector with relevant direction to map The I vector represent the plane normal // and the J,K vector the U,V vector CMatrix QuantizationTbl[3]; QuantizationTbl[0].identity(); QuantizationTbl[1].identity(); QuantizationTbl[1].rotateZ((float)(Pi/2.0)); QuantizationTbl[2].identity(); QuantizationTbl[2].rotateY((float)(Pi/2.0)); float fMax = 0.0f; int pos = 0; for( int i = 0; i < 3; ++i ) { if( fMax < fabsf(QuantizationTbl[i].getI()*n) ) { fMax = fabsf(QuantizationTbl[i].getI()*n); pos = i; } } // Map with the i_th vector from the quantization table // Projection of the 3 vertices of the triangle on the plane // defined by the quantized vector (as the plane normal) and the origin (as a point in the plane) // This is equivalent to a base changement with annulation of the I vector CMatrix invMat = QuantizationTbl[pos].inverted(); CVector newPtinUVBasis = invMat.mulPoint(Vertices[pFace->Corner[0].Vertex]); pFace->Corner[0].Uvws[1].U = newPtinUVBasis.y / rRatio; pFace->Corner[0].Uvws[1].V = newPtinUVBasis.z / rRatio; newPtinUVBasis = invMat.mulPoint(Vertices[pFace->Corner[1].Vertex]); pFace->Corner[1].Uvws[1].U = newPtinUVBasis.y / rRatio; pFace->Corner[1].Uvws[1].V = newPtinUVBasis.z / rRatio; newPtinUVBasis = invMat.mulPoint(Vertices[pFace->Corner[2].Vertex]); pFace->Corner[2].Uvws[1].U = newPtinUVBasis.y / rRatio; pFace->Corner[2].Uvws[1].V = newPtinUVBasis.z / rRatio; } // ----------------------------------------------------------------------------------------------- CMatrix getObjectToWorldMatrix( CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB ) { CMatrix m1; m1.identity(); // T*P m1.translate (pMBB->DefaultPos + pMBB->DefaultPivot); // R*S*P-1. m1.rotate (pMBB->DefaultRotQuat); m1.scale (pMBB->DefaultScale); m1.translate (-pMBB->DefaultPivot); return m1; } // ----------------------------------------------------------------------------------------------- float getUVDist( CUV& UV1, CUV& UV2 ) { return sqrtf( (UV2.U - UV1.U)*(UV2.U - UV1.U) + (UV2.V - UV1.V)*(UV2.V - UV1.V) ); } // ----------------------------------------------------------------------------------------------- void getLightNodeList (std::vector& vectLightNode, TimeValue tvTime, Interface& ip, bool visibleOnly, INode*node=NULL ) { if( node == NULL ) node = ip.GetRootNode(); SLightBuild nelLight; if (nelLight.canConvertFromMaxLight(node, tvTime)) { // visible, or add hidden too? if(!visibleOnly || !node->IsHidden()) { // Yoyo: if this light is checked to lightMap export int nLMExport= CExportNel::getScriptAppData (node, NEL3D_APPDATA_EXPORT_LIGHTMAP_LIGHT, BST_CHECKED); if(nLMExport == BST_CHECKED) vectLightNode.push_back (node); } } // Recurse sub node for (int i=0; iNumberOfChildren(); i++) getLightNodeList (vectLightNode, tvTime, ip, visibleOnly, node->GetChildNode(i)); } // ----------------------------------------------------------------------------------------------- void getLightmapLightBuilds( vector &lights, TimeValue tvTime, Interface& ip, bool visibleOnly ) { vector nodeLights; getLightNodeList (nodeLights, tvTime, ip, visibleOnly); lights.resize(nodeLights.size()); for(uint32 i = 0; i < nodeLights.size(); ++i) { lights[i].convertFromMaxLight (nodeLights[i], tvTime); } } // ----------------------------------------------------------------------------------------------- double calculateTriangleSurface( CVector &p1, CVector &p2, CVector &p3 ) { CVector n = ((p2-p1)^(p3-p1)); return 0.5 * n.norm(); // Half of the norm } // ----------------------------------------------------------------------------------------------- void MoveFaceUV1( vector::iterator ItFace, sint32 nNbFace, double rOffsU, double rOffsV ) { sint32 i,j ; for( i = 0; i < nNbFace; ++i ) { CMesh::CFace *pF = (*ItFace); for( j = 0; j < 3; ++j ) { pF->Corner[j].Uvws[1].U += (float)rOffsU; pF->Corner[j].Uvws[1].V += (float)rOffsV; } ++ItFace; } } // ----------------------------------------------------------------------------------------------- void MultiplyFaceUV1( vector::iterator ItFace, sint32 nNbFace, double rFactor ) { sint32 i,j ; for( i = 0; i < nNbFace; ++i ) { CMesh::CFace *pF = (*ItFace); for( j = 0; j < 3; ++j ) { pF->Corner[j].Uvws[1].U *= (float)rFactor; pF->Corner[j].Uvws[1].V *= (float)rFactor; } ++ItFace; } } // ----------------------------------------------------------------------------------------------- bool PutFaceUV1InLumelCoord( double rRatioLightMap, vector &Vertices, vector::iterator ItFace, sint32 nNbFace ) { sint32 i, j; double SpaceSurf = 0.0, TextureSurf = 0.0; vector::iterator ItParseI = ItFace; for( i = 0; i < nNbFace; ++i ) { CVector p1, p2, p3; CMesh::CFace* pF = (*ItParseI); p1 = Vertices[pF->Corner[0].Vertex]; p2 = Vertices[pF->Corner[1].Vertex]; p3 = Vertices[pF->Corner[2].Vertex]; SpaceSurf += calculateTriangleSurface( p1, p2, p3 ); p1.x = pF->Corner[0].Uvws[1].U; p1.y = pF->Corner[0].Uvws[1].V; p1.z = 0.0f; p2.x = pF->Corner[1].Uvws[1].U; p2.y = pF->Corner[1].Uvws[1].V; p2.z = 0.0f; p3.x = pF->Corner[2].Uvws[1].U; p3.y = pF->Corner[2].Uvws[1].V; p3.z = 0.0f; TextureSurf += calculateTriangleSurface( p1, p2, p3 ); // Next face ++ItParseI; } if( TextureSurf < 0.00001 ) return false; double LMTextRatio = sqrt(SpaceSurf / TextureSurf) * (1.0/rRatioLightMap); ItParseI = ItFace; for( i = 0; i < nNbFace; ++i ) { CMesh::CFace* pF = (*ItParseI); for( j = 0; j < 3; ++j ) // Express the UVs in lumel for each corner { pF->Corner[j].Uvws[1].U *= (float)LMTextRatio; pF->Corner[j].Uvws[1].V *= (float)LMTextRatio; } ++ItParseI; } return true; } // ----------------------------------------------------------------------------------------------- void PutFaceUV1InTextureCoord( sint32 TextureSizeX, sint32 TextureSizeY, vector::iterator ItFace, sint32 nNbFace ) { sint32 i,j; for( i = 0; i < nNbFace; ++i ) { for( j = 0; j < 3; ++j ) { CMesh::CFace *pF = *ItFace; pF->Corner[j].Uvws[1].U /= (float)TextureSizeX; pF->Corner[j].Uvws[1].V /= (float)TextureSizeY; } // Next face ++ItFace; } } // ----------------------------------------------------------------------------------------------- bool IsFaceCoverFace( CMesh::CFace *pF1, CMesh::CFace *pF2 ) { sint32 i, j; for( j = 0; j < 3; ++j ) for( i = 0; i < 3; ++i ) if( segmentIntersection(pF1->Corner[i].Uvws[1].U, pF1->Corner[i].Uvws[1].V, pF1->Corner[(i+1)%3].Uvws[1].U, pF1->Corner[(i+1)%3].Uvws[1].V, pF2->Corner[j].Uvws[1].U, pF2->Corner[j].Uvws[1].V, pF2->Corner[(j+1)%3].Uvws[1].U, pF2->Corner[(j+1)%3].Uvws[1].V ) ) return true; for( i = 0; i < 3; ++i ) if( isInTriangle( pF1->Corner[i].Uvws[1].U, pF1->Corner[i].Uvws[1].V, pF2->Corner[0].Uvws[1].U, pF2->Corner[0].Uvws[1].V, pF2->Corner[1].Uvws[1].U, pF2->Corner[1].Uvws[1].V, pF2->Corner[2].Uvws[1].U, pF2->Corner[2].Uvws[1].V ) ) return true; for( i = 0; i < 3; ++i ) if( isInTriangle( pF2->Corner[i].Uvws[1].U, pF2->Corner[i].Uvws[1].V, pF1->Corner[0].Uvws[1].U, pF1->Corner[0].Uvws[1].V, pF1->Corner[1].Uvws[1].U, pF1->Corner[1].Uvws[1].V, pF1->Corner[2].Uvws[1].U, pF1->Corner[2].Uvws[1].V ) ) return true; return false; } // ----------------------------------------------------------------------------------------------- void SortFaceByPlane( vector &FaceGroup, vector::iterator ItFace, sint32 nNbFace ) { sint32 j, k, nGroupNb = 0; FaceGroup.resize( nNbFace ); for( j = 0; j < nNbFace; ++j ) FaceGroup[j] = 1; vector::iterator CurGrpBeg = ItFace; vector::iterator CurGrpEnd = ItFace; sint32 nGroupOffset = 1; list lifo; lifo.clear(); for( nGroupOffset = 1; nGroupOffset <= nNbFace; ) { lifo.push_front( *CurGrpBeg ); // Do a complete plane : Graph traversal in width while( ! lifo.empty() ) { CMesh::CFace *pFace = lifo.back(); lifo.pop_back(); vector::iterator ItParseJ = CurGrpEnd; ++ItParseJ; for( j = nGroupOffset; j < nNbFace; ++j ) { if( FaceContinuous( pFace, *ItParseJ ) ) { // Is this face cover other face present in the current group ? vector::iterator ItParseK = CurGrpBeg; bool bFaceCovering = false; for( k = 0; k < FaceGroup[nGroupNb]; ++k ) { if( IsFaceCoverFace( *ItParseK, *ItParseJ ) ) { bFaceCovering = true; break; } ++ItParseK; } // The face do not cover other face -> add it to current group if( !bFaceCovering ) { lifo.push_front( *ItParseJ ); ++CurGrpEnd; CMesh::CFace *pFaceTemp = *CurGrpEnd; *CurGrpEnd = *ItParseJ; *ItParseJ = pFaceTemp; nGroupOffset += 1; FaceGroup[nGroupNb] += 1; } } ++ItParseJ; } } ++CurGrpEnd; CurGrpBeg = CurGrpEnd; ++nGroupNb; nGroupOffset += 1; } FaceGroup.resize( nGroupNb ); } // ----------------------------------------------------------------------------------------------- void SortPlanesBySurface( vector &planes ) { uint32 i, j; for( i = 0; i < planes.size()-1; ++i ) for( j = i+1; j < planes.size(); ++j ) { if( (planes[i]->w *planes[i]->h) < (planes[j]->w *planes[j]->h) ) { SLMPlane *tmp = planes[i]; planes[i] = planes[j]; planes[j] = tmp; } } } // ----------------------------------------------------------------------------------------------- void ModifyLMPlaneWithOverSampling( SLMPlane *pPlane, double rOverSampling, bool bCreateMask ) { uint32 i, j; vector::iterator ItFace = pPlane->faces.begin(); uint32 nNbFace = pPlane->faces.size(); pPlane->stretch( rOverSampling ); MultiplyFaceUV1( ItFace, nNbFace, rOverSampling ); if( bCreateMask ) { // Reset the mask for( j = 0; j < pPlane->w*pPlane->h; ++j ) pPlane->msk[j] = 0; ItFace = pPlane->faces.begin(); // Recreate the form for( i = 0; i < nNbFace; ++i ) { CMesh::CFace *pF = *ItFace; TTicks ttTemp3 = CTime::getPerformanceTime(); pPlane->createFromFace (pF); ++ItFace; } } } // ----------------------------------------------------------------------------------------------- void PlaceLMPlaneInLMPLane( SLMPlane &Dst, SLMPlane &Src ) { TTicks ttTemp = CTime::getPerformanceTime(); while( true ) { if( ! Src.tryAllPosToPutIn( Dst ) ) { if( ( Dst.w < MAXLIGHTMAPSIZE ) || ( Dst.h < MAXLIGHTMAPSIZE ) ) { if( Dst.w < Dst.h ) Dst.resize( Dst.w*2, Dst.h ); else Dst.resize( Dst.w, Dst.h*2 ); } else { // ERROR: we reached the maximum texture size break; } } else { // We found a position Src.putIn( Dst ); break; } } } // ----------------------------------------------------------------------------------------------- CRGBAF LightAVertex( uint8 &rtVal, CVector &pRT, CVector &p, CVector &n, vector &vLights, vector &AllLights, CRTWorld &wrt, bool bDoubleSided, bool bRcvShadows ) { CRGBAF rgbafRet; rgbafRet.R = rgbafRet.G = rgbafRet.B = rgbafRet.A = 0.0; // Color calculation for( uint32 nLight = 0; nLight < vLights.size(); ++nLight ) { SLightBuild &rLight = AllLights[vLights[nLight]]; CRGBAF lightAmbiCol = CRGBAF(0.0f, 0.0f, 0.0f, 0.0f); CRGBAF lightDiffCol = CRGBAF(0.0f, 0.0f, 0.0f, 0.0f); CRGBAF lightSpecCol = CRGBAF(0.0f, 0.0f, 0.0f, 0.0f); CRGBAF RTFactor = CRGBAF(0.0f, 0.0f, 0.0f, 0.0f); float light_intensity = 0.0; switch( rLight.Type ) { case SLightBuild::LightAmbient: lightAmbiCol.R = rLight.Ambient.R / 255.0f; lightAmbiCol.G = rLight.Ambient.G / 255.0f; lightAmbiCol.B = rLight.Ambient.B / 255.0f; lightAmbiCol.A = rLight.Ambient.A / 255.0f; light_intensity = 1.0; break; case SLightBuild::LightPoint: { CVector p_light = rLight.Position - p; float p_light_distance = p_light.norm(); if( p_light_distance < rLight.rRadiusMin ) light_intensity = 1.0f; else if( p_light_distance > rLight.rRadiusMax ) light_intensity = 0.0f; else light_intensity = 1.0f - (p_light_distance-rLight.rRadiusMin)/(rLight.rRadiusMax-rLight.rRadiusMin); p_light.normalize(); if( bDoubleSided && (n*p_light < 0.0f) ) { p_light = -p_light; } light_intensity *= rLight.rMult; lightAmbiCol.R = light_intensity * rLight.Ambient.R / 255.0f; lightAmbiCol.G = light_intensity * rLight.Ambient.G / 255.0f; lightAmbiCol.B = light_intensity * rLight.Ambient.B / 255.0f; lightAmbiCol.A = light_intensity * rLight.Ambient.A / 255.0f; light_intensity *= max(0.0f, n*p_light); lightDiffCol.R = light_intensity * rLight.Diffuse.R / 255.0f; lightDiffCol.G = light_intensity * rLight.Diffuse.G / 255.0f; lightDiffCol.B = light_intensity * rLight.Diffuse.B / 255.0f; lightDiffCol.A = light_intensity * rLight.Diffuse.A / 255.0f; } break; case SLightBuild::LightDir: { CVector p_light = - rLight.Direction; p_light.normalize(); if( bDoubleSided && (n*p_light < 0.0f) ) { p_light = -p_light; } light_intensity = rLight.rMult; lightAmbiCol.R = light_intensity * rLight.Ambient.R / 255.0f; lightAmbiCol.G = light_intensity * rLight.Ambient.G / 255.0f; lightAmbiCol.B = light_intensity * rLight.Ambient.B / 255.0f; lightAmbiCol.A = light_intensity * rLight.Ambient.A / 255.0f; light_intensity *= max(0.0f, n*p_light); lightDiffCol.R = light_intensity * rLight.Diffuse.R / 255.0f; lightDiffCol.G = light_intensity * rLight.Diffuse.G / 255.0f; lightDiffCol.B = light_intensity * rLight.Diffuse.B / 255.0f; lightDiffCol.A = light_intensity * rLight.Diffuse.A / 255.0f; } break; case SLightBuild::LightSpot: { CVector p_light = rLight.Position - p; float p_light_distance = p_light.norm(); if( p_light_distance < rLight.rRadiusMin ) light_intensity = 1.0f; else if( p_light_distance > rLight.rRadiusMax ) light_intensity = 0.0f; else light_intensity = 1.0f - (p_light_distance-rLight.rRadiusMin)/(rLight.rRadiusMax-rLight.rRadiusMin); p_light.normalize(); float ang = acosf( p_light * (-rLight.Direction) ); if( ang > rLight.rFallof ) light_intensity = 0.0f; else if( ang > rLight.rHotspot ) light_intensity *= 1.0f - (ang-rLight.rHotspot)/(rLight.rFallof-rLight.rHotspot); light_intensity *= rLight.rMult; if( bDoubleSided && (n*p_light < 0.0f) ) { p_light = -p_light; } lightAmbiCol.R = light_intensity * rLight.Ambient.R / 255.0f; lightAmbiCol.G = light_intensity * rLight.Ambient.G / 255.0f; lightAmbiCol.B = light_intensity * rLight.Ambient.B / 255.0f; lightAmbiCol.A = light_intensity * rLight.Ambient.A / 255.0f; light_intensity *= max(0.0f, n*p_light); lightDiffCol.R = light_intensity * rLight.Diffuse.R / 255.0f; lightDiffCol.G = light_intensity * rLight.Diffuse.G / 255.0f; lightDiffCol.B = light_intensity * rLight.Diffuse.B / 255.0f; lightDiffCol.A = light_intensity * rLight.Diffuse.A / 255.0f; // Apply projected image if some if ((rLight.ProjBitmap.getHeight() != 0) && (light_intensity > 0.0f)) { // Make the plane where the texture is CPlane plane; // Projection plane CVector ori = rLight.Position + rLight.mProj.getK(); CVector norm = rLight.mProj.getK(); plane.make( norm, ori ); CVector inter = plane.intersect( rLight.Position, p ); // Intersection conversion in i,j coordinate system with ori as origin //float dotSize = gOptions.rLumelSize * (rLight.Position-inter).norm() / (rLight.Position-p).norm(); float x = -(inter-ori)*rLight.mProj.getI(); float y = (inter-ori)*rLight.mProj.getJ(); // Normalization x [-tan(fallof),tan(fallof)] -> [0,1] x = ((x / tanf( rLight.rFallof ))+1.0f)/2.0f; y = ((y / tanf( rLight.rFallof ))+1.0f)/2.0f; CRGBAF col = rLight.ProjBitmap.getColor(x, y); lightDiffCol.R *= col.R; lightDiffCol.G *= col.G; lightDiffCol.B *= col.B; lightDiffCol.A *= col.A; } } break; default: break; } if( light_intensity > 0.0f ) { if( bRcvShadows && rLight.bCastShadow && gOptions.bShadow ) RTFactor = wrt.raytrace (pRT, vLights[nLight], rtVal, gOptions.nExportLighting==1); else RTFactor = CRGBAF(1.0f, 1.0f, 1.0f, 1.0f); } rgbafRet.R += lightAmbiCol.R + lightDiffCol.R * RTFactor.R; if( rgbafRet.R > 2.0f ) rgbafRet.R = 2.0; rgbafRet.G += lightAmbiCol.G + lightDiffCol.G * RTFactor.G; if( rgbafRet.G > 2.0f ) rgbafRet.G = 2.0; rgbafRet.B += lightAmbiCol.B + lightDiffCol.B * RTFactor.B; if( rgbafRet.B > 2.0f ) rgbafRet.B = 2.0; rgbafRet.A += lightAmbiCol.A + lightDiffCol.A * RTFactor.A; if( rgbafRet.A > 2.0f ) rgbafRet.A = 2.0; } return rgbafRet; } // ----------------------------------------------------------------------------------------------- bool segmentIntersectBSphere( CVector &p1, CVector &p2, CBSphere &bs ) { // Is one point is in the sphere ? CVector r = bs.Center - p1; float f; if( r.norm() <= bs.Radius ) return true; r = bs.Center - p2; if( r.norm() <= bs.Radius ) return true; // Is the orthogonal projection of the center on the segment is in the sphere ? r = p2 - p1; f = r.norm(); f = ( r * (bs.Center - p1) ) / ( f * f ); if( ( f >= 0.0 ) && ( f <= 1.0 ) ) { r = bs.Center - (p1 + r*f); if( r.norm() <= bs.Radius ) return true; } return false; } // ----------------------------------------------------------------------------------------------- void FirstLight( CMesh::CMeshBuild* pMB, CMeshBase::CMeshBaseBuild *pMBB, SLMPlane &Plane, vector &vVertices, CMatrix& ToWorldMat, vector &vLights, vector &AllLights, uint32 nLayerNb, CRTWorld &wrt ) { // Fill interiors vector::iterator ItFace = Plane.faces.begin(); uint32 nNbFace = Plane.faces.size(); uint32 i; sint32 j, k; double rMinU = 1000000.0, rMaxU = -1000000.0, rMinV = 1000000.0, rMaxV = -1000000.0; sint32 nPosMinU, nPosMaxU, nPosMinV, nPosMaxV; CMesh::CFace *pF; SGradient g; for( i = 0; i < Plane.w*Plane.h; ++i ) if( Plane.msk[i] != 0 ) Plane.msk[i] = 1; for( i = 0; i < nNbFace; ++i ) { pF = *ItFace; bool doubleSided = pMBB->Materials[pF->MaterialId].getDoubleSided(); CRGBA matDiff = pMBB->Materials[pF->MaterialId].getDiffuse(); // Select bounding square of the triangle for( j = 0; j < 3; ++j ) { if( rMinU > pF->Corner[j].Uvws[1].U ) rMinU = pF->Corner[j].Uvws[1].U; if( rMaxU < pF->Corner[j].Uvws[1].U ) rMaxU = pF->Corner[j].Uvws[1].U; if( rMinV > pF->Corner[j].Uvws[1].V ) rMinV = pF->Corner[j].Uvws[1].V; if( rMaxV < pF->Corner[j].Uvws[1].V ) rMaxV = pF->Corner[j].Uvws[1].V; } nPosMaxU = ((sint32)floor( rMaxU + 0.5 )); nPosMaxV = ((sint32)floor( rMaxV + 0.5 )); nPosMinU = ((sint32)floor( rMinU - 0.5 )); nPosMinV = ((sint32)floor( rMinV - 0.5 )); CVector n1 = ToWorldMat.mulVector( pF->Corner[0].Normal ); CVector n2 = ToWorldMat.mulVector( pF->Corner[1].Normal ); CVector n3 = ToWorldMat.mulVector( pF->Corner[2].Normal ); g.init( pF, vVertices, n1, n2, n3 ); // Process all the interior for( k = nPosMinV; k <= nPosMaxV; ++k ) for( j = nPosMinU; j <= nPosMaxU; ++j ) if( Plane.msk[j-Plane.x + (k-Plane.y)*Plane.w] == 1 ) { if( isInTriangleOrEdge( j+0.5, k+0.5, pF->Corner[0].Uvws[1].U, pF->Corner[0].Uvws[1].V, pF->Corner[1].Uvws[1].U, pF->Corner[1].Uvws[1].V, pF->Corner[2].Uvws[1].U, pF->Corner[2].Uvws[1].V ) ) { CVector p = g.getInterpolatedVertex( j+0.5, k+0.5); CVector n = g.getInterpolatedNormal( j+0.5, k+0.5); CRGBAF vl = g.getInterpolatedColor( j+0.5, k+0.5); uint8 rtVal = Plane.ray[j-Plane.x + (k-Plane.y)*Plane.w]; CRGBAF col = LightAVertex( rtVal, p, p, n, vLights, AllLights, wrt, doubleSided, pMBB->bRcvShadows ); Plane.ray[j-Plane.x + (k-Plane.y)*Plane.w] = rtVal; Plane.col[j-Plane.x + (k-Plane.y)*Plane.w+Plane.w*Plane.h*nLayerNb].R = col.R*(vl.R/255.0f)*(matDiff.R/255.0f); Plane.col[j-Plane.x + (k-Plane.y)*Plane.w+Plane.w*Plane.h*nLayerNb].G = col.G*(vl.G/255.0f)*(matDiff.G/255.0f); Plane.col[j-Plane.x + (k-Plane.y)*Plane.w+Plane.w*Plane.h*nLayerNb].B = col.B*(vl.B/255.0f)*(matDiff.B/255.0f); Plane.col[j-Plane.x + (k-Plane.y)*Plane.w+Plane.w*Plane.h*nLayerNb].A = 1.0f; // Darken the plane to indicate pixel is calculated Plane.msk[j-Plane.x + (k-Plane.y)*Plane.w] = 2; } } // Next Face ++ItFace; } } // ----------------------------------------------------------------------------------------------- void SecondLight( CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB, vector::iterator ItPlanes, uint32 nNbPlanes, vector &vVertices, CMatrix& ToWorldMat, vector &vLights, vector &AllLights, uint32 nLayerNb, CRTWorld &wrt) { // Fill interiors uint32 nPlanes1; vector::iterator ItPlanes1 = ItPlanes; for( nPlanes1 = 0; nPlanes1 < nNbPlanes; ++nPlanes1 ) { uint32 i; sint32 j, k; sint32 nPosMinU, nPosMaxU, nPosMinV, nPosMaxV; SGradient g; SLMPlane *pP1 = *ItPlanes1; vector::iterator ItParseI = pP1->faces.begin(); uint32 nNbFace1 = pP1->faces.size(); for( i = 0; i < nNbFace1; ++i ) { CMesh::CFace *pF1 = *ItParseI; double rMinU = 1000000.0, rMaxU = -1000000.0, rMinV = 1000000.0, rMaxV = -1000000.0; bool doubleSided = pMBB->Materials[pF1->MaterialId].getDoubleSided(); CRGBA matDiff = pMBB->Materials[pF1->MaterialId].getDiffuse(); // Select bounding square of the triangle for( j = 0; j < 3; ++j ) { if( rMinU > pF1->Corner[j].Uvws[1].U ) rMinU = pF1->Corner[j].Uvws[1].U; if( rMaxU < pF1->Corner[j].Uvws[1].U ) rMaxU = pF1->Corner[j].Uvws[1].U; if( rMinV > pF1->Corner[j].Uvws[1].V ) rMinV = pF1->Corner[j].Uvws[1].V; if( rMaxV < pF1->Corner[j].Uvws[1].V ) rMaxV = pF1->Corner[j].Uvws[1].V; } nPosMaxU = ((sint32)floor( rMaxU + 0.5 )); nPosMaxV = ((sint32)floor( rMaxV + 0.5 )); nPosMinU = ((sint32)floor( rMinU - 0.5 )); nPosMinV = ((sint32)floor( rMinV - 0.5 )); CVector n1 = ToWorldMat.mulVector( pF1->Corner[0].Normal ); CVector n2 = ToWorldMat.mulVector( pF1->Corner[1].Normal ); CVector n3 = ToWorldMat.mulVector( pF1->Corner[2].Normal ); g.init( pF1, vVertices, n1, n2, n3 ); double lumx1 = pF1->Corner[0].Uvws[1].U, lumy1 = pF1->Corner[0].Uvws[1].V, lumx2 = pF1->Corner[1].Uvws[1].U, lumy2 = pF1->Corner[1].Uvws[1].V, lumx3 = pF1->Corner[2].Uvws[1].U, lumy3 = pF1->Corner[2].Uvws[1].V; // Process all the exterior and try to link with other planes for( k = nPosMinV; k < nPosMaxV; ++k ) for( j = nPosMinU; j < nPosMaxU; ++j ) if( ( pP1->msk[j-pP1->x + (k-pP1->y)*pP1->w] == 1 ) || ( pP1->msk[1+j-pP1->x + (k-pP1->y)*pP1->w] == 1 ) || ( pP1->msk[1+j-pP1->x + (1+k-pP1->y)*pP1->w] == 1 ) || ( pP1->msk[j-pP1->x + (1+k-pP1->y)*pP1->w] == 1 ) ) if( segmentIntersection(j+0.5, k+0.5, j+1.5, k+0.5, lumx1, lumy1, lumx2, lumy2) || segmentIntersection(j+0.5, k+0.5, j+1.5, k+0.5, lumx2, lumy2, lumx3, lumy3) || segmentIntersection(j+0.5, k+0.5, j+1.5, k+0.5, lumx3, lumy3, lumx1, lumy1) || segmentIntersection(j+0.5, k+0.5, j+0.5, k+1.5, lumx1, lumy1, lumx2, lumy2) || segmentIntersection(j+0.5, k+0.5, j+0.5, k+1.5, lumx2, lumy2, lumx3, lumy3) || segmentIntersection(j+0.5, k+0.5, j+0.5, k+1.5, lumx3, lumy3, lumx1, lumy1) || segmentIntersection(j+1.5, k+1.5, j+1.5, k+0.5, lumx1, lumy1, lumx2, lumy2) || segmentIntersection(j+1.5, k+1.5, j+1.5, k+0.5, lumx2, lumy2, lumx3, lumy3) || segmentIntersection(j+1.5, k+1.5, j+1.5, k+0.5, lumx3, lumy3, lumx1, lumy1) || segmentIntersection(j+1.5, k+1.5, j+0.5, k+1.5, lumx1, lumy1, lumx2, lumy2) || segmentIntersection(j+1.5, k+1.5, j+0.5, k+1.5, lumx2, lumy2, lumx3, lumy3) || segmentIntersection(j+1.5, k+1.5, j+0.5, k+1.5, lumx3, lumy3, lumx1, lumy1) || vertexInSquare(j+0.5, k+0.5, 1.0, 1.0, lumx1, lumy1) || vertexInSquare(j+0.5, k+0.5, 1.0, 1.0, lumx2, lumy2) || vertexInSquare(j+0.5, k+0.5, 1.0, 1.0, lumx3, lumy3) ) { // If all segment of the current face are linked with a face in this plane, no need to continue vector::iterator ItParseM = pP1->faces.begin(); uint32 nNbSeg = 0; uint32 m, n; for( m = 0; m < nNbFace1; ++m ) { CMesh::CFace *pF2 = *ItParseM; if( m != i ) if( FaceContinuous( pF1, pF2 ) ) ++nNbSeg; ++ItParseM; } if( nNbSeg >= 3 ) continue; // Get the face on the other plane with a common segment vector::iterator ItParsePlanes = ItPlanes; for( m = 0; m < nNbPlanes; ++m ) { SLMPlane *pP2 = *ItParsePlanes; if( pP2 != pP1 ) for( n = 0; n < pP2->faces.size(); ++n ) { CMesh::CFace *pF2 = pP2->faces[n]; if( FaceContinuous( pF1, pF2 ) ) { for( uint32 o = 0; o < 4; ++o ) { sint32 nAbsX = j + (o/2), nAbsY = k + (o%2); // Is it a pixel to treat and pixel in the 2nd plane if( ( pP1->msk[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w] == 1 ) && (nAbsX >= pP2->x) && (nAbsX < (pP2->x+(sint32)pP2->w) ) && (nAbsY >= pP2->y) && (nAbsY < (pP2->y+(sint32)pP2->h) ) ) { // Is it an interior calculated pixel ? if( pP2->msk[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w] == 2 ) { // Yes -> ok so get it pP1->col[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w+pP1->w*pP1->h*nLayerNb] = pP2->col[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w+pP2->w*pP2->h*nLayerNb]; pP1->msk[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w] = 3; pP1->ray[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w] = pP2->ray[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w]; } else if( pP2->msk[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w] == 1 ) { // No -> Add extrapolated value CVector iv = g.getInterpolatedVertex( ((double)nAbsX)+0.5, ((double)nAbsY)+0.5); CVector in = g.getInterpolatedNormal( ((double)nAbsX)+0.5, ((double)nAbsY)+0.5); CRGBAF vl = g.getInterpolatedColor( j+0.5, k+0.5); CVector rv = g.getInterpolatedVertexInFace( ((double)nAbsX)+0.5, ((double)nAbsY)+0.5, pF1 ); uint8 rtVal = pP2->ray[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w]; CRGBAF col = LightAVertex( rtVal, rv, iv, in, vLights, AllLights, wrt, doubleSided, pMBB->bRcvShadows ); pP2->ray[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w] = rtVal; //float f = 1.0f; pP2->col[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w+pP2->w*pP2->h*nLayerNb].R += col.R*(vl.R/255.0f)*(matDiff.R/255.0f); pP2->col[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w+pP2->w*pP2->h*nLayerNb].G += col.G*(vl.G/255.0f)*(matDiff.G/255.0f); pP2->col[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w+pP2->w*pP2->h*nLayerNb].B += col.B*(vl.B/255.0f)*(matDiff.B/255.0f); pP2->col[nAbsX-pP2->x + (nAbsY-pP2->y)*pP2->w+pP2->w*pP2->h*nLayerNb].A += 1.0f; } } } } } ++ItParsePlanes; } for( sint32 o = 0; o < 4; ++o ) { sint32 nAbsX = j + (o/2), nAbsY = k + (o%2); if( pP1->msk[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w] == 1 ) { CVector iv = g.getInterpolatedVertex( ((double)nAbsX)+0.5, ((double)nAbsY)+0.5); CVector in = g.getInterpolatedNormal( ((double)nAbsX)+0.5, ((double)nAbsY)+0.5); CRGBAF vl = g.getInterpolatedColor( j+0.5, k+0.5); CVector rv = g.getInterpolatedVertexInFace( ((double)nAbsX)+0.5, ((double)nAbsY)+0.5, pF1 ); uint8 rtVal = pP1->ray[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w]; CRGBAF col = LightAVertex( rtVal, rv, iv, in, vLights, AllLights, wrt, doubleSided, pMBB->bRcvShadows ); pP1->ray[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w] = rtVal; //float f = 1.0f; pP1->col[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w+pP1->w*pP1->h*nLayerNb].R += col.R*(vl.R/255.0f)*(matDiff.R/255.0f); pP1->col[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w+pP1->w*pP1->h*nLayerNb].G += col.G*(vl.G/255.0f)*(matDiff.G/255.0f); pP1->col[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w+pP1->w*pP1->h*nLayerNb].B += col.B*(vl.B/255.0f)*(matDiff.B/255.0f); pP1->col[nAbsX-pP1->x + (nAbsY-pP1->y)*pP1->w+pP1->w*pP1->h*nLayerNb].A += 1.0f; } } } // Next Face ++ItParseI; } ++ItPlanes1; } // All planes are done so now we have to average the value of lumels grouping severals normals ItPlanes1 = ItPlanes; for( nPlanes1 = 0; nPlanes1 < nNbPlanes; ++nPlanes1 ) { uint32 j, k; SLMPlane *pP1 = *ItPlanes1; for( k = 0; k < pP1->h; ++k ) for( j = 0; j < pP1->w; ++j ) { if( pP1->msk[j+k*pP1->w] == 1 ) { sint32 nNbNormals = (sint32)pP1->col[j + k*pP1->w+pP1->w*pP1->h*nLayerNb].A; // Avoid divid by zero if (nNbNormals) { pP1->col[j + k*pP1->w+pP1->w*pP1->h*nLayerNb].R /= nNbNormals; pP1->col[j + k*pP1->w+pP1->w*pP1->h*nLayerNb].G /= nNbNormals; pP1->col[j + k*pP1->w+pP1->w*pP1->h*nLayerNb].B /= nNbNormals; } pP1->col[j + k*pP1->w+pP1->w*pP1->h*nLayerNb].A = 1.0f; pP1->msk[j + k*pP1->w] = 4; } } ++ItPlanes1; } } // ----------------------------------------------------------------------------------------------- bool isAllFaceMapped( vector::iterator ItFace, sint32 nNbFaces ) { sint32 i, j; vector::iterator ItParseI = ItFace; for( i = 0; i < nNbFaces; ++i ) { CMesh::CFace *pF = *ItParseI; for( j = 0; j < 3; ++j ) { if( (fabsf(pF->Corner[j].Uvws[1].U) > 64.0) || (fabsf(pF->Corner[j].Uvws[1].V) > 64.0) ) return false; } ++ItParseI; } double TextureSurf = 0.0f; ItParseI = ItFace; for( i = 0; i < nNbFaces; ++i ) { CMesh::CFace *pF = *ItParseI; CVector p1, p2, p3; p1.x = pF->Corner[0].Uvws[1].U; p1.y = pF->Corner[0].Uvws[1].V; p1.z = 0.0f; p2.x = pF->Corner[1].Uvws[1].U; p2.y = pF->Corner[1].Uvws[1].V; p2.z = 0.0f; p3.x = pF->Corner[2].Uvws[1].U; p3.y = pF->Corner[2].Uvws[1].V; p3.z = 0.0f; TextureSurf += calculateTriangleSurface( p1, p2, p3 ); ++ItParseI; } if( fabs(TextureSurf) < 0.000001 ) return false; return true; } // ----------------------------------------------------------------------------------------------- CAABBox getMeshBBox (CMesh::CMeshBuild& rMB, CMeshBase::CMeshBaseBuild &rMBB, bool bNeedToTransform) { CAABBox meshBox; if( bNeedToTransform ) { CMatrix MBMatrix = getObjectToWorldMatrix (&rMB, &rMBB); MBMatrix.movePos (-vGlobalPos); for( uint32 j = 0; j < rMB.Vertices.size(); ++j ) if( j == 0 ) meshBox.setCenter( MBMatrix * rMB.Vertices[j] ); else meshBox.extend( MBMatrix * rMB.Vertices[j] ); } else { for( uint32 j = 0; j < rMB.Vertices.size(); ++j ) if( j == 0 ) meshBox.setCenter( rMB.Vertices[j] ); else meshBox.extend( rMB.Vertices[j] ); } return meshBox; } // ----------------------------------------------------------------------------------------------- CAABBox getLightBBox( CVector &vPos, float rRadius ) { CAABBox lightBox; lightBox.setCenter( vPos ); lightBox.extend( vPos - CVector(rRadius,0,0) ); lightBox.extend( vPos + CVector(rRadius,0,0) ); lightBox.extend( vPos - CVector(0,rRadius,0) ); lightBox.extend( vPos + CVector(0,rRadius,0) ); lightBox.extend( vPos - CVector(0,0,rRadius) ); lightBox.extend( vPos + CVector(0,0,rRadius) ); return lightBox; } // ----------------------------------------------------------------------------------------------- bool isLightCanCastShadowOnBox( SLightBuild &rSLB, CAABBox &b ) { switch( rSLB.Type ) { case SLightBuild::LightAmbient: // No need an ambient light... // No ambient handled for the moment break; case SLightBuild::LightSpot: // For the moment spot like point case SLightBuild::LightPoint: { CAABBox lightBox = getLightBBox( rSLB.Position, rSLB.rRadiusMax ); if( lightBox.intersect( b ) ) return true; if( b.include( lightBox.getMin() ) ) return true; if( lightBox.include( b.getMin() ) ) return true; } break; case SLightBuild::LightDir: // Attenuation not handled (ask cyril later) break; } return false; } // ----------------------------------------------------------------------------------------------- bool isInteractionLightMesh( SLightBuild &rSLB, CAABBox &meshBox) { if (rSLB.Type == SLightBuild::LightAmbient) return true; if (rSLB.Type == SLightBuild::LightDir) { // Construct acceleration for a dir light to not select all nodes in scene rSLB.rDirRadius = meshBox.getHalfSize().norm(); rSLB.Position = meshBox.getCenter(); return true; } return isLightCanCastShadowOnBox( rSLB, meshBox ); } // ----------------------------------------------------------------------------------------------- bool isInteractionLightMesh( SLightBuild &rSLB, CMesh::CMeshBuild &rMB, CMeshBase::CMeshBaseBuild &rMBB ) { CAABBox meshBox; meshBox = getMeshBBox( rMB, rMBB, true ); return isInteractionLightMesh(rSLB, meshBox); } // ----------------------------------------------------------------------------------------------- // Get all lights that can cast shadows on the current mesh void getLightInteract( CMesh::CMeshBuild* pMB, CMeshBase::CMeshBaseBuild *pMBB, vector &AllLights, vector< vector >&vvLights ) { uint32 nNbGroup = 0; vector vlbTmp; uint32 i, j; for( i = 0; i < AllLights.size(); ++i ) { if( isInteractionLightMesh( AllLights[i], *pMB, *pMBB ) ) { // Is the light name already exist for( j = 0; j < nNbGroup; ++j ) if ( ( AllLights[vvLights[j].operator[](0)].AnimatedLight == AllLights[i].AnimatedLight ) && ( AllLights[vvLights[j].operator[](0)].LightGroup == AllLights[i].LightGroup ) ) break; // The light name does not exist create a new group if ( j == nNbGroup ) { vvLights.push_back( vlbTmp ); // Static lighting vvLights[nNbGroup].push_back( i ); ++nNbGroup; } else { vvLights[j].push_back( i ); } } } } // ----------------------------------------------------------------------------------------------- void convertToWorldCoordinate (CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB, CVector &translation) { uint32 j, k; CMatrix MBMatrix = getObjectToWorldMatrix (pMB, pMBB); MBMatrix.movePos (translation); // Update vertices for( j = 0; j < pMB->Vertices.size(); ++j ) pMB->Vertices[j] = MBMatrix * pMB->Vertices[j]; // Update normals MBMatrix.invert(); MBMatrix.transpose(); for( j = 0; j < pMB->Faces.size(); ++j ) for( k = 0; k < 3 ; ++k ) pMB->Faces[j].Corner[k].Normal = MBMatrix.mulVector( pMB->Faces[j].Corner[k].Normal ); } // ----------------------------------------------------------------------------------------------- // Is the box b1 can cast shadow on the box b2 with the light l ? bool isBoxCanCastShadowOnBoxWithLight( CAABBox &b1, CAABBox &b2, SLightBuild &l ) { // if the light is included in the box b2 return isLightCanCastShadowOnBox( l, b1 ); } // ----------------------------------------------------------------------------------------------- void supprLightNoInteractOne( vector &vLights, CMesh::CMeshBuild* pMB, CMeshBase::CMeshBaseBuild *pMBB, INode &node ) { uint32 i; // temp result vector result; result.reserve(vLights.size()); // build the bb of the shape only one time CAABBox meshBox; meshBox = getMeshBBox( *pMB, *pMBB, true ); // for all lights for( i = 0; i < vLights.size(); ++i ) { bool bInteract = false; if( vLights[i].setExclusion.find( node.GetName() ) != vLights[i].setExclusion.end() ) { bInteract = false; } else { if( isInteractionLightMesh( vLights[i], meshBox) ) { bInteract = true; } } if( bInteract ) { result.push_back(vLights[i]); } } // copy result vLights= result; } // ----------------------------------------------------------------------------------------------- // Add information for ont mesh to reference all the lights that interact with him void addLightInfo( CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB, string &animatedLight, uint lightGroup, uint8 nMatNb, uint8 nStageNb ) { /* Search in the light mesh info the good light group. Add the material stage if it exists. * Else, add a new entry in the light group. */ const uint count = pMBB->LightInfoMap.size (); uint i; for (i=0; iLightInfoMap[i]; if ( (info.AnimatedLight == animatedLight) && (info.LightGroup == lightGroup) ) { // This one, append the material stage CMeshBase::CLightMapInfoList::CMatStage temp; temp.MatId = nMatNb; temp.StageId = nStageNb; info.StageList.push_back (temp); break; } } // Not found ? if (i == count) { // Add a new entry CMeshBase::CLightMapInfoList info; info.LightGroup = lightGroup; info.AnimatedLight = animatedLight; CMeshBase::CLightMapInfoList::CMatStage temp; temp.MatId = nMatNb; temp.StageId = nStageNb; info.StageList.push_back (temp); pMBB->LightInfoMap.push_back (info); } } // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- void CExportNel::deleteLM(INode& ZeNode) { sint32 i; // Suppress all lightmap files for( i = 0; i < 8; ++i ) { string sSaveName; sSaveName = _Options.sExportLighting; if( sSaveName[sSaveName.size()-1] != '\\' ) sSaveName += "\\"; sSaveName += ZeNode.GetName(); char tmp[32]; sprintf( tmp, "%d", i ); sSaveName += tmp; sSaveName += ".tga"; FILE *file; if( file = fopen(sSaveName.c_str(),"rb") ) { fclose( file ); DeleteFile( sSaveName.c_str() ); } } } // In : set of faces // Out : planes /* void computeSetOfFaces (vector&planes, ) */ // Object that touch the reference object struct SSurObj { CMesh::CMeshBuild *pMB; CMeshBase::CMeshBaseBuild *pMBB; vector faces; }; // Get the objects next to one /* void sans_majuscule_au_debut_LinkToObjectAround (CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB, vector& vertices, SWorldRT& wrt) { CAABBox ref = getMeshBBox (*pMB, *pMBB, true); vector objs; uint32 i, k, l, m; for( i = 0; wrt.vMB.size(); ++i) { if (ref.intersect (getMeshBBox (*wrt.vMB[i], *wrt.vMBB[i], false))) { SSurObj oTmp; vector ivert; oTmp.pMB = wrt.vMB[i]; oTmp.pMBB = wrt.vMBB[i]; // check a vertex continuity (precision is 1 centimeter) for (k = 0; k < vertices.size(); ++k) for (l = 0; l < wrt.vMB[i]->Vertices.size(); ++l) { float dist = (vertices[k] - wrt.vMB[i]->Vertices[l]).norm(); if (dist < 0.01) { ivert.push_back (l); } } if (ivert.size() > 0) { // Get all faces that contains at least one shared vertex for (k = 0; k < wrt.vMB[i]->Faces.size(); ++k) for (l = 0; l < ivert.size(); ++l) { if ((wrt.vMB[i]->Faces[k].Corner[0].Vertex == ivert[l]) || (wrt.vMB[i]->Faces[k].Corner[1].Vertex == ivert[l]) || (wrt.vMB[i]->Faces[k].Corner[2].Vertex == ivert[l])) { bool bToAdd = true; // Check if the face is not already added for (m = 0; m < oTmp.faces.size(); ++m) if (oTmp.faces[m] == &wrt.vMB[i]->Faces[k]) { bToAdd = false; break; } if (bToAdd) oTmp.faces.push_back (&wrt.vMB[i]->Faces[k]); } } // Add object objs.push_back (oTmp); } } } // Here objs contains the objects of the worldRT structure that touch the reference mesh // And it contains the pointer to the face that must be rendered to give continuity to our object } */ void createLightmapLog (COFile &outputLog, const char *outputDirectory, const char *projectName, const char *objectName) { string outputFilename = CPath::standardizePath (outputDirectory, true); outputFilename += projectName; outputFilename += "_"; outputFilename += objectName; outputFilename += ".txt"; if (!outputLog.open (outputFilename, false, true, false)) { nlwarning ("Can't open the file %s for writing.", outputFilename.c_str()); } } void appendLightmapLog (COFile &outputLog, const char *lightmapName, const vector lightIndexes, const vector &lights) { try { string text; text = lightmapName; text += " :\n"; // Dump light name used on this lightmap uint i; for (i=0; i 0.0f) gOptions.rLumelSize *= rLumelSizeMul; //if (gOptions.nExportLighting == 1) // return calculateLMRad (pZeMeshBuild, pZeMeshBaseBuild, ZeNode, ip, tvTime, absolutePath, gOptions); if(gOptions.FeedBack != NULL) { string sTmp = "LumelSize = " + toString(gOptions.rLumelSize); if (gOptions.bShadow) sTmp += ",Shadow On"; else sTmp += ",Shadow Off"; sTmp += ",OverSampling = " +toString(gOptions.nOverSampling*gOptions.nOverSampling); gOptions.FeedBack->setLine (1, sTmp); } CRTWorld WorldRT (_ErrorInDialog, _View, _AbsolutePath, _Ip, _ErrorTitle, this); // The static world for raytrace vector AllLights; CMatrix mtmp = getObjectToWorldMatrix (pZeMeshBuild, pZeMeshBaseBuild); vGlobalPos = mtmp.getPos(); vGlobalPos.x = (float)((int)vGlobalPos.x); vGlobalPos.y = (float)((int)vGlobalPos.y); vGlobalPos.z = (float)((int)vGlobalPos.z); if (gOptions.FeedBack != NULL) { string sTmp = "Initializing"; gOptions.FeedBack->setLine (2, sTmp); gOptions.FeedBack->update (); } // Select meshes to test for raytrace // Get all lights from MAX getLightmapLightBuilds( AllLights, tvTime, *_Ip ); // Get all lights L that have influence over the mesh selected supprLightNoInteractOne( AllLights, pZeMeshBuild, pZeMeshBaseBuild, ZeNode ); // Get all the lod child nodes and the node for which this node is a lod std::set lodListToExclude; addChildLodNode (lodListToExclude); addParentLodNode (ZeNode, lodListToExclude); // Remove from exclude the node lodListToExclude.erase (&ZeNode); // List to include std::set lodListToInclude; lodListToInclude.insert (&ZeNode); // Get all meshes that are influenced by the lights L WorldRT.build (AllLights, -vGlobalPos, gOptions.bExcludeNonSelected, lodListToExclude, lodListToInclude); //for( nNode=0; nNode < nNbMesh; ++nNode ) { // First order face by Material and by texture surface CMesh::CMeshBuild *pMB = pZeMeshBuild; CMeshBase::CMeshBaseBuild *pMBB = pZeMeshBaseBuild; vector AllFaces; CMatrix MBMatrix = getObjectToWorldMatrix (pMB, pMBB); MBMatrix.movePos (-vGlobalPos); vector AllVertices; // All vertices in world space vector FaceGroupByMat; // Number of faces with the same properties uint32 nNbFace = pMB->Faces.size(), nNbVertex = pMB->Vertices.size(); sint32 offsetMat, offsetSmooth, offsetPlane; vector AllPlanes; sint32 AllPlanesPrevSize; vector< vector > vvLights; // Select Lights interacting with the node getLightInteract( pMB, pMBB, AllLights, vvLights ); AllPlanes.clear(); // Make Geometry like we want // Make a vector of pointer to all the faces of the MeshBuild AllFaces.resize( nNbFace ); for( i = 0; i < nNbFace; ++i ) AllFaces[i] = &pMB->Faces[i]; // Make All vertices of the mesh in the world basis AllVertices.resize(nNbVertex); for( i = 0; i < nNbVertex; ++i ) AllVertices[i] = MBMatrix * pMB->Vertices[i]; // Invert and transpose for use of futur normal MBMatrix.invert(); MBMatrix.transpose(); // Bubble sort pointer to the faces (Material sorting) ClearFaceWithNoLM( pMB, pMBB, AllFaces ); if( AllFaces.size() == 0 ) { if (InfoLog) InfoLog->display("CalculateLM : %d ms\n", timeGetTime()-t); return false; } if (gOptions.FeedBack != NULL) { string sTmp = "Calculating"; gOptions.FeedBack->setLine (2, sTmp); gOptions.FeedBack->update (); } SortFaceByMaterialId (FaceGroupByMat, AllFaces.begin(), AllFaces.size()); if( ! isAllFaceMapped (AllFaces.begin(), AllFaces.size() ) ) { string thetext; thetext = "Warning "; thetext += ZeNode.GetName(); thetext = "have all faces NOT mapped (UV2)"; if (gOptions.FeedBack != NULL) { gOptions.FeedBack->setLine (11, thetext); gOptions.FeedBack->update (); } //MessageBox( NULL, thetext.c_str(), "LightMap ERROR", MB_OK|MB_ICONERROR ); if (InfoLog) InfoLog->display("CalculateLM : %d ms\n", timeGetTime()-t); return false; } // PATCH FaceGroupByMat.resize(1); FaceGroupByMat[0] = AllFaces.size(); offsetMat = 0; for( uint32 nMat = 0; nMat < FaceGroupByMat.size(); ++nMat ) { vector FaceGroupBySmooth; if (gOptions.FeedBack != NULL) { string sTmp = "Material (" + toString(1+nMat) + "/" + toString(FaceGroupByMat.size()) + ")"; gOptions.FeedBack->setLine (3, sTmp); gOptions.FeedBack->update (); } // Sort faces by smoothing group SortFaceBySMoothGroup( FaceGroupBySmooth, AllFaces.begin()+offsetMat, FaceGroupByMat[nMat] ); offsetSmooth = offsetMat; for( uint32 nSmoothNb = 0; nSmoothNb < FaceGroupBySmooth.size(); ++nSmoothNb ) { sint32 nPlaneNb, nLight; vector FaceGroupByPlane; if (gOptions.FeedBack != NULL) { string sTmp = "SmoothGroup (" + toString(1+nSmoothNb) + "/" + toString(FaceGroupBySmooth.size()) + ")"; gOptions.FeedBack->setLine (4, sTmp); gOptions.FeedBack->update (); } if( ! PutFaceUV1InLumelCoord( gOptions.rLumelSize, AllVertices, AllFaces.begin()+offsetSmooth, FaceGroupBySmooth[nSmoothNb] ) ) { // Make an error message string sTmp = "Warning : "; sTmp += ZeNode.GetName(); sTmp += " has mapping problem"; // Script trace mprintf ((sTmp+"\n").c_str()); // Feedback is here ? if (gOptions.FeedBack != NULL) { gOptions.FeedBack->setLine (11, sTmp); sTmp = "Mat(" + toString(1+nMat) + ") at least one group with texture"; gOptions.FeedBack->setLine (12, sTmp); sTmp = "surface equal to zero"; gOptions.FeedBack->setLine (13, sTmp); gOptions.FeedBack->update (); } continue; } SortFaceByPlane( FaceGroupByPlane, AllFaces.begin()+offsetSmooth, FaceGroupBySmooth[nSmoothNb] ); //AllPlanes.resize( FaceGroupByPlane.size() ); //SortPlanesBySurface( FaceGroupByPlane, AllFaces.begin()+offsetSmooth, FaceGroupBySmooth[nSmoothNb] ); AllPlanesPrevSize = AllPlanes.size(); AllPlanes.resize( AllPlanesPrevSize + FaceGroupByPlane.size() ); offsetPlane = offsetSmooth; for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) { AllPlanes[AllPlanesPrevSize+nPlaneNb] = new SLMPlane; for( nLight = 0; nLight < ((sint32)vvLights.size()-1); ++nLight ) AllPlanes[AllPlanesPrevSize+nPlaneNb]->newLayer(); // Fill planes (part of lightmap) AllPlanes[AllPlanesPrevSize+nPlaneNb]->createFromFaceGroup( AllFaces.begin()+offsetPlane, FaceGroupByPlane[nPlaneNb] ); // Next group of face with the same plane in the same smooth group of the same material offsetPlane += FaceGroupByPlane[nPlaneNb]; } // Make join between all planes (all planes must be created) for( nLight = 0; nLight < (sint)vvLights.size(); ++nLight ) { vector TempPlanes; TempPlanes.resize(FaceGroupByPlane.size()); for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) { TempPlanes[nPlaneNb] = new SLMPlane; TempPlanes[nPlaneNb]->createFromPlane (*AllPlanes[AllPlanesPrevSize+nPlaneNb]); } for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) { // Light the LightMap for the plane (interior only) FirstLight( pMB, pMBB, *TempPlanes[nPlaneNb], AllVertices, MBMatrix, vvLights[nLight], AllLights, 0, WorldRT ); } // Make extoriors SecondLight( pMB, pMBB, TempPlanes.begin(), FaceGroupByPlane.size(), AllVertices, MBMatrix, vvLights[nLight], AllLights, 0, WorldRT ); // Oversampling optimization if( gOptions.nOverSampling > 1 ) { for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) { // Detect which pixels need to be& oversampled TempPlanes[nPlaneNb]->contourDetect(); // Enlarge image ModifyLMPlaneWithOverSampling( TempPlanes[nPlaneNb], gOptions.nOverSampling, true ); // And the contour detection and the mask TempPlanes[nPlaneNb]->andRayWidthMask(); } for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) FirstLight( pMB, pMBB, *TempPlanes[nPlaneNb], AllVertices, MBMatrix, vvLights[nLight], AllLights, 0, WorldRT ); SecondLight( pMB, pMBB, TempPlanes.begin(), FaceGroupByPlane.size(), AllVertices, MBMatrix, vvLights[nLight], AllLights, 0, WorldRT ); for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) ModifyLMPlaneWithOverSampling( TempPlanes[nPlaneNb], 1.0/((double)gOptions.nOverSampling), false ); } for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) TempPlanes[nPlaneNb]->copyFirstLayerTo(*AllPlanes[AllPlanesPrevSize+nPlaneNb],(uint8)nLight); for( nPlaneNb = 0; nPlaneNb < (sint)FaceGroupByPlane.size(); ++nPlaneNb ) delete TempPlanes[nPlaneNb]; } // Next group of face with the same smooth group and the same material offsetSmooth += FaceGroupBySmooth[nSmoothNb]; } // Next group of face with the same material offsetMat += FaceGroupByMat[nMat]; } // Create the lightmap if (gOptions.FeedBack != NULL) { string sTmp = "Placement"; gOptions.FeedBack->setLine (3, sTmp); sTmp = ""; for(i=4;i<10;++i) gOptions.FeedBack->setLine (i, sTmp); gOptions.FeedBack->update (); } // Optimize the planes if we can (if a plane contains the same color) for (i = 0; i < AllPlanes.size(); ++i) { SLMPlane *pPlane = AllPlanes[i]; bool bIsGoodForOptimization = true; for (j = 0; j < pPlane->nNbLayerUsed; ++j) { CRGBAF rAverage = pPlane->getAverageColor ((uint8)j); if (!pPlane->isSameColorAs((uint8)j, rAverage, 1.0f/256.0f)) { bIsGoodForOptimization = false; break; } } if (bIsGoodForOptimization) { vector allColors; uint32 k; for (j = 0; j < pPlane->nNbLayerUsed; ++j) allColors.push_back(pPlane->getAverageColor ((uint8)j)); for (j = 0; j < pPlane->faces.size(); ++j) for (k = 0; k < 3; ++k) { float u, v; u = pPlane->faces[j]->Corner[k].Uvws[1].U - (float)pPlane->x - 0.5f; u = u / (float)pPlane->w; v = pPlane->faces[j]->Corner[k].Uvws[1].V - (float)pPlane->y - 0.5f; v = v / (float)pPlane->h; if (u < 0.0f) u = 0.0f; if (u > 1.0f) u = 1.0f; if (v < 0.0f) v = 0.0f; if (v > 1.0f) v = 1.0f; pPlane->faces[j]->Corner[k].Uvws[1].U = u + (float)pPlane->x + 0.5f; pPlane->faces[j]->Corner[k].Uvws[1].V = v + (float)pPlane->y + 0.5f; } pPlane->resize (2,2); for (j = 0; j < pPlane->nNbLayerUsed; ++j) for (k = 0; k < 4; ++k) pPlane->col[k+j*4] = allColors[j]; for (k = 0; k < 4; ++k) pPlane->msk[k] = 1; // Just set the mask to a non zero value } } // Place the planes in the lightmap SLMPlane LightMap; SortPlanesBySurface( AllPlanes ); for( i = 0; i < AllPlanes.size(); ++i ) { if (gOptions.FeedBack != NULL) { string sTmp = "Plane (" + toString(i) + "/" + toString(AllPlanes.size()) + ")"; gOptions.FeedBack->setLine (4, sTmp); gOptions.FeedBack->update (); } // Put in the basis of the plane MoveFaceUV1( AllPlanes[i]->faces.begin(), AllPlanes[i]->faces.size(), -AllPlanes[i]->x, -AllPlanes[i]->y ); PlaceLMPlaneInLMPLane( LightMap, *AllPlanes[i] ); LightMap.nNbLayerUsed = AllPlanes[i]->nNbLayerUsed; // Put in the new basis MoveFaceUV1( AllPlanes[i]->faces.begin(), AllPlanes[i]->faces.size(), AllPlanes[i]->x, AllPlanes[i]->y ); delete AllPlanes[i]; } // Save the lightmap // Assign the name of the lightmap and get the complete save name // Get the name of the max project char projectName[512]; _splitpath (_Ip->GetCurFileName(), NULL, NULL, projectName, NULL); // Add lightmap information in the lightmap log COFile outputLog; if (outputLightmapLog) createLightmapLog (outputLog, gOptions.sExportLighting.c_str(), projectName, ZeNode.GetName()); // Update UV coords to Texture space PutFaceUV1InTextureCoord( LightMap.w, LightMap.h, AllFaces.begin(), AllFaces.size() ); uint32 nLightMapNb = 0; for (j = 0; j < LightMap.nNbLayerUsed; ++j) { CRGBA lmcLayerAmbient= CRGBA::Black; CRGBA lmcLayerDiffuse= CRGBA::White; if (lmcEnabled) { // get the compress color term according to light group if( !vvLights.empty() && !vvLights[j].empty() ) { // Take the first light that is not an ambiant light SLightBuild &rTmpLB = AllLights[vvLights[j][0]]; if(rTmpLB.LightGroupGetName(); string sSaveName = ZeNode.GetName(); char tmp[32]; sSaveName += "_"; sprintf( tmp, "%d", nLightMapNb ); sSaveName += tmp; sSaveName += ".tga"; // Concat name of the project with name of the file sSaveName = (const char*)projectName + std::string ("_") + sSaveName; sSaveName = strlwr (sSaveName); // Remove spaces uint i; for (i=0; isetFileName (sSaveName); sSaveName = gOptions.sExportLighting; if (sSaveName[sSaveName.size()-1] != '\\') sSaveName += "\\"; sSaveName += pLightMap->getFileName(); if (_AbsolutePath) pLightMap->setFileName (sSaveName); // if first time Remove all lightmaps of the same name over all layers if (j == 0) { string sBaseName = sSaveName.substr(0, 1+sSaveName.rfind('_')); for (i = 0; i < 256; ++i) { string sLMName = sBaseName + NLMISC::toString(i) + ".tga"; CIFile ifi; if (ifi.open(sLMName)) { ifi.close (); DeleteFile (sLMName.c_str()); } } } // Convert and write if (lmcEnabled) { // compress to 8 bits LightMap.copyColToBitmap8x2 (pLightMap, j, lmcLayerAmbient, lmcLayerDiffuse); } else // cust copy to 32 bits LightMap.copyColToBitmap32 (pLightMap, j); COFile f( sSaveName ); try { if (lmcEnabled) { // In fact the output is 32 bits because we need the alpha channel // to indicate where the lightmap parts are. pLightMap->loadGrayscaleAsAlpha(false); pLightMap->writeTGA (f, 32); } else { pLightMap->writeTGA (f, 32); } } catch(Exception &e) { if (gOptions.FeedBack != NULL) { char message[512]; sprintf (message, "Can't write the file %s : %s", sSaveName, e.what()); mprintf (message); } } // Add lightmap information in the lightmap log if (outputLightmapLog) appendLightmapLog (outputLog, sSaveName.c_str(), vvLights[j], AllLights); if (lmcEnabled) pLightMap->setUploadFormat (ITexture::Luminance); else pLightMap->setUploadFormat (ITexture::RGB565); pLightMap->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff); pLightMap->setAllowDegradation (false); if (gOptions.bShowLumel) pLightMap->setFilterMode (ITexture::Nearest, ITexture::NearestMipMapOff); // Setup all material of the object for( i = firstMaterial; i < pMBB->Materials.size(); ++i ) if( pMBB->Materials[i].getShader() == CMaterial::LightMap ) { pMBB->Materials[i].setLightMap( nLightMapNb, pLightMap ); // If some light for this layer if( !vvLights.empty() && !vvLights[j].empty() ) { // Set the light factor if it is a 8 bits lightmap if (lmcEnabled) { // Divide by 2 the ambient color in the material. see compute CRGBA mA; mA.modulateFromui(lmcLayerAmbient, 127); pMBB->Materials[i].setLMCColors ( nLightMapNb, mA, lmcLayerDiffuse ); pMBB->Materials[i].setLightMapMulx2 (true); } addLightInfo( pMB, pMBB, AllLights[vvLights[j].operator[](0)].AnimatedLight, AllLights[vvLights[j].operator[](0)].LightGroup, (uint8)i, (uint8)nLightMapNb ); } } ++nLightMapNb; } } // Next mesh } // End of the lighting process for this node we have to export the data CMesh::CMeshBuild *pMB = pZeMeshBuild; CMeshBase::CMeshBaseBuild *pMBB = pZeMeshBaseBuild; pMB->VertexFlags |= CVertexBuffer::TexCoord1Flag; // Build the mesh with the build interface for( i = firstMaterial; i < pMBB->Materials.size(); ++i ) if( pMBB->Materials[i].getShader() == CMaterial::LightMap ) { pMBB->Materials[i].setLighting( false ); pMBB->Materials[i].setColor( CRGBA(255,255,255,255) ); } if (InfoLog) InfoLog->display("CalculateLM : %d ms\n", timeGetTime()-t); return true; }