khanat-opennel-code/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp

2698 lines
86 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include <vector>
#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
ObjectState os = node->EvalWorldState(tvTime);
Object *obj = os.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->DeleteThis();
return true;
}
// -----------------------------------------------------------------------------------------------
void SLightBuild::convertFromMaxLight (INode *node,TimeValue tvTime)
{
// Get a pointer on the object's node
ObjectState os = node->EvalWorldState(tvTime);
Object *obj = os.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<uint8> &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);
}
#else // (MAX_RELEASE < 4000)
ExclList& exclusionList = maxLight->GetExclusionList();
for (sint i = 0; i < exclusionList.Count(); ++i)
{
INode *exclNode = exclusionList[i];
string tmp = exclNode->GetName();
this->setExclusion.insert(tmp);
}
#endif // (MAX_RELEASE < 4000)
// Get Soft Shadow information
string sTmp = CExportNel::getScriptAppData (node, NEL3D_APPDATA_SOFTSHADOW_RADIUS, toString(NEL3D_APPDATA_SOFTSHADOW_RADIUS_DEFAULT));
NLMISC::fromString(sTmp, this->rSoftShadowRadius);
sTmp = CExportNel::getScriptAppData (node, NEL3D_APPDATA_SOFTSHADOW_CONELENGTH, toString(NEL3D_APPDATA_SOFTSHADOW_CONELENGTH_DEFAULT));
NLMISC::fromString(sTmp, this->rSoftShadowConeLength);
if( deleteIt )
maxLight->DeleteThis();
}
// ***********************************************************************************************
// 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<CVector>& 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<CMesh::CFace*> &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<CTextureFile *>(pT);
string namei = "Default";
if( pTF != NULL )
namei = pTF->getFileName();
pT = pMBB->Materials[AllFaces[j]->MaterialId].getTexture(0);
pTF = dynamic_cast<CTextureFile *>(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<sint32> &TextureNames, vector<CMesh::CFace*> &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<CTextureFile *>(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<CTextureFile *>(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<CMesh::CFace*> &ZeFaces )
{
sint32 i;
vector<CMesh::CFace*>::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<sint32> &FaceGroup, vector<CMesh::CFace*>::iterator ItFaces, sint32 nNbFace )
{
int i, j;
sint32 nMatID;
// Bubble sort face
vector<CMesh::CFace*>::iterator ItParseI = ItFaces;
for( i = 0; i < nNbFace-1; ++i )
{
vector<CMesh::CFace*>::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<sint32> cont;
uint8 nNbCont;
bool added;
};
// -----------------------------------------------------------------------------------------------
void SortFaceBySMoothGroup( vector<sint32> &FaceGroup, vector<CMesh::CFace*>::iterator ItFaces, uint32 nNbFace )
{
uint32 i, j, k, nCurSG_NbFace, nCurSG_NbFaceLeft, nGroupNb;
vector<FaceNext> FacesNext;
vector<FaceNext> FacesFinal;
if (gOptions.FeedBack != NULL)
{
string sTmp = "Grouping (1/2)";
gOptions.FeedBack->setLine (4, sTmp);
gOptions.FeedBack->update ();
}
vector<CMesh::CFace*>::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<CMesh::CFace*>::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<CMesh::CFace*> &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<CVector> &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<INode*>& 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; i<node->NumberOfChildren(); i++)
getLightNodeList (vectLightNode, tvTime, ip, visibleOnly, node->GetChildNode(i));
}
// -----------------------------------------------------------------------------------------------
void getLightmapLightBuilds( vector<SLightBuild> &lights, TimeValue tvTime, Interface& ip, bool visibleOnly )
{
vector<INode*> 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<CMesh::CFace*>::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<CMesh::CFace*>::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<CVector> &Vertices,
vector<CMesh::CFace*>::iterator ItFace, sint32 nNbFace )
{
sint32 i, j;
double SpaceSurf = 0.0, TextureSurf = 0.0;
vector<CMesh::CFace*>::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<CMesh::CFace*>::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<sint32> &FaceGroup, vector<CMesh::CFace*>::iterator ItFace, sint32 nNbFace )
{
sint32 j, k, nGroupNb = 0;
FaceGroup.resize( nNbFace );
for( j = 0; j < nNbFace; ++j )
FaceGroup[j] = 1;
vector<CMesh::CFace*>::iterator CurGrpBeg = ItFace;
vector<CMesh::CFace*>::iterator CurGrpEnd = ItFace;
sint32 nGroupOffset = 1;
list<CMesh::CFace*> 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<CMesh::CFace*>::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<CMesh::CFace*>::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<SLMPlane*> &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<CMesh::CFace*>::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<sint32> &vLights, vector<SLightBuild> &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<CVector> &vVertices,
CMatrix& ToWorldMat, vector<sint32> &vLights, vector<SLightBuild> &AllLights,
uint32 nLayerNb, CRTWorld &wrt )
{
// Fill interiors
vector<CMesh::CFace*>::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<SLMPlane*>::iterator ItPlanes, uint32 nNbPlanes,
vector<CVector> &vVertices, CMatrix& ToWorldMat,
vector<sint32> &vLights, vector<SLightBuild> &AllLights,
uint32 nLayerNb, CRTWorld &wrt)
{
// Fill interiors
uint32 nPlanes1;
vector<SLMPlane*>::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<CMesh::CFace*>::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<CMesh::CFace*>::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<SLMPlane*>::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<CMesh::CFace*>::iterator ItFace, sint32 nNbFaces )
{
sint32 i, j;
vector<CMesh::CFace*>::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<SLightBuild> &AllLights, vector< vector<sint32> >&vvLights )
{
uint32 nNbGroup = 0;
vector<sint32> 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<SLightBuild> &vLights, CMesh::CMeshBuild* pMB, CMeshBase::CMeshBaseBuild *pMBB, INode &node )
{
uint32 i;
// temp result
vector<SLightBuild> 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; i<count; i++)
{
CMeshBase::CLightMapInfoList &info = pMBB->LightInfoMap[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";
if (CFile::fileExists(sSaveName))
{
if (!CFile::deleteFile(sSaveName))
{
nlwarning("Failed to delete file %s.", sSaveName.c_str());
}
}
}
}
// In : set of faces
// Out : planes
/*
void computeSetOfFaces (vector<SLMPlane*>&planes, )
*/
// Object that touch the reference object
struct SSurObj
{
CMesh::CMeshBuild *pMB;
CMeshBase::CMeshBaseBuild *pMBB;
vector<CMesh::CFace*> faces;
};
// Get the objects next to one
/*
void sans_majuscule_au_debut_LinkToObjectAround (CMesh::CMeshBuild *pMB, CMeshBase::CMeshBaseBuild *pMBB,
vector<CVector>& vertices,
SWorldRT& wrt)
{
CAABBox ref = getMeshBBox (*pMB, *pMBB, true);
vector<SSurObj> 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<sint32> 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<sint32> lightIndexes, const vector<SLightBuild> &lights)
{
try
{
string text;
text = lightmapName;
text += " :\n";
// Dump light name used on this lightmap
uint i;
for (i=0; i<lightIndexes.size(); i++)
{
text += "\t";
text += lights[lightIndexes[i]].Name + toString (" (group %d, animation \"%s\")", lights[lightIndexes[i]].LightGroup, lights[lightIndexes[i]].AnimatedLight.c_str());
text += "\n";
}
outputLog.serialBuffer ((uint8*)text.c_str(), text.size());
}
catch (exception &e)
{
nlwarning ("Error writing the file : %s", e.what());
}
}
// -----------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------
// Caclulate the lightmap of the specified mesh build
// absolutePath tell this code to put the name of the lightmap in absolute or relative path
// this is very usefull for viewer inside MAX
bool CExportNel::calculateLM( CMesh::CMeshBuild *pZeMeshBuild, CMeshBase::CMeshBaseBuild *pZeMeshBaseBuild, INode& ZeNode,
TimeValue tvTime, uint firstMaterial, bool outputLightmapLog)
{
DWORD t = timeGetTime();
uint32 i, j;
// **** Retrieve Shape Node properties
string sLumelSizeMul = CExportNel::getScriptAppData (&ZeNode, NEL3D_APPDATA_LUMELSIZEMUL, "1.0");
float rLumelSizeMul;
NLMISC::fromString(sLumelSizeMul, rLumelSizeMul);
// 8Bits LightMap Compression
bool lmcEnabled= CExportNel::getScriptAppData (&ZeNode, NEL3D_APPDATA_EXPORT_LMC_ENABLED, BST_UNCHECKED)==BST_CHECKED;
enum {NumLightGroup= 3};
CRGBA lmcAmbient[NumLightGroup];
CRGBA lmcDiffuse[NumLightGroup];
for(i=0;i<NumLightGroup;i++)
{
lmcAmbient[i]= CExportNel::getScriptAppData (&ZeNode, NEL3D_APPDATA_EXPORT_LMC_AMBIENT_START+i, CRGBA::Black);
lmcDiffuse[i]= CExportNel::getScriptAppData (&ZeNode, NEL3D_APPDATA_EXPORT_LMC_DIFFUSE_START+i, CRGBA::White);
}
// **** Read options
gOptions = _Options;
if (rLumelSizeMul > 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<SLightBuild> 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<INode*> lodListToExclude;
addChildLodNode (lodListToExclude);
addParentLodNode (ZeNode, lodListToExclude);
// Remove from exclude the node
lodListToExclude.erase (&ZeNode);
// List to include
std::set<INode*> 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<CMesh::CFace*> AllFaces;
CMatrix MBMatrix = getObjectToWorldMatrix (pMB, pMBB);
MBMatrix.movePos (-vGlobalPos);
vector<CVector> AllVertices; // All vertices in world space
vector<sint32> FaceGroupByMat; // Number of faces with the same properties
uint32 nNbFace = pMB->Faces.size(), nNbVertex = pMB->Vertices.size();
sint32 offsetMat, offsetSmooth, offsetPlane;
vector<SLMPlane*> AllPlanes;
sint32 AllPlanesPrevSize;
vector< vector<sint32> > 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<sint32> 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<sint32> 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<SLMPlane*> 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];
TempPlanes[nPlaneNb] = NULL;
}
}
// 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<CRGBAF> 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];
AllPlanes[i] = NULL;
}
// 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.LightGroup<NumLightGroup)
{
lmcLayerAmbient= lmcAmbient[rTmpLB.LightGroup];
lmcLayerDiffuse= lmcDiffuse[rTmpLB.LightGroup];
}
}
}
// if lightmap compression enabled, must not skip this layer if the ambient term is not black!
if (!LightMap.isAllBlack((uint8)j) || lmcLayerAmbient!=CRGBA::Black)
{
CTextureFile *pLightMap = new CTextureFile();
//string sSaveName = AllMeshBuilds[nNode].second->GetName();
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; i<sSaveName.length(); i++)
{
if (sSaveName[i] == ' ')
sSaveName[i] = '_';
}
pLightMap->setFileName (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";
if (CFile::fileExists(sLMName))
{
nlinfo("DELETE %s", sLMName.c_str());
if (!CFile::deleteFile(sLMName))
{
nlwarning("Failed to delete file %s", 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
{
nlinfo("SAVE %s", sSaveName.c_str());
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;
}