3824 lines
125 KiB
C++
3824 lines
125 KiB
C++
// --------------------
|
|
// LIGHTMAP CALCULATION
|
|
// --------------------
|
|
// OLD VERSION
|
|
// This version handled a group of object at one time to optimize the precalculation
|
|
// of the accelerated world raytrace structure
|
|
|
|
|
|
#include "nel/misc/time_nl.h"
|
|
#include "nel/misc/file.h"
|
|
#include "nel/misc/triangle.h"
|
|
#include "nel/misc/bsphere.h"
|
|
#include "3d/quad_tree.h"
|
|
#include "3d/skeleton_shape.h"
|
|
#include "3d/texture_file.h"
|
|
#include "3d/light.h"
|
|
|
|
#include "nel_export_scene.h"
|
|
|
|
#include "3d/bsp_tree.h"
|
|
#include "3d/quad_grid.h"
|
|
// TOOLS
|
|
// *****
|
|
|
|
#define NEL_LIGHT_CLASS_ID_A 0x36e3181f
|
|
#define NEL_LIGHT_CLASS_ID_B 0x3ac24049
|
|
|
|
#define MAXLIGHTMAPSIZE 1024
|
|
//#define RATIOLIGHTMAP 0.25 // 0.25 meters in real world represent 1 Lumel in UV world
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
struct SLightBuild
|
|
{
|
|
string GroupName;
|
|
enum EType { LightAmbient, LightPoint, LightDir, LightSpot };
|
|
EType Type;
|
|
CVector Position; // Used by LightPoint and LightSpot
|
|
CVector Direction; // Used by LightSpot and LightDir
|
|
float rRadiusMin, rRadiusMax; // Used by LightPoint and LightSpot
|
|
float rHotspot, rFallof; // Used by LightSpot
|
|
CRGBA Ambient;
|
|
CRGBA Diffuse;
|
|
CRGBA Specular;
|
|
bool bAnimate;
|
|
bool bCastShadow;
|
|
float rMult;
|
|
// ------------------------------------
|
|
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);
|
|
bAnimate = false; // TEMP MAT
|
|
rMult = 1.0f;
|
|
}
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
struct SLMPixel
|
|
{
|
|
CRGBAF p[8]; // 8 Layers of lightmap possible
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
struct SLMPlane
|
|
{
|
|
sint32 x, y; // Pos in lightmap
|
|
sint32 w, h; // Size
|
|
vector<uint8> msk; // 0 - No pixel
|
|
// 1 - Pixel must be calculated
|
|
// 2 - Pixel is interior and is calculated
|
|
// 3 - Pixel is exterior in this plane but interior in another of the same smooth group
|
|
// 4 - Pixel is exterior and is extrapolated
|
|
vector<SLMPixel> col; // 32 bits value for each pixel of each layer
|
|
sint32 nNbLayerUsed;
|
|
vector<CMesh::CFace*> faces;
|
|
// ------------------------------------
|
|
SLMPlane()
|
|
{
|
|
nNbLayerUsed = 0;
|
|
x = y = 0; w = h = 1; msk.resize(1); msk[0] = 0;
|
|
col.resize(1);
|
|
for( sint32 i = 0; i < 8; ++i )
|
|
{ col[0].p[i].R = col[0].p[i].G = col[0].p[i].B = col[0].p[i].A = 0.0f; }
|
|
}
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
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;
|
|
// Initial u,v
|
|
double InitU, InitV;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
struct SCubeGridCell
|
|
{
|
|
CMesh::CFace* pF;
|
|
CMesh::CMeshBuild* pMB;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Represent a cube made of grids centered on (0,0,0) with a size of 1
|
|
struct SCubeGrid
|
|
{
|
|
enum gridPos { kUp = 0, kDown, kLeft, kRight, kFront, kBack };
|
|
CQuadGrid<SCubeGridCell> grids[6];
|
|
|
|
sint32 nSelGrid;
|
|
CQuadGrid<SCubeGridCell>::CIterator itSel;
|
|
|
|
// =======================================================================
|
|
SCubeGrid()
|
|
{
|
|
CMatrix tmp;
|
|
CVector I, J, K;
|
|
|
|
// grids[kUp].changeBase( );
|
|
I = CVector( 1, 0, 0 );
|
|
J = CVector( 0, -1, 0 );
|
|
K = CVector( 0, 0, -1 );
|
|
tmp.identity(); tmp.setRot( I, J, K, true );
|
|
grids[kDown].changeBase( tmp );
|
|
|
|
I = CVector( 0, 0, 1 );
|
|
J = CVector( 0, 1, 0 );
|
|
K = CVector( -1, 0, 0 );
|
|
tmp.identity(); tmp.setRot( I, J, K, true);
|
|
grids[kLeft].changeBase( tmp );
|
|
|
|
I = CVector( 0, 0, -1 );
|
|
J = CVector( 0, 1, 0 );
|
|
K = CVector( 1, 0, 0 );
|
|
tmp.identity(); tmp.setRot( I, J, K, true);
|
|
grids[kRight].changeBase( tmp );
|
|
|
|
I = CVector( 1, 0, 0 );
|
|
J = CVector( 0, 0, 1 );
|
|
K = CVector( 0, -1, 0 );
|
|
tmp.identity(); tmp.setRot( I, J, K, true);
|
|
grids[kFront].changeBase( tmp );
|
|
|
|
I = CVector( 1, 0, 0 );
|
|
J = CVector( 0, 0, -1 );
|
|
K = CVector( 0, 1, 0 );
|
|
tmp.identity(); tmp.setRot( I, J, K, true);
|
|
grids[kBack].changeBase( tmp );
|
|
}
|
|
|
|
// =======================================================================
|
|
void create( int nSize )
|
|
{
|
|
grids[kUp].create ( nSize, 1.0f / ((float)nSize) );
|
|
grids[kDown].create ( nSize, 1.0f / ((float)nSize) );
|
|
grids[kLeft].create ( nSize, 1.0f / ((float)nSize) );
|
|
grids[kRight].create( nSize, 1.0f / ((float)nSize) );
|
|
grids[kFront].create( nSize, 1.0f / ((float)nSize) );
|
|
grids[kBack].create ( nSize, 1.0f / ((float)nSize) );
|
|
}
|
|
|
|
// =======================================================================
|
|
void project( CTriangle &tri, CPlane pyr[4], CPlane &gridPlane,
|
|
sint32 nGridNb, SCubeGridCell &cell )
|
|
{
|
|
CVector vIn[7], vOut[7];
|
|
sint32 i, nOut;
|
|
vIn[0] = tri.V0; vIn[1] = tri.V1; vIn[2] = tri.V2;
|
|
nOut = pyr[0].clipPolygonFront( vIn, vOut, 3 );
|
|
if( nOut == 0 ) return;
|
|
for( i = 0; i < nOut; ++i ) vIn[i] = vOut[i];
|
|
nOut = pyr[1].clipPolygonFront( vIn, vOut, nOut );
|
|
if( nOut == 0 ) return;
|
|
for( i = 0; i < nOut; ++i ) vIn[i] = vOut[i];
|
|
nOut = pyr[2].clipPolygonFront( vIn, vOut, nOut );
|
|
if( nOut == 0 ) return;
|
|
for( i = 0; i < nOut; ++i ) vIn[i] = vOut[i];
|
|
nOut = pyr[3].clipPolygonFront( vIn, vOut, nOut );
|
|
if( nOut >= 3 )
|
|
{
|
|
CVector vMin(1,1,1), vMax(-1,-1,-1);
|
|
for( i = 0; i < nOut; ++i )
|
|
{
|
|
vOut[i] = gridPlane.intersect( CVector(0,0,0), vOut[i] );
|
|
if( vMin.x > vOut[i].x ) vMin.x = vOut[i].x;
|
|
if( vMin.y > vOut[i].y ) vMin.y = vOut[i].y;
|
|
if( vMin.z > vOut[i].z ) vMin.z = vOut[i].z;
|
|
if( vMax.x < vOut[i].x ) vMax.x = vOut[i].x;
|
|
if( vMax.y < vOut[i].y ) vMax.y = vOut[i].y;
|
|
if( vMax.z < vOut[i].z ) vMax.z = vOut[i].z;
|
|
}
|
|
// Create the bbox
|
|
grids[nGridNb].insert( vMin, vMax, cell );
|
|
}
|
|
}
|
|
|
|
// =======================================================================
|
|
void insert( CTriangle &tri, SCubeGridCell &cell )
|
|
{
|
|
CPlane p[4], gp;
|
|
// Construct clip pyramid for grid : UP
|
|
p[0].make( CVector(0,0,0), CVector( -1,-1,+1 ), CVector( +1,-1,+1 ) );
|
|
p[1].make( CVector(0,0,0), CVector( +1,-1,+1 ), CVector( +1,+1,+1 ) );
|
|
p[2].make( CVector(0,0,0), CVector( +1,+1,+1 ), CVector( -1,+1,+1 ) );
|
|
p[3].make( CVector(0,0,0), CVector( -1,+1,+1 ), CVector( -1,-1,+1 ) );
|
|
gp.make( CVector(0,0,1), CVector(0,0,0.5) );
|
|
project( tri, p, gp, kUp, cell );
|
|
// Construct clip pyramid for grid : DOWN
|
|
p[0].make( CVector(0,0,0), CVector( +1,-1,-1 ), CVector( -1,-1,-1 ) );
|
|
p[1].make( CVector(0,0,0), CVector( -1,-1,-1 ), CVector( -1,+1,-1 ) );
|
|
p[2].make( CVector(0,0,0), CVector( -1,+1,-1 ), CVector( +1,+1,-1 ) );
|
|
p[3].make( CVector(0,0,0), CVector( +1,+1,-1 ), CVector( +1,-1,-1 ) );
|
|
gp.make( CVector(0,0,-1), CVector(0,0,-0.5) );
|
|
project( tri, p, gp, kDown, cell );
|
|
// Construct clip pyramid for grid : LEFT
|
|
p[0].make( CVector(0,0,0), CVector( -1,-1,-1 ), CVector( -1,-1,+1 ) );
|
|
p[1].make( CVector(0,0,0), CVector( -1,-1,+1 ), CVector( -1,+1,+1 ) );
|
|
p[2].make( CVector(0,0,0), CVector( -1,+1,+1 ), CVector( -1,+1,-1 ) );
|
|
p[3].make( CVector(0,0,0), CVector( -1,+1,-1 ), CVector( -1,-1,-1 ) );
|
|
gp.make( CVector(-1,0,0), CVector(-0.5,0,0) );
|
|
project( tri, p, gp, kLeft, cell );
|
|
// Construct clip pyramid for grid : RIGHT
|
|
p[0].make( CVector(0,0,0), CVector( +1,-1,+1 ), CVector( +1,-1,-1 ) );
|
|
p[1].make( CVector(0,0,0), CVector( +1,-1,-1 ), CVector( +1,+1,-1 ) );
|
|
p[2].make( CVector(0,0,0), CVector( +1,+1,-1 ), CVector( +1,+1,+1 ) );
|
|
p[3].make( CVector(0,0,0), CVector( +1,+1,+1 ), CVector( +1,-1,+1 ) );
|
|
gp.make( CVector(1,0,0), CVector(0.5,0,0) );
|
|
project( tri, p, gp, kRight, cell );
|
|
// Construct clip pyramid for grid : FRONT
|
|
p[0].make( CVector(0,0,0), CVector( -1,-1,-1 ), CVector( +1,-1,-1 ) );
|
|
p[1].make( CVector(0,0,0), CVector( +1,-1,-1 ), CVector( +1,-1,+1 ) );
|
|
p[2].make( CVector(0,0,0), CVector( +1,-1,+1 ), CVector( -1,-1,+1 ) );
|
|
p[3].make( CVector(0,0,0), CVector( -1,-1,+1 ), CVector( -1,-1,-1 ) );
|
|
gp.make( CVector(0,-1,0), CVector(0,-0.5,0) );
|
|
project( tri, p, gp, kFront, cell );
|
|
// Construct clip pyramid for grid : BACK
|
|
p[0].make( CVector(0,0,0), CVector( +1,+1,+1 ), CVector( +1,+1,-1 ) );
|
|
p[1].make( CVector(0,0,0), CVector( +1,+1,-1 ), CVector( -1,+1,-1 ) );
|
|
p[2].make( CVector(0,0,0), CVector( -1,+1,-1 ), CVector( -1,+1,+1 ) );
|
|
p[3].make( CVector(0,0,0), CVector( -1,+1,+1 ), CVector( +1,+1,+1 ) );
|
|
gp.make( CVector(0,1,0), CVector(0,0.5,0) );
|
|
project( tri, p, gp, kBack, cell );
|
|
}
|
|
|
|
// =======================================================================
|
|
void select( CVector &v )
|
|
{
|
|
CPlane gp;
|
|
// Get the plane
|
|
if( ( -v.z <= v.x ) && ( v.x <= v.z ) &&
|
|
( -v.z <= v.y ) && ( v.y <= v.z ) &&
|
|
( 0.0f <= v.z ) )
|
|
{
|
|
nSelGrid = kUp;
|
|
gp.make( CVector(0,0,1), CVector(0,0,0.5) );
|
|
}
|
|
if( ( v.z <= v.x ) && ( v.x <= -v.z ) &&
|
|
( v.z <= v.y ) && ( v.y <= -v.z ) &&
|
|
( v.z <= 0.0f ) )
|
|
{
|
|
nSelGrid = kDown;
|
|
gp.make( CVector(0,0,-1), CVector(0,0,-0.5) );
|
|
}
|
|
if( ( v.x <= 0.0f ) &&
|
|
( v.x <= v.y ) && ( v.y <= -v.x ) &&
|
|
( v.x <= v.z ) && ( v.z <= -v.x ) )
|
|
{
|
|
nSelGrid = kLeft;
|
|
gp.make( CVector(-1,0,0), CVector(-0.5,0,0) );
|
|
}
|
|
if( ( 0.0f <= v.x ) &&
|
|
( -v.x <= v.y ) && ( v.y <= v.x ) &&
|
|
( -v.x <= v.z ) && ( v.z <= v.x ) )
|
|
{
|
|
nSelGrid = kRight;
|
|
gp.make( CVector(1,0,0), CVector(0.5,0,0) );
|
|
}
|
|
if( ( v.y <= v.x ) && ( v.x <= -v.y ) &&
|
|
( v.y <= 0.0f ) &&
|
|
( v.y <= v.z ) && ( v.z <= -v.y ) )
|
|
{
|
|
nSelGrid = kFront;
|
|
gp.make( CVector(0,-1,0), CVector(0,-0.5,0) );
|
|
}
|
|
if( ( -v.y <= v.x ) && ( v.x <= v.y ) &&
|
|
( 0.0f <= v.y ) &&
|
|
( -v.y <= v.z ) && ( v.z <= v.y ) )
|
|
{
|
|
nSelGrid = kBack;
|
|
gp.make( CVector(0,1,0), CVector(0,0.5,0) );
|
|
}
|
|
nlassert(nSelGrid!=-1);
|
|
CVector newV = gp.intersect( CVector(0,0,0), v );
|
|
grids[nSelGrid].select(newV, newV);
|
|
itSel = grids[nSelGrid].begin();
|
|
}
|
|
|
|
SCubeGridCell getSel()
|
|
{
|
|
return *itSel;
|
|
}
|
|
|
|
void nextSel()
|
|
{
|
|
++itSel;
|
|
}
|
|
|
|
bool isEndSel()
|
|
{
|
|
return (itSel == grids[nSelGrid].end());
|
|
}
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
struct SWorldRT
|
|
{
|
|
vector<CMesh::CMeshBuild *> vMB;
|
|
vector<SCubeGrid> cgAccel; // One cube grid by light
|
|
|
|
// vector<CAABBox> bbBoxes;
|
|
// vector<sint32> MeshSel;
|
|
// vector<CQuadTree<SWorldRTCell> > qtAccel;
|
|
// vector<CBSPTree<SWorldRTCell> > btAccel;
|
|
};
|
|
|
|
TTicks timerCalcRT = 0;
|
|
TTicks timerExportLighting = 0;
|
|
TTicks timerInit = 0;
|
|
TTicks timerCalc = 0;
|
|
TTicks timerPlac = 0;
|
|
TTicks timerSave = 0;
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void SortFaceByTextureName(vector<CMesh::CFace*> &AllFaces, CMesh::CMeshBuild *pMB)
|
|
{
|
|
int i, j;
|
|
int nNbFace = AllFaces.size();
|
|
|
|
for( i = 0; i < nNbFace-1; ++i )
|
|
for( j = i+1; j < nNbFace; ++j )
|
|
{
|
|
ITexture *pT = pMB->Materials[AllFaces[i]->MaterialId].getTexture(0);
|
|
CTextureFile *pTF = dynamic_cast<CTextureFile *>(pT);
|
|
string namei = "Default";
|
|
if( pTF != NULL )
|
|
namei = pTF->getFileName();
|
|
pT = pMB->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)
|
|
{
|
|
int i, nNbFace = AllFaces.size();
|
|
TextureNames.resize(nNbFace);
|
|
ITexture *pT = pMB->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 = pMB->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 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 )
|
|
{
|
|
sint32 i, j;
|
|
sint32 F1c[2] = { -1, -1 };
|
|
sint32 F2c[2] = { -1, -1 };
|
|
|
|
// Is there a vertices continuity
|
|
for( j = 0; j < 3; ++j )
|
|
for( i = 0; i < 3; ++i )
|
|
{
|
|
if( (pF1->Corner[j].Vertex == pF2->Corner[i].Vertex) &&
|
|
(pF1->Corner[(j+1)%3].Vertex == pF2->Corner[(i+1)%3].Vertex) )
|
|
{
|
|
F1c[0] = j; F1c[1] = (j+1)%3;
|
|
F2c[0] = i; F2c[1] = (i+1)%3;
|
|
}
|
|
if( (pF1->Corner[j].Vertex == pF2->Corner[(i+1)%3].Vertex) &&
|
|
(pF1->Corner[(j+1)%3].Vertex == pF2->Corner[i].Vertex) )
|
|
{
|
|
F1c[0] = (j+1)%3; F1c[1] = j;
|
|
F2c[0] = i; F2c[1] = (i+1)%3;
|
|
}
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void SortFaceBySMoothGroup( vector<sint32> &FaceGroup, vector<CMesh::CFace*>::iterator ItFaces, sint32 nNbFace )
|
|
{
|
|
sint32 j, k, nGroupNb = 0, nGroupOffset = 1;
|
|
bool bFaceAdded;
|
|
|
|
// Bubble sort face
|
|
FaceGroup.resize(nNbFace);
|
|
for( j = 0; j < nNbFace; ++j )
|
|
FaceGroup[j] = 1;
|
|
vector<CMesh::CFace*>::iterator CurGrpBeg = ItFaces;
|
|
vector<CMesh::CFace*>::iterator CurGrpEnd = ItFaces;
|
|
|
|
for( nGroupOffset = 1; nGroupOffset <= nNbFace; )
|
|
{
|
|
do
|
|
{
|
|
bFaceAdded = false;
|
|
vector<CMesh::CFace*>::iterator ItParseJ = CurGrpEnd;
|
|
++ItParseJ;
|
|
for( j = nGroupOffset; j < nNbFace; ++j )
|
|
{
|
|
// Is the face is connected to one of the current group
|
|
vector<CMesh::CFace*>::iterator ItParseK = CurGrpBeg;
|
|
for( k = 0; k < FaceGroup[nGroupNb]; ++k )
|
|
{
|
|
if( FaceContinuous( *ItParseK, *ItParseJ, false ) )
|
|
{
|
|
// Yes the face must be added at the end of the group
|
|
++CurGrpEnd;
|
|
CMesh::CFace *pFaceTemp = *CurGrpEnd;
|
|
*CurGrpEnd = *ItParseJ;
|
|
*ItParseJ = pFaceTemp;
|
|
nGroupOffset += 1;
|
|
FaceGroup[nGroupNb] += 1;
|
|
bFaceAdded = true;
|
|
break;
|
|
}
|
|
++ItParseK;
|
|
}
|
|
++ItParseJ;
|
|
}
|
|
} while( bFaceAdded ); // In this pass have we added faces ?
|
|
// No -> Next smooth group
|
|
++CurGrpEnd;
|
|
CurGrpBeg = CurGrpEnd;
|
|
++nGroupNb;
|
|
nGroupOffset += 1;
|
|
}
|
|
FaceGroup.resize(nGroupNb);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
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 intersectionTriangleSphere( CTriangle &t, CBSphere &s )
|
|
{
|
|
// if a vertex of the triangle is in the sphere
|
|
CVector v = t.V0 - s.Center;
|
|
float f = v.norm();
|
|
if( f < s.Radius )
|
|
return true;
|
|
v = t.V1 - s.Center;
|
|
f = v.norm();
|
|
if( f < s.Radius )
|
|
return true;
|
|
v = t.V2 - s.Center;
|
|
f = v.norm();
|
|
if( f < s.Radius )
|
|
return true;
|
|
// Ok sonow project the center of the triangle on the plane
|
|
CPlane p;
|
|
p.make( t.V0, t.V1, t.V2 );
|
|
p.normalize();
|
|
|
|
CVector newCenter = p.project( s.Center );
|
|
v = newCenter - s.Center;
|
|
float newRadius = v.norm() / s.Radius;
|
|
if( newRadius > 1.0 )
|
|
newRadius = 1.0;
|
|
newRadius = cosf( newRadius * PI / 2.0f );
|
|
|
|
CVector n = p.getNormal();
|
|
CPlane p2;
|
|
p2.make( t.V0, t.V1, t.V0 + n ); p2.normalize();
|
|
f = p2*newCenter;
|
|
p2.make( t.V1, t.V2, t.V1 + n ); p2.normalize();
|
|
float f2 = p2*newCenter;
|
|
p2.make( t.V2, t.V0, t.V2 + n ); p2.normalize();
|
|
float f3 = p2*newCenter;
|
|
|
|
// Is the newcenter insied the triangle ?
|
|
if( ( f <= 0.0 ) && ( f2 <= 0.0 ) && ( f3 <= 0.0 ) )
|
|
return true;
|
|
if( ( f >= 0.0 ) && ( f2 >= 0.0 ) && ( f3 >= 0.0 ) )
|
|
return true;
|
|
|
|
// Is the newCenter at a distance < newradius from one of the triangle edge ?
|
|
if( ( fabs(f) < newRadius ) || ( fabs(f2) < newRadius ) || ( fabs(f3) < newRadius ) )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void CreatePiece( vector<uint8>& Piece, sint32& nSizeX, sint32& nSizeY, sint32 &nPosX, sint32 &nPosY,
|
|
float lumx1, float lumy1,
|
|
float lumx2, float lumy2,
|
|
float lumx3, float lumy3, uint8 nCol )
|
|
{
|
|
double minx, miny;
|
|
double maxx, maxy;
|
|
int j,k;
|
|
|
|
if( nCol == 0 )
|
|
nCol = 1;
|
|
minx = lumx1;
|
|
if( minx > lumx2 ) minx = lumx2;
|
|
if( minx > lumx3 ) minx = lumx3;
|
|
maxx = lumx1;
|
|
if( maxx < lumx2 ) maxx = lumx2;
|
|
if( maxx < lumx3 ) maxx = lumx3;
|
|
miny = lumy1;
|
|
if( miny > lumy2 ) miny = lumy2;
|
|
if( miny > lumy3 ) miny = lumy3;
|
|
maxy = lumy1;
|
|
if( maxy < lumy2 ) maxy = lumy2;
|
|
if( maxy < lumy3 ) maxy = lumy3;
|
|
|
|
// Put the piece in the new basis (nPosX,nPosY)
|
|
nPosX = ((sint32)floor(minx-0.5));
|
|
nPosY = ((sint32)floor(miny-0.5));
|
|
|
|
lumx1 -= nPosX; lumy1 -= nPosY;
|
|
lumx2 -= nPosX; lumy2 -= nPosY;
|
|
lumx3 -= nPosX; lumy3 -= nPosY;
|
|
|
|
nSizeX = 1 + ((sint32)floor(maxx+0.5)) - ((sint32)floor(minx-0.5));
|
|
nSizeY = 1 + ((sint32)floor(maxy+0.5)) - ((sint32)floor(miny-0.5));
|
|
Piece.resize( nSizeX*nSizeY );
|
|
for( j = 0; j < nSizeX*nSizeY; ++j )
|
|
Piece[j] = 0;
|
|
|
|
// The square interact with the triangle if an edge of the square is cut by an edge of the triangle
|
|
// Or the square is in the triangle
|
|
|
|
for( j = 0; j < nSizeY-1; ++j )
|
|
for( k = 0; k < nSizeX-1; ++k )
|
|
{
|
|
// Is the square (j,k) is interacting with the triangle
|
|
// This means : The square contains a point of the triangle (can be done for the 3 points)
|
|
// The triangle contains a point of the square
|
|
// If so then we have to turn on all the 4 pixels of the square
|
|
if( isInTriangleOrEdge(k+0.5,j+0.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) ||
|
|
isInTriangleOrEdge(k+1.5,j+0.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) ||
|
|
isInTriangleOrEdge(k+0.5,j+1.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) ||
|
|
isInTriangleOrEdge(k+1.5,j+1.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) )
|
|
{
|
|
Piece[k + j *nSizeX] = nCol;
|
|
Piece[1+k + j *nSizeX] = nCol;
|
|
Piece[k + (1+j)*nSizeX] = nCol;
|
|
Piece[1+k + (1+j)*nSizeX] = nCol;
|
|
}
|
|
|
|
if( segmentIntersection(k+0.5, j+0.5, k+1.5, j+0.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+1.5, j+0.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+1.5, j+0.5, lumx3, lumy3, lumx1, lumy1) ||
|
|
|
|
segmentIntersection(k+0.5, j+0.5, k+0.5, j+1.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+0.5, j+1.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+0.5, j+1.5, lumx3, lumy3, lumx1, lumy1) ||
|
|
|
|
segmentIntersection(k+1.5, j+1.5, k+1.5, j+0.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+1.5, j+0.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+1.5, j+0.5, lumx3, lumy3, lumx1, lumy1) ||
|
|
|
|
segmentIntersection(k+1.5, j+1.5, k+0.5, j+1.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+0.5, j+1.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+0.5, j+1.5, lumx3, lumy3, lumx1, lumy1) )
|
|
{
|
|
Piece[k + j *nSizeX] = nCol;
|
|
Piece[1+k + j *nSizeX] = nCol;
|
|
Piece[k + (1+j)*nSizeX] = nCol;
|
|
Piece[1+k + (1+j)*nSizeX] = nCol;
|
|
}
|
|
|
|
}
|
|
// For all the points of the triangle update the square
|
|
Piece[((sint32)(lumx1-0.5)) + ((sint32)(lumy1-0.5))*nSizeX] = nCol;
|
|
Piece[1+((sint32)(lumx1-0.5)) + ((sint32)(lumy1-0.5))*nSizeX] = nCol;
|
|
Piece[((sint32)(lumx1-0.5)) + (1+((sint32)(lumy1-0.5)))*nSizeX] = nCol;
|
|
Piece[1+((sint32)(lumx1-0.5)) + (1+((sint32)(lumy1-0.5)))*nSizeX] = nCol;
|
|
|
|
Piece[((sint32)(lumx2-0.5)) + ((sint32)(lumy2-0.5))*nSizeX] = nCol;
|
|
Piece[1+((sint32)(lumx2-0.5)) + ((sint32)(lumy2-0.5))*nSizeX] = nCol;
|
|
Piece[((sint32)(lumx2-0.5)) + (1+((sint32)(lumy2-0.5)))*nSizeX] = nCol;
|
|
Piece[1+((sint32)(lumx2-0.5)) + (1+((sint32)(lumy2-0.5)))*nSizeX] = nCol;
|
|
|
|
Piece[((sint32)(lumx3-0.5)) + ((sint32)(lumy3-0.5))*nSizeX] = nCol;
|
|
Piece[1+((sint32)(lumx3-0.5)) + ((sint32)(lumy3-0.5))*nSizeX] = nCol;
|
|
Piece[((sint32)(lumx3-0.5)) + (1+((sint32)(lumy3-0.5)))*nSizeX] = nCol;
|
|
Piece[1+((sint32)(lumx3-0.5)) + (1+((sint32)(lumy3-0.5)))*nSizeX] = nCol;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void ResizeBitmap( vector<uint8> &vBitmap, sint32 &nSizeX, sint32 &nSizeY, sint32 nNewSizeX, sint32 nNewSizeY )
|
|
{
|
|
vector<uint8> vImgTemp;
|
|
int i, j;
|
|
|
|
vImgTemp.resize(nNewSizeX*nNewSizeY);
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
vImgTemp[i] = 0;
|
|
|
|
for( j = 0; j < min(nSizeY,nNewSizeY); ++j )
|
|
for( i = 0; i < min(nSizeX,nNewSizeX); ++i )
|
|
{
|
|
vImgTemp[i+j*nNewSizeX] = vBitmap[i+j*nSizeX];
|
|
}
|
|
|
|
vBitmap.resize(nNewSizeX*nNewSizeY);
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
vBitmap[i] = vImgTemp[i];
|
|
|
|
nSizeX = nNewSizeX;
|
|
nSizeY = nNewSizeY;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Same as ResizeBitmap but for 32 bits image
|
|
void ResizeBitmap32( CBitmap *pImage, sint32 nNewSizeX, sint32 nNewSizeY )
|
|
{
|
|
vector<uint8> vImgTemp;
|
|
int i, j;
|
|
|
|
vImgTemp.resize( 4*nNewSizeX*nNewSizeY );
|
|
for( i = 0; i < 4*nNewSizeX*nNewSizeY; ++i )
|
|
vImgTemp[i] = 0;
|
|
|
|
CObjectVector<uint8> &vBitmap = pImage->getPixels();
|
|
sint32 nCurSizeX = pImage->getWidth();
|
|
sint32 nCurSizeY = pImage->getHeight();
|
|
for( j = 0; j < min(nCurSizeY,nNewSizeY); ++j )
|
|
for( i = 0; i < min(nCurSizeX,nNewSizeX); ++i )
|
|
{
|
|
vImgTemp[4*(i+j*nNewSizeX)+0] = vBitmap[4*(i+j*pImage->getWidth())+0];
|
|
vImgTemp[4*(i+j*nNewSizeX)+1] = vBitmap[4*(i+j*pImage->getWidth())+1];
|
|
vImgTemp[4*(i+j*nNewSizeX)+2] = vBitmap[4*(i+j*pImage->getWidth())+2];
|
|
vImgTemp[4*(i+j*nNewSizeX)+3] = vBitmap[4*(i+j*pImage->getWidth())+3];
|
|
}
|
|
|
|
pImage->resize(nNewSizeX,nNewSizeY);
|
|
vBitmap = pImage->getPixels();
|
|
for( i = 0; i < 4*nNewSizeX*nNewSizeY; ++i )
|
|
vBitmap[i] = vImgTemp[i];
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
bool PutPieceInLightMap( vector<uint8>& Piece, sint32 nPieceSizeX, sint32 nPieceSizeY,
|
|
vector<uint8>& LightMap, sint32 nLightMapSizeX, sint32 nLightMapSizeY,
|
|
sint32 &nNewPosX, sint32 &nNewPosY )
|
|
{
|
|
sint32 i, j, a, b;
|
|
bool bGoodPosition;
|
|
|
|
if( nPieceSizeX > nLightMapSizeX ) return false;
|
|
if( nPieceSizeY > nLightMapSizeY ) return false;
|
|
|
|
// For all position test if the piece can be put in
|
|
for( j = 0; j < (nLightMapSizeY-nPieceSizeY); ++j )
|
|
for( i = 0; i < (nLightMapSizeX-nPieceSizeX); ++i )
|
|
{
|
|
bGoodPosition = true;
|
|
for( b = 0; b < nPieceSizeY; ++b )
|
|
{
|
|
for( a = 0; a < nPieceSizeX; ++a )
|
|
{
|
|
if( Piece[a+b*nPieceSizeX] != 0 )
|
|
if( LightMap[(i+a)+(j+b)*nLightMapSizeX] != 0 )
|
|
{
|
|
bGoodPosition = false;
|
|
break;
|
|
}
|
|
}
|
|
if( bGoodPosition == false )
|
|
break;
|
|
}
|
|
if( bGoodPosition )
|
|
{
|
|
// Write the piece in the lightmap !!!
|
|
for( b = 0; b < nPieceSizeY; ++b )
|
|
{
|
|
for( a = 0; a < nPieceSizeX; ++a )
|
|
{
|
|
if( Piece[a+b*nPieceSizeX] != 0 )
|
|
LightMap[(i+a)+(j+b)*nLightMapSizeX] = Piece[a+b*nPieceSizeX];
|
|
}
|
|
}
|
|
nNewPosX = i;
|
|
nNewPosY = j;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
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 )
|
|
{
|
|
CMatrix m1, m2, m3, m4, m5;
|
|
m1.identity();
|
|
m1.setPos( pMB->DefaultPivot );
|
|
m1.invert();
|
|
|
|
m2.identity();
|
|
m2.scale( pMB->DefaultScale );
|
|
|
|
m3.identity();
|
|
m3.setRot( pMB->DefaultRotQuat );
|
|
|
|
m4.identity();
|
|
m4.setPos( pMB->DefaultPivot );
|
|
|
|
m5.identity();
|
|
m5.setPos( pMB->DefaultPos );
|
|
|
|
m1 = m5*m4*m3*m2*m1;
|
|
|
|
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 getLightBuildList(std::vector<SLightBuild>& vectLight, TimeValue tvTime, Interface& ip, INode*node=NULL )
|
|
{
|
|
if( node == NULL )
|
|
node = ip.GetRootNode();
|
|
|
|
// Get a pointer on the object's node
|
|
Object *obj = node->EvalWorldState(tvTime).obj;
|
|
|
|
// Check if there is an object
|
|
if (obj)
|
|
{
|
|
// Get a GenLight from the node
|
|
if (obj->SuperClassID()==LIGHT_CLASS_ID)
|
|
{
|
|
GenLight *maxLight = (GenLight *) obj;
|
|
|
|
bool deleteIt=false;
|
|
if (obj != maxLight)
|
|
deleteIt = true;
|
|
|
|
SLightBuild nelLight;
|
|
|
|
// Is the light is animatable ? (TEMP MAT)
|
|
Modifier *pModifier = CExportNel::getModifier( node, Class_ID(NEL_LIGHT_CLASS_ID_A, NEL_LIGHT_CLASS_ID_B) );
|
|
if( pModifier != NULL )
|
|
{
|
|
int bDynamic;
|
|
string sGroup;
|
|
// Get the value of the parameters
|
|
CExportNel::getValueByNameUsingParamBlock2( *pModifier, "bDynamic", (ParamType2)TYPE_BOOL, &bDynamic, 0);
|
|
CExportNel::getValueByNameUsingParamBlock2( *pModifier, "sGroup", (ParamType2)TYPE_STRING, &sGroup, 0);
|
|
if( bDynamic )
|
|
{
|
|
nelLight.bAnimate = false;
|
|
nelLight.GroupName = sGroup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nelLight.bAnimate = false;
|
|
nelLight.GroupName = "GlobalLight";
|
|
}
|
|
|
|
// Eval the light state fot this tvTime
|
|
Interval valid=NEVER;
|
|
LightState ls;
|
|
|
|
if (maxLight->EvalLightState(tvTime, valid, &ls)==REF_SUCCEED)
|
|
{
|
|
// Set the light mode
|
|
switch (maxLight->Type())
|
|
{
|
|
case OMNI_LIGHT:
|
|
nelLight.Type = SLightBuild::EType::LightPoint;
|
|
break;
|
|
case TSPOT_LIGHT:
|
|
case FSPOT_LIGHT:
|
|
nelLight.Type = SLightBuild::EType::LightSpot;
|
|
break;
|
|
case DIR_LIGHT:
|
|
case TDIR_LIGHT:
|
|
nelLight.Type = SLightBuild::EType::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 ?
|
|
nelLight.Ambient = nelColor;
|
|
nelLight.Diffuse = CRGBA (0,0,0);
|
|
nelLight.Specular = CRGBA (0,0,0);
|
|
|
|
/*
|
|
if (maxLight->GetAmbientOnly())
|
|
{
|
|
nelLight.Ambient = nelColor;
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
// Affect the diffuse color ?
|
|
if( maxLight->GetAffectDiffuse() )
|
|
nelLight.Diffuse = nelColor;
|
|
// Affect the specular color ?
|
|
if (maxLight->GetAffectSpecular())
|
|
nelLight.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
|
|
nelLight.Position = position;
|
|
|
|
// 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
|
|
nelLight.Direction = direction;
|
|
/*
|
|
// Set spot light information
|
|
nelLight.setCutoff ((float)(NLMISC::Pi*maxLight.GetFallsize(tvTime)/180.f/2.f));
|
|
|
|
// Compute the exponent value
|
|
float angle=(float)(NLMISC::Pi*maxLight.GetHotspot(tvTime)/(2.0*180.0));
|
|
nelLight.setupSpotExponent (angle);
|
|
|
|
// *** Set attenuation
|
|
|
|
if (maxLight.GetUseAtten())
|
|
nelLight.setupAttenuation (maxLight.GetAtten (tvTime, ATTEN_START), maxLight.GetAtten (tvTime, ATTEN_END));
|
|
else
|
|
nelLight.setNoAttenuation ();
|
|
*/
|
|
|
|
nelLight.rHotspot = (float)(Pi * maxLight->GetHotspot(tvTime) /(2.0*180.0));
|
|
nelLight.rFallof = (float)(Pi * maxLight->GetFallsize(tvTime)/(2.0*180.0));
|
|
|
|
if (maxLight->GetUseAtten())
|
|
{
|
|
nelLight.rRadiusMin = maxLight->GetAtten (tvTime, ATTEN_START);
|
|
nelLight.rRadiusMax = maxLight->GetAtten (tvTime, ATTEN_END);
|
|
}
|
|
else
|
|
{ // Limit
|
|
nelLight.rRadiusMin = 10.0;
|
|
nelLight.rRadiusMax = 10.0;
|
|
}
|
|
|
|
nelLight.bCastShadow = ( maxLight->GetShadow() != 0 );
|
|
nelLight.rMult = maxLight->GetShadMult (tvTime);
|
|
// Add the light in the list
|
|
vectLight.push_back (nelLight);
|
|
}
|
|
if( deleteIt )
|
|
delete maxLight;
|
|
}
|
|
}
|
|
|
|
// Recurse sub node
|
|
for (int i=0; i<node->NumberOfChildren(); i++)
|
|
getLightBuildList(vectLight, tvTime, ip, node->GetChildNode(i));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void getLightBuilds( vector<SLightBuild> &lights, TimeValue tvTime, Interface& ip )
|
|
{
|
|
SLightBuild amb;
|
|
|
|
amb.Type = SLightBuild::EType::LightAmbient;
|
|
amb.GroupName = "GlobalLight";
|
|
amb.bAnimate = false;
|
|
amb.Ambient.R = (uint8)(ip.GetAmbient( tvTime, FOREVER ).x*255);
|
|
amb.Ambient.G = (uint8)(ip.GetAmbient( tvTime, FOREVER ).y*255);
|
|
amb.Ambient.B = (uint8)(ip.GetAmbient( tvTime, FOREVER ).z*255);
|
|
amb.Ambient.A = 255;
|
|
amb.Specular = amb.Diffuse = CRGBA(0,0,0,0);
|
|
lights.push_back( amb );
|
|
getLightBuildList( lights, tvTime, ip );
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
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;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
double calculateTriangleSurface( CVector &p1, CVector &p2, CVector &p3 )
|
|
{
|
|
CVector n = ((p2-p1)^(p3-p1));
|
|
return 0.5 * n.norm(); // Half of the norm
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
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.0001 )
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
/* // To keep
|
|
void SortPlanesBySurface( vector<sint32> &PlaneGroup, vector<CMesh::CFace*>::iterator ItFace, sint32 nNbFace )
|
|
{
|
|
sint32 i, j, k;
|
|
double rMinU = 1000000.0, rMaxU = -1000000.0, rMinV = 1000000.0, rMaxV = -1000000.0;
|
|
|
|
CMesh::CFace *pF;
|
|
vector< pair < sint32, sint32 > > sizes;
|
|
|
|
sizes.resize( PlaneGroup.size() );
|
|
// Get the size of surface for each plane
|
|
vector<CMesh::CFace*>::iterator ItParseI = ItFace;
|
|
for( i = 0; i < sizes.size(); ++i )
|
|
{
|
|
rMinU = 1000000.0; rMaxU = -1000000.0;
|
|
rMinV = 1000000.0; rMaxV = -1000000.0;
|
|
for( j = 0; j < PlaneGroup[i]; ++j )
|
|
{
|
|
pF = *ItParseI;
|
|
for( k = 0; k < 3; ++k )
|
|
{
|
|
if( rMinU > pF->Corner[k].Uvws[1].U ) rMinU = pF->Corner[k].Uvws[1].U;
|
|
if( rMaxU < pF->Corner[k].Uvws[1].U ) rMaxU = pF->Corner[k].Uvws[1].U;
|
|
if( rMinV > pF->Corner[k].Uvws[1].V ) rMinV = pF->Corner[k].Uvws[1].V;
|
|
if( rMaxV < pF->Corner[k].Uvws[1].V ) rMaxV = pF->Corner[k].Uvws[1].V;
|
|
}
|
|
++ItParseI;
|
|
}
|
|
sizes[i].first = i;
|
|
sizes[i].second = (rMaxU - rMinU) * (rMaxV - rMinV);
|
|
}
|
|
// Sort surfaces to put the biggest first
|
|
for( i = 0; i < sizes.size()-1; ++i )
|
|
for( j = i+1; j < sizes.size(); ++j )
|
|
{
|
|
if( sizes[i].second < sizes[j].second )
|
|
{
|
|
pair< sint32, sint32 > tmp = sizes[i];
|
|
sizes[i] = sizes[j];
|
|
sizes[j] = tmp;
|
|
}
|
|
}
|
|
|
|
vector<CMesh::CFace*> TempGrp;
|
|
vector<CMesh::CFace*>::iterator ItParseOut;
|
|
TempGrp.resize( nNbFace );
|
|
|
|
ItParseOut = TempGrp.begin();
|
|
for( i = 0; i < sizes.size(); ++i )
|
|
{
|
|
// Treating group
|
|
j = sizes[i].first;
|
|
ItParseI = ItFace;
|
|
// Positionnement of the pointer to the first face of the j th group
|
|
for( k = 0; k < j; ++k )
|
|
ItParseI += PlaneGroup[k];
|
|
// Copy the group j at the end of TempGrp
|
|
for( k = 0; k < PlaneGroup[j]; ++k )
|
|
{
|
|
*ItParseOut = *ItParseI;
|
|
++ItParseOut;
|
|
++ItParseI;
|
|
}
|
|
}
|
|
// So now we just have to copy back the temporary group
|
|
ItParseOut = TempGrp.begin();
|
|
ItParseI = ItFace;
|
|
for( i = 0; i < nNbFace; ++i )
|
|
{
|
|
*ItParseI = *ItParseOut;
|
|
++ItParseOut;
|
|
++ItParseI;
|
|
}
|
|
// And the same with the group delimiter
|
|
for( i = 0; i < sizes.size(); ++i )
|
|
{
|
|
j = sizes[i].first;
|
|
sizes[i].first = PlaneGroup[j];
|
|
}
|
|
for( i = 0; i < sizes.size(); ++i )
|
|
PlaneGroup[i] = sizes[i].first;
|
|
}
|
|
*/
|
|
// -----------------------------------------------------------------------------------------------
|
|
void PutLMPlaneInLMPlane( SLMPlane &Dst, SLMPlane &Src, bool bMaskOnly = false )
|
|
{
|
|
sint32 a, b;
|
|
if( ( (Src.w + Src.x) > Dst.w ) || ( (Src.h + Src.y) > Dst.h ) )
|
|
{
|
|
a = 0; b = 0;
|
|
}
|
|
for( b = 0; b < Src.h; ++b )
|
|
for( a = 0; a < Src.w; ++a )
|
|
if( Src.msk[a+b*Src.w] != 0 )
|
|
{
|
|
Dst.msk[(Src.x+a)+(Src.y+b)*Dst.w] = Src.msk[a+b*Src.w];
|
|
if( bMaskOnly == false )
|
|
Dst.col[(Src.x+a)+(Src.y+b)*Dst.w] = Src.col[a+b*Src.w];
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
bool TestLMPLaneInLMPlane( SLMPlane &Dst, SLMPlane &Src )
|
|
{
|
|
sint32 a, b;
|
|
for( b = 0; b < Src.h; ++b )
|
|
for( a = 0; a < Src.w; ++a )
|
|
if( Src.msk[a+b*Src.w] != 0 )
|
|
if( Dst.msk[(Src.x+a)+(Src.y+b)*Dst.w] != 0 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
bool TryAllPosForLMPlaneInLMPlane( SLMPlane &Dst, SLMPlane &Src )
|
|
{
|
|
sint32 i, j;
|
|
|
|
if( Src.w > Dst.w ) return false;
|
|
if( Src.h > Dst.h ) return false;
|
|
|
|
// For all position test if the Src plane can be put in
|
|
for( j = 0; j < (Dst.h-Src.h); ++j )
|
|
for( i = 0; i < (Dst.w-Src.w); ++i )
|
|
{
|
|
Src.x = i; Src.y = j;
|
|
if( TestLMPLaneInLMPlane( Dst, Src ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void CopyPlaneColToBitmap32( CBitmap* pImage, SLMPlane &Plane, sint32 nLayerNb )
|
|
{
|
|
if( ( pImage->getWidth() != Plane.w ) ||
|
|
( pImage->getHeight() != Plane.h ) )
|
|
ResizeBitmap32( pImage, Plane.w, Plane.h );
|
|
|
|
CObjectVector<uint8> &vBitmap = pImage->getPixels();
|
|
|
|
for( sint32 i = 0; i < Plane.w*Plane.h; ++i )
|
|
{
|
|
vBitmap[4*i+0] = (uint8)(127.0*Plane.col[i].p[nLayerNb].R);
|
|
vBitmap[4*i+1] = (uint8)(127.0*Plane.col[i].p[nLayerNb].G);
|
|
vBitmap[4*i+2] = (uint8)(127.0*Plane.col[i].p[nLayerNb].B);
|
|
vBitmap[4*i+3] = (uint8)(255.0*Plane.col[i].p[nLayerNb].A);
|
|
}
|
|
/* // To see contours
|
|
for( sint32 i = 0; i < Plane.w*Plane.h; ++i )
|
|
if( ( Plane.msk[i] == 0 ) || ( Plane.msk[i] == 1 ) || ( Plane.msk[i] == 3 ) )
|
|
{
|
|
vBitmap[i*4+0] = 0;
|
|
vBitmap[i*4+1] = 255;
|
|
vBitmap[i*4+2] = 0;
|
|
vBitmap[i*4+3] = 255;
|
|
}
|
|
else
|
|
{
|
|
vBitmap[i*4+0] = Plane.col[i*4+0];
|
|
vBitmap[i*4+1] = Plane.col[i*4+1];
|
|
vBitmap[i*4+2] = Plane.col[i*4+2];
|
|
vBitmap[i*4+3] = Plane.col[i*4+3];
|
|
}
|
|
*/
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Do not stretch the image inside the plane
|
|
void ResizeLMPlane( SLMPlane &Dst, sint32 nNewSizeX, sint32 nNewSizeY )
|
|
{
|
|
vector<uint8> vImgTemp;
|
|
vector<SLMPixel> vImgTemp2;
|
|
int i, j;
|
|
|
|
vImgTemp.resize(nNewSizeX*nNewSizeY);
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
vImgTemp[i] = 0;
|
|
|
|
for( j = 0; j < min(Dst.h,nNewSizeY); ++j )
|
|
for( i = 0; i < min(Dst.w,nNewSizeX); ++i )
|
|
{
|
|
vImgTemp[i+j*nNewSizeX] = Dst.msk[i+j*Dst.w];
|
|
}
|
|
|
|
Dst.msk.resize(nNewSizeX*nNewSizeY);
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
Dst.msk[i] = vImgTemp[i];
|
|
|
|
// The same as the mask but for the bitmap
|
|
vImgTemp2.resize(nNewSizeX*nNewSizeY);
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
for( j = 0; j < 8; ++j )
|
|
{ vImgTemp2[i].p[j].R = vImgTemp2[i].p[j].G = vImgTemp2[i].p[j].B = vImgTemp2[i].p[j].A = 0.0f; }
|
|
|
|
for( j = 0; j < min(Dst.h,nNewSizeY); ++j )
|
|
for( i = 0; i < min(Dst.w,nNewSizeX); ++i )
|
|
vImgTemp2[i+j*nNewSizeX] = Dst.col[i+j*Dst.w];
|
|
|
|
Dst.col.resize(nNewSizeX*nNewSizeY);
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
Dst.col[i] = vImgTemp2[i];
|
|
|
|
Dst.w = nNewSizeX;
|
|
Dst.h = nNewSizeY;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Stretch a plane by a given factor 4.0 -> multiply its size by 4 and 0.5 -> halves its size
|
|
void stretchLMPlane( SLMPlane *pPlane, double osFactor )
|
|
{
|
|
sint32 nNewSizeX = (sint32)(pPlane->w * osFactor);
|
|
sint32 nNewSizeY = (sint32)(pPlane->h * osFactor);
|
|
vector<uint8> vImgTemp;
|
|
vector<SLMPixel> vImgTemp2;
|
|
int i, j, k;
|
|
|
|
// Reduce the color
|
|
vImgTemp2.resize( nNewSizeX * nNewSizeY );
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
for( j = 0; j < 8; ++j )
|
|
{ vImgTemp2[i].p[j].R = vImgTemp2[i].p[j].G = vImgTemp2[i].p[j].B = vImgTemp2[i].p[j].A = 0.0f; }
|
|
|
|
vImgTemp.resize( nNewSizeX * nNewSizeY );
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
vImgTemp[i] = 0;
|
|
|
|
double dx, dy, x, y;
|
|
if( osFactor > 1.0 ) // Up
|
|
{
|
|
dx = 1.0/osFactor;
|
|
dy = 1.0/osFactor;
|
|
y = 0.0;
|
|
for( j = 0; j < nNewSizeY; ++j )
|
|
{
|
|
x = 0.0;
|
|
for( i = 0; i < nNewSizeX; ++i )
|
|
{
|
|
if( pPlane->msk[((sint32)x)+((sint32)y)*pPlane->w] != 0 )
|
|
{
|
|
vImgTemp[i+j*nNewSizeX] = 1;
|
|
}
|
|
for( k = 0; k < 8; ++k )
|
|
{
|
|
vImgTemp2[i+j*nNewSizeX].p[k].R += pPlane->col[((sint32)x)+((sint32)y)*pPlane->w].p[k].R;
|
|
vImgTemp2[i+j*nNewSizeX].p[k].G += pPlane->col[((sint32)x)+((sint32)y)*pPlane->w].p[k].G;
|
|
vImgTemp2[i+j*nNewSizeX].p[k].B += pPlane->col[((sint32)x)+((sint32)y)*pPlane->w].p[k].B;
|
|
vImgTemp2[i+j*nNewSizeX].p[k].A += 1.0f;
|
|
}
|
|
x += dx;
|
|
}
|
|
y += dy;
|
|
}
|
|
}
|
|
else // Down
|
|
{
|
|
dx = osFactor;
|
|
dy = osFactor;
|
|
y = 0.0;
|
|
for( j = 0; j < pPlane->h; ++j )
|
|
{
|
|
x = 0.0;
|
|
for( i = 0; i < pPlane->w; ++i )
|
|
{
|
|
if( pPlane->msk[i+j*pPlane->w] != 0 )
|
|
{
|
|
vImgTemp[((sint32)x)+((sint32)y)*nNewSizeX] = 1;
|
|
}
|
|
for( k = 0; k < 8; ++k )
|
|
{
|
|
vImgTemp2[((sint32)x)+((sint32)y)*nNewSizeX].p[k].R += pPlane->col[i+j*pPlane->w].p[k].R;
|
|
vImgTemp2[((sint32)x)+((sint32)y)*nNewSizeX].p[k].G += pPlane->col[i+j*pPlane->w].p[k].G;
|
|
vImgTemp2[((sint32)x)+((sint32)y)*nNewSizeX].p[k].B += pPlane->col[i+j*pPlane->w].p[k].B;
|
|
vImgTemp2[((sint32)x)+((sint32)y)*nNewSizeX].p[k].A += 1.0f;
|
|
}
|
|
x += dx;
|
|
}
|
|
y += dy;
|
|
}
|
|
}
|
|
|
|
for( j = 0; j < nNewSizeY; ++j )
|
|
for( i = 0; i < nNewSizeX; ++i )
|
|
for( k = 0; k < 8; ++k )
|
|
if( vImgTemp2[i+j*nNewSizeX].p[k].A > 1.0f )
|
|
{
|
|
vImgTemp2[i+j*nNewSizeX].p[k].R /= vImgTemp2[i+j*nNewSizeX].p[k].A;
|
|
vImgTemp2[i+j*nNewSizeX].p[k].G /= vImgTemp2[i+j*nNewSizeX].p[k].A;
|
|
vImgTemp2[i+j*nNewSizeX].p[k].B /= vImgTemp2[i+j*nNewSizeX].p[k].A;
|
|
vImgTemp2[i+j*nNewSizeX].p[k].A = 1.0f;
|
|
}
|
|
|
|
pPlane->col.resize( nNewSizeX * nNewSizeY );
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
pPlane->col[i] = vImgTemp2[i];
|
|
|
|
pPlane->msk.resize( nNewSizeX * nNewSizeY );
|
|
for( i = 0; i < nNewSizeX*nNewSizeY; ++i )
|
|
pPlane->msk[i] = vImgTemp[i];
|
|
|
|
pPlane->w = nNewSizeX;
|
|
pPlane->h = nNewSizeY;
|
|
pPlane->x = (sint32)(pPlane->x * osFactor);
|
|
pPlane->y = (sint32)(pPlane->y * osFactor);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void CreateLMPlaneFromFace( SLMPlane &Out, CMesh::CFace *pF )
|
|
{
|
|
double lumx1 = pF->Corner[0].Uvws[1].U, lumy1 = pF->Corner[0].Uvws[1].V,
|
|
lumx2 = pF->Corner[1].Uvws[1].U, lumy2 = pF->Corner[1].Uvws[1].V,
|
|
lumx3 = pF->Corner[2].Uvws[1].U, lumy3 = pF->Corner[2].Uvws[1].V;
|
|
double minx, miny;
|
|
double maxx, maxy;
|
|
int j,k;
|
|
|
|
minx = lumx1;
|
|
if( minx > lumx2 ) minx = lumx2;
|
|
if( minx > lumx3 ) minx = lumx3;
|
|
maxx = lumx1;
|
|
if( maxx < lumx2 ) maxx = lumx2;
|
|
if( maxx < lumx3 ) maxx = lumx3;
|
|
miny = lumy1;
|
|
if( miny > lumy2 ) miny = lumy2;
|
|
if( miny > lumy3 ) miny = lumy3;
|
|
maxy = lumy1;
|
|
if( maxy < lumy2 ) maxy = lumy2;
|
|
if( maxy < lumy3 ) maxy = lumy3;
|
|
|
|
// Put the piece in the new basis (nPosX,nPosY)
|
|
Out.x = ((sint32)floor(minx-0.5));
|
|
Out.y = ((sint32)floor(miny-0.5));
|
|
|
|
lumx1 -= Out.x; lumy1 -= Out.y;
|
|
lumx2 -= Out.x; lumy2 -= Out.y;
|
|
lumx3 -= Out.x; lumy3 -= Out.y;
|
|
|
|
ResizeLMPlane( Out, 1 + ((sint32)floor(maxx+0.5)) - ((sint32)floor(minx-0.5)),
|
|
1 + ((sint32)floor(maxy+0.5)) - ((sint32)floor(miny-0.5)) );
|
|
|
|
for( j = 0; j < Out.w*Out.h; ++j )
|
|
{
|
|
Out.msk[j] = 0;
|
|
}
|
|
|
|
// The square interact with the triangle if an edge of the square is cut by an edge of the triangle
|
|
// Or the square is in the triangle
|
|
|
|
for( j = 0; j < Out.h-1; ++j )
|
|
for( k = 0; k < Out.w-1; ++k )
|
|
{
|
|
// Is the square (j,k) is interacting with the triangle
|
|
// This means : The square contains a point of the triangle (can be done for the 3 points)
|
|
// The triangle contains a point of the square
|
|
// If so then we have to turn on all the 4 pixels of the square
|
|
if( isInTriangleOrEdge(k+0.5,j+0.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) ||
|
|
isInTriangleOrEdge(k+1.5,j+0.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) ||
|
|
isInTriangleOrEdge(k+0.5,j+1.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) ||
|
|
isInTriangleOrEdge(k+1.5,j+1.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) )
|
|
{
|
|
Out.msk[k + j *Out.w] = 1;
|
|
Out.msk[1+k + j *Out.w] = 1;
|
|
Out.msk[k + (1+j)*Out.w] = 1;
|
|
Out.msk[1+k + (1+j)*Out.w] = 1;
|
|
}
|
|
|
|
if( segmentIntersection(k+0.5, j+0.5, k+1.5, j+0.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+1.5, j+0.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+1.5, j+0.5, lumx3, lumy3, lumx1, lumy1) ||
|
|
|
|
segmentIntersection(k+0.5, j+0.5, k+0.5, j+1.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+0.5, j+1.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+0.5, j+0.5, k+0.5, j+1.5, lumx3, lumy3, lumx1, lumy1) ||
|
|
|
|
segmentIntersection(k+1.5, j+1.5, k+1.5, j+0.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+1.5, j+0.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+1.5, j+0.5, lumx3, lumy3, lumx1, lumy1) ||
|
|
|
|
segmentIntersection(k+1.5, j+1.5, k+0.5, j+1.5, lumx1, lumy1, lumx2, lumy2) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+0.5, j+1.5, lumx2, lumy2, lumx3, lumy3) ||
|
|
segmentIntersection(k+1.5, j+1.5, k+0.5, j+1.5, lumx3, lumy3, lumx1, lumy1) )
|
|
{
|
|
Out.msk[k + j *Out.w] = 1;
|
|
Out.msk[1+k + j *Out.w] = 1;
|
|
Out.msk[k + (1+j)*Out.w] = 1;
|
|
Out.msk[1+k + (1+j)*Out.w] = 1;
|
|
}
|
|
|
|
}
|
|
// For all the points of the triangle update the square
|
|
|
|
// TODO : Test if we need it !
|
|
|
|
Out.msk[((sint32)(lumx1-0.5)) + ((sint32)(lumy1-0.5)) *Out.w] = 1;
|
|
Out.msk[1+((sint32)(lumx1-0.5)) + ((sint32)(lumy1-0.5)) *Out.w] = 1;
|
|
Out.msk[((sint32)(lumx1-0.5)) + (1+((sint32)(lumy1-0.5)))*Out.w] = 1;
|
|
Out.msk[1+((sint32)(lumx1-0.5)) + (1+((sint32)(lumy1-0.5)))*Out.w] = 1;
|
|
|
|
Out.msk[((sint32)(lumx2-0.5)) + ((sint32)(lumy2-0.5)) *Out.w] = 1;
|
|
Out.msk[1+((sint32)(lumx2-0.5)) + ((sint32)(lumy2-0.5)) *Out.w] = 1;
|
|
Out.msk[((sint32)(lumx2-0.5)) + (1+((sint32)(lumy2-0.5)))*Out.w] = 1;
|
|
Out.msk[1+((sint32)(lumx2-0.5)) + (1+((sint32)(lumy2-0.5)))*Out.w] = 1;
|
|
|
|
Out.msk[((sint32)(lumx3-0.5)) + ((sint32)(lumy3-0.5)) *Out.w] = 1;
|
|
Out.msk[1+((sint32)(lumx3-0.5)) + ((sint32)(lumy3-0.5)) *Out.w] = 1;
|
|
Out.msk[((sint32)(lumx3-0.5)) + (1+((sint32)(lumy3-0.5)))*Out.w] = 1;
|
|
Out.msk[1+((sint32)(lumx3-0.5)) + (1+((sint32)(lumy3-0.5)))*Out.w] = 1;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Warning : modify the Faces Uvws[1]
|
|
void CreateLMPlaneFromFaceGroup( SLMPlane &Plane, vector<CMesh::CFace*>::iterator ItFace, sint32 nNbFace )
|
|
{
|
|
sint32 i, j;
|
|
double rMinU = 1000000.0, rMaxU = -1000000.0, rMinV = 1000000.0, rMaxV = -1000000.0;
|
|
vector<CMesh::CFace*>::iterator ItParseI = ItFace;
|
|
CMesh::CFace *pF;
|
|
|
|
Plane.faces.resize( nNbFace );
|
|
|
|
for( i = 0; i < nNbFace; ++i )
|
|
{
|
|
pF = *ItParseI;
|
|
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;
|
|
}
|
|
Plane.faces[i] = pF;
|
|
++ItParseI;
|
|
}
|
|
|
|
sint32 w = ( 1 + ((sint32)floor( rMaxU + 0.5 )) - ((sint32)floor( rMinU - 0.5 )) );
|
|
sint32 h = ( 1 + ((sint32)floor( rMaxV + 0.5 )) - ((sint32)floor( rMinV - 0.5 )) );
|
|
ResizeLMPlane( Plane, w, h );
|
|
Plane.x = ( ((sint32)floor( rMinU - 0.5 )) );
|
|
Plane.y = ( ((sint32)floor( rMinV - 0.5 )) );
|
|
for( j = 0; j < Plane.w*Plane.h; ++j )
|
|
Plane.msk[j] = 0;
|
|
|
|
ItParseI = ItFace;
|
|
for( i = 0; i < nNbFace; ++i )
|
|
{
|
|
pF = *ItParseI;
|
|
// Create Mask
|
|
SLMPlane Piece;
|
|
|
|
CreateLMPlaneFromFace( Piece, pF );
|
|
// Because all is in absolute coordinate
|
|
Piece.x -= Plane.x;
|
|
Piece.y -= Plane.y;
|
|
PutLMPlaneInLMPlane( Plane, Piece );
|
|
|
|
++ItParseI;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void ModifyLMPlaneWithOverSampling( SLMPlane *pPlane, double rOverSampling )
|
|
{
|
|
sint32 i, j;
|
|
vector<CMesh::CFace*>::iterator ItFace = pPlane->faces.begin();
|
|
sint32 nNbFace = pPlane->faces.size();
|
|
|
|
stretchLMPlane( pPlane, rOverSampling );
|
|
for( j = 0; j < pPlane->w*pPlane->h; ++j ) // Reset the mask
|
|
pPlane->msk[j] = 0;
|
|
MultiplyFaceUV1( ItFace, nNbFace, rOverSampling );
|
|
ItFace = pPlane->faces.begin();
|
|
// Recreate the form
|
|
for( i = 0; i < nNbFace; ++i )
|
|
{
|
|
CMesh::CFace *pF = *ItFace;
|
|
SLMPlane Piece;
|
|
CreateLMPlaneFromFace( Piece, pF );
|
|
Piece.x -= pPlane->x;
|
|
Piece.y -= pPlane->y;
|
|
PutLMPlaneInLMPlane( *pPlane, Piece, true );
|
|
++ItFace;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void PlaceLMPlaneInLMPLane( SLMPlane &Dst, SLMPlane &Src )
|
|
{
|
|
while( true )
|
|
{
|
|
if( !TryAllPosForLMPlaneInLMPlane( Dst, Src ) )
|
|
{
|
|
if( ( Dst.w < MAXLIGHTMAPSIZE ) || ( Dst.h < MAXLIGHTMAPSIZE ) )
|
|
{
|
|
if( Dst.w < Dst.h )
|
|
ResizeLMPlane( Dst, Dst.w*2, Dst.h );
|
|
else
|
|
ResizeLMPlane( Dst, Dst.w, Dst.h*2 );
|
|
}
|
|
else
|
|
{
|
|
// ERROR: we reached the maximum texture size
|
|
nlstop;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We found a position
|
|
PutLMPlaneInLMPlane( Dst, Src );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void CalculateGradient( SGradient &g, 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];
|
|
|
|
double GradDen = 1.0 / ( (u3-u1)*(v2-v1) - (u2-u1)*(v3-v1) );
|
|
|
|
g.InitU = u1;
|
|
g.InitV = v1;
|
|
|
|
g.InitPx = p1.x;
|
|
g.InitPy = p1.y;
|
|
g.InitPz = p1.z;
|
|
|
|
g.InitNx = n1.x;
|
|
g.InitNy = n1.y;
|
|
g.InitNz = n1.z;
|
|
|
|
// Gradients for the vertex
|
|
|
|
g.GraduPx = ( (p3.x-p1.x)*(v2-v1)-(p2.x-p1.x)*(v3-v1) ) * GradDen;
|
|
g.GradvPx = ( (p2.x-p1.x)*(u3-u1)-(p3.x-p1.x)*(u2-u1) ) * GradDen;
|
|
|
|
g.GraduPy = ( (p3.y-p1.y)*(v2-v1)-(p2.y-p1.y)*(v3-v1) ) * GradDen;
|
|
g.GradvPy = ( (p2.y-p1.y)*(u3-u1)-(p3.y-p1.y)*(u2-u1) ) * GradDen;
|
|
|
|
g.GraduPz = ( (p3.z-p1.z)*(v2-v1)-(p2.z-p1.z)*(v3-v1) ) * GradDen;
|
|
g.GradvPz = ( (p2.z-p1.z)*(u3-u1)-(p3.z-p1.z)*(u2-u1) ) * GradDen;
|
|
|
|
// The same for the normal
|
|
|
|
g.GraduNx = ( (n3.x-n1.x)*(v2-v1)-(n2.x-n1.x)*(v3-v1) ) * GradDen;
|
|
g.GradvNx = ( (n2.x-n1.x)*(u3-u1)-(n3.x-n1.x)*(u2-u1) ) * GradDen;
|
|
|
|
g.GraduNy = ( (n3.y-n1.y)*(v2-v1)-(n2.y-n1.y)*(v3-v1) ) * GradDen;
|
|
g.GradvNy = ( (n2.y-n1.y)*(u3-u1)-(n3.y-n1.y)*(u2-u1) ) * GradDen;
|
|
|
|
g.GraduNz = ( (n3.z-n1.z)*(v2-v1)-(n2.z-n1.z)*(v3-v1) ) * GradDen;
|
|
g.GradvNz = ( (n2.z-n1.z)*(u3-u1)-(n3.z-n1.z)*(u2-u1) ) * GradDen;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
CVector CalculateInterpolatedVertex( SGradient &g, double u, double v )
|
|
{
|
|
CVector vRet;
|
|
vRet.x = (float)(g.GraduPx*(u-g.InitU) + g.GradvPx*(v-g.InitV) + g.InitPx);
|
|
vRet.y = (float)(g.GraduPy*(u-g.InitU) + g.GradvPy*(v-g.InitV) + g.InitPy);
|
|
vRet.z = (float)(g.GraduPz*(u-g.InitU) + g.GradvPz*(v-g.InitV) + g.InitPz);
|
|
return vRet;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
CVector CalculateInterpolatedNormal( SGradient &g, double u, double v )
|
|
{
|
|
CVector vRet;
|
|
vRet.x = (float)(g.GraduNx*(u-g.InitU) + g.GradvNx*(v-g.InitV) + g.InitNx);
|
|
vRet.y = (float)(g.GraduNy*(u-g.InitU) + g.GradvNy*(v-g.InitV) + g.InitNy);
|
|
vRet.z = (float)(g.GraduNz*(u-g.InitU) + g.GradvNz*(v-g.InitV) + g.InitNz);
|
|
vRet.normalize();
|
|
return vRet;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
float TestRay( CVector &vLightPos, CVector &vVertexPos, SWorldRT &wrt, sint32 nLightNb )
|
|
{
|
|
|
|
// Optim avec Cube Grid
|
|
wrt.cgAccel[nLightNb].select( vVertexPos - vLightPos );
|
|
while( !wrt.cgAccel[nLightNb].isEndSel() )
|
|
{
|
|
SCubeGridCell cell = wrt.cgAccel[nLightNb].getSel();
|
|
|
|
CVector hit;
|
|
CTriangle t(cell.pMB->Vertices[cell.pF->Corner[0].Vertex],
|
|
cell.pMB->Vertices[cell.pF->Corner[1].Vertex],
|
|
cell.pMB->Vertices[cell.pF->Corner[2].Vertex] );
|
|
CPlane plane;
|
|
plane.make( t.V0, t.V1, t.V2 );
|
|
|
|
if( t.intersect( vLightPos, vVertexPos, hit, plane ) )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
// Next selected element
|
|
wrt.cgAccel[nLightNb].nextSel();
|
|
}
|
|
return 1.0f;
|
|
|
|
/* // Optim avec BSP
|
|
for( sint32 i = 0; i < wrt.bbBoxes.size(); ++i )
|
|
{
|
|
if( wrt.bbBoxes[i].intersect( rayP1, rayP2, rayP2 ) )
|
|
{
|
|
sint32 nNbSel = wrt.btAccel[i].select( rayP1, rayP2 );
|
|
for( sint32 j = 0; j < nNbSel; ++j )
|
|
{
|
|
SWorldRTCell cell = wrt.btAccel[i].getSelection( j );
|
|
|
|
CVector hit;
|
|
CTriangle t(cell.pMB->Vertices[cell.pF->Corner[0].Vertex],
|
|
cell.pMB->Vertices[cell.pF->Corner[1].Vertex],
|
|
cell.pMB->Vertices[cell.pF->Corner[2].Vertex] );
|
|
CPlane plane;
|
|
plane.make( t.V0, t.V1, t.V2 );
|
|
|
|
if( t.intersect( rayP1, rayP2, hit, plane ) )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1.0f;*/
|
|
|
|
|
|
/*// Optim avec quad tree
|
|
for( sint32 i = 0; i < wrt.bbBoxes.size(); ++i )
|
|
{
|
|
if( wrt.bbBoxes[i].intersect( rayP1, rayP2, rayP2 ) )
|
|
{
|
|
wrt.qtAccel[i].selectSegment( rayP1, rayP2 );
|
|
|
|
CQuadTree<SWorldRTCell>::CIterator it = wrt.qtAccel[i].begin();
|
|
while( it != wrt.qtAccel[i].end() )
|
|
{
|
|
SWorldRTCell cell = *it;
|
|
|
|
CVector hit;
|
|
CTriangle t(cell.pMB->Vertices[cell.pF->Corner[0].Vertex],
|
|
cell.pMB->Vertices[cell.pF->Corner[1].Vertex],
|
|
cell.pMB->Vertices[cell.pF->Corner[2].Vertex] );
|
|
CPlane plane;
|
|
plane.make( t.V0, t.V1, t.V2 );
|
|
|
|
if( t.intersect( rayP1, rayP2, hit, plane ) )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
// Next selected element
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
return 1.0f;*/
|
|
|
|
|
|
/* // No optim
|
|
for( sint32 i = 0; i < Meshes.size(); ++i )
|
|
{
|
|
CMesh::CMeshBuild *pMB = Meshes[i].first;
|
|
// Test the ray with the bsphere first
|
|
if( segmentIntersectBSphere( rayP1, rayP2, Meshes[i].second ) )
|
|
{
|
|
CMatrix MBMatrix = getObjectToWorldMatrix( pMB );
|
|
for( sint32 j = 0; j < pMB->Faces.size(); ++j )
|
|
{
|
|
CVector hit;
|
|
CTriangle t(MBMatrix * pMB->Vertices[pMB->Faces[j].Corner[0].Vertex],
|
|
MBMatrix * pMB->Vertices[pMB->Faces[j].Corner[1].Vertex],
|
|
MBMatrix * pMB->Vertices[pMB->Faces[j].Corner[2].Vertex] );
|
|
CPlane plane;
|
|
plane.make( t.V0, t.V1, t.V2 );
|
|
|
|
if( t.intersect( rayP1, rayP2, hit, plane ) )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
float RayTraceAVertex( CVector &p, SWorldRT &wrt, sint32 nLightNb, SLightBuild& rLight )
|
|
{
|
|
double rFactor = 0.0;
|
|
sint32 nLightForFactor = 0;
|
|
|
|
TTicks zeTime = CTime::getPerformanceTime();
|
|
|
|
switch( rLight.Type )
|
|
{
|
|
case SLightBuild::LightAmbient:
|
|
rFactor = 1.0;
|
|
break;
|
|
case SLightBuild::LightSpot:
|
|
case SLightBuild::LightPoint:
|
|
{
|
|
CVector light_p = p - rLight.Position;
|
|
float light_p_distance = light_p.norm();
|
|
light_p_distance = light_p_distance - (0.01+(0.05*light_p_distance/100.0)); // Substract n centimeter
|
|
light_p.normalize();
|
|
light_p *= light_p_distance;
|
|
rFactor = TestRay( rLight.Position, rLight.Position + light_p, wrt, nLightNb );
|
|
nLightForFactor++;
|
|
}
|
|
break;
|
|
case SLightBuild::LightDir:
|
|
/*
|
|
{
|
|
CVector r1 = p-rLight.Direction/100;
|
|
CVector r2 = p-100*rLight.Direction;
|
|
rFactor = TestRay( r1, r2, wrt );
|
|
nLightForFactor++;
|
|
}
|
|
*/
|
|
rFactor = 1.0; // Not done for the moment
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
timerCalcRT += CTime::getPerformanceTime() - zeTime;
|
|
|
|
if( rFactor > 0.0 )
|
|
return 1.0f;
|
|
else
|
|
return 0.0f;
|
|
//return rFactor / ((double)nLightForFactor);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
CRGBAF LightAVertex( CVector &pRT, CVector &p, CVector &n,
|
|
vector<sint32> &vLights, vector<SLightBuild> &AllLights,
|
|
SWorldRT &wrt, bool bDoubleSided )
|
|
{
|
|
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);
|
|
float factor = 0.0;
|
|
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();
|
|
|
|
// ??? light_intensity *= light_intensity * light_intensity;
|
|
if( bDoubleSided && (n*p_light < 0.0f) )
|
|
{
|
|
p_light = -p_light;
|
|
}
|
|
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 = 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 *= light_intensity * light_intensity;
|
|
if( bDoubleSided && (n*p_light < 0.0f) )
|
|
{
|
|
p_light = -p_light;
|
|
}
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
if( light_intensity > 0.0f )
|
|
{
|
|
if( ( rLight.bCastShadow ) && ( theExportSceneStruct.bShadow ) )
|
|
factor = RayTraceAVertex( pRT, wrt, vLights[nLight], rLight );
|
|
else
|
|
factor = 1.0f;
|
|
factor *= rLight.rMult;
|
|
}
|
|
|
|
rgbafRet.R += lightAmbiCol.R + lightDiffCol.R * factor;
|
|
if( rgbafRet.R > 1.0f ) rgbafRet.R = 1.0;
|
|
rgbafRet.G += lightAmbiCol.G + lightDiffCol.G * factor;
|
|
if( rgbafRet.G > 1.0f ) rgbafRet.G = 1.0;
|
|
rgbafRet.B += lightAmbiCol.B + lightDiffCol.B * factor;
|
|
if( rgbafRet.B > 1.0f ) rgbafRet.B = 1.0;
|
|
rgbafRet.A += lightAmbiCol.A + lightDiffCol.A * factor;
|
|
if( rgbafRet.A > 1.0f ) rgbafRet.A = 1.0;
|
|
}
|
|
return rgbafRet;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
CVector CalcInterpolatedVertexInFace( SGradient &g, 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 CalculateInterpolatedVertex( g, Uout, Vout );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
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, SLMPlane &Plane, vector<CVector> &vVertices,
|
|
CMatrix& ToWorldMat, vector<sint32> &vLights, vector<SLightBuild> &AllLights,
|
|
sint32 nLayerNb, SWorldRT &wrt )
|
|
{
|
|
// Fill interiors
|
|
vector<CMesh::CFace*>::iterator ItFace = Plane.faces.begin();
|
|
sint32 nNbFace = Plane.faces.size();
|
|
sint32 i, 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 = pMB->Materials[pF->MaterialId].detDoubleSided();
|
|
|
|
// 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 );
|
|
|
|
CalculateGradient( g, pF, vVertices, n1, n2, n3 );
|
|
|
|
// Process all the interior
|
|
for( k = nPosMinV; k <= nPosMaxV; ++k )
|
|
for( j = nPosMinU; j <= nPosMaxU; ++j )
|
|
{
|
|
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 = CalculateInterpolatedVertex( g, j+0.5, k+0.5);
|
|
CVector n = CalculateInterpolatedNormal( g, j+0.5, k+0.5);
|
|
CRGBAF col = LightAVertex( p, p, n, vLights, AllLights, wrt, doubleSided );
|
|
Plane.col[j-Plane.x + (k-Plane.y)*Plane.w].p[nLayerNb].R = col.R;
|
|
Plane.col[j-Plane.x + (k-Plane.y)*Plane.w].p[nLayerNb].G = col.G;
|
|
Plane.col[j-Plane.x + (k-Plane.y)*Plane.w].p[nLayerNb].B = col.B;
|
|
Plane.col[j-Plane.x + (k-Plane.y)*Plane.w].p[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, vector<SLMPlane*>::iterator ItPlanes, sint32 nNbPlanes,
|
|
vector<CVector> &vVertices, CMatrix& ToWorldMat,
|
|
vector<sint32> &vLights, vector<SLightBuild> &AllLights,
|
|
sint32 nLayerNb, SWorldRT &wrt)
|
|
{
|
|
// Fill interiors
|
|
sint32 i, j, k;
|
|
sint32 nPosMinU, nPosMaxU, nPosMinV, nPosMaxV;
|
|
SGradient g;
|
|
|
|
vector<SLMPlane*>::iterator ItPlanes1 = ItPlanes;
|
|
for( sint32 nPlanes1 = 0; nPlanes1 < nNbPlanes; ++nPlanes1 )
|
|
{
|
|
SLMPlane *pPlane1 = *ItPlanes1;
|
|
vector<CMesh::CFace*>::iterator ItParseI = pPlane1->faces.begin();
|
|
sint32 nNbFace1 = pPlane1->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 = pMB->Materials[pF1->MaterialId].detDoubleSided();
|
|
// 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 );
|
|
|
|
CalculateGradient( g, 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( ( pPlane1->msk[j-pPlane1->x + (k-pPlane1->y)*pPlane1->w] == 1 ) ||
|
|
( pPlane1->msk[1+j-pPlane1->x + (k-pPlane1->y)*pPlane1->w] == 1 ) ||
|
|
( pPlane1->msk[1+j-pPlane1->x + (1+k-pPlane1->y)*pPlane1->w] == 1 ) ||
|
|
( pPlane1->msk[j-pPlane1->x + (1+k-pPlane1->y)*pPlane1->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) )
|
|
{
|
|
// If all segment of the current face are linked with a face in this plane, no need to continue
|
|
vector<CMesh::CFace*>::iterator ItParseM = pPlane1->faces.begin();
|
|
sint32 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 *pPlane2 = *ItParsePlanes;
|
|
if( pPlane2 != pPlane1 )
|
|
for( n = 0; n < pPlane2->faces.size(); ++n )
|
|
{
|
|
CMesh::CFace *pF2 = pPlane2->faces[n];
|
|
if( FaceContinuous( pF1, pF2 ) )
|
|
{
|
|
for( sint32 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( ( pPlane1->msk[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w] == 1 ) &&
|
|
(nAbsX >= pPlane2->x) && (nAbsX < (pPlane2->x+pPlane2->w) ) &&
|
|
(nAbsY >= pPlane2->y) && (nAbsY < (pPlane2->y+pPlane2->h) ) )
|
|
{
|
|
// Is it an interior calculated pixel ?
|
|
if( pPlane2->msk[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w] == 2 )
|
|
{ // Yes -> ok so get it
|
|
pPlane1->col[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w].p[nLayerNb] =
|
|
pPlane2->col[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w].p[nLayerNb];
|
|
pPlane1->msk[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w] = 3;
|
|
}
|
|
else
|
|
if( pPlane2->msk[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w] == 1 )
|
|
{ // No -> Add extrapolated value
|
|
CVector iv = CalculateInterpolatedVertex( g, ((double)nAbsX)+0.5, ((double)nAbsY)+0.5);
|
|
CVector in = CalculateInterpolatedNormal( g, ((double)nAbsX)+0.5, ((double)nAbsY)+0.5);
|
|
CVector rv = CalcInterpolatedVertexInFace( g, ((double)nAbsX)+0.5, ((double)nAbsY)+0.5, pF1 );
|
|
CRGBAF col = LightAVertex( rv, iv, in, vLights, AllLights, wrt, doubleSided );
|
|
//float f = 1.0f;
|
|
pPlane2->col[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w].p[nLayerNb].R += col.R;
|
|
pPlane2->col[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w].p[nLayerNb].G += col.G;
|
|
pPlane2->col[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w].p[nLayerNb].B += col.B;
|
|
pPlane2->col[nAbsX-pPlane2->x + (nAbsY-pPlane2->y)*pPlane2->w].p[nLayerNb].A += 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++ItParsePlanes;
|
|
}
|
|
|
|
for( sint32 o = 0; o < 4; ++o )
|
|
{
|
|
sint32 nAbsX = j + (o/2), nAbsY = k + (o%2);
|
|
if( pPlane1->msk[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w] == 1 )
|
|
{
|
|
CVector iv = CalculateInterpolatedVertex( g, ((double)nAbsX)+0.5, ((double)nAbsY)+0.5);
|
|
CVector in = CalculateInterpolatedNormal( g, ((double)nAbsX)+0.5, ((double)nAbsY)+0.5);
|
|
CVector rv = CalcInterpolatedVertexInFace( g, ((double)nAbsX)+0.5, ((double)nAbsY)+0.5, pF1 );
|
|
CRGBAF col = LightAVertex( rv, iv, in, vLights, AllLights, wrt, doubleSided );
|
|
//float f = 1.0f;
|
|
pPlane1->col[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w].p[nLayerNb].R += col.R;
|
|
pPlane1->col[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w].p[nLayerNb].G += col.G;
|
|
pPlane1->col[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w].p[nLayerNb].B += col.B;
|
|
pPlane1->col[nAbsX-pPlane1->x + (nAbsY-pPlane1->y)*pPlane1->w].p[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 )
|
|
{
|
|
SLMPlane *pPlane1 = *ItPlanes1;
|
|
|
|
for( k = 0; k < pPlane1->h; ++k )
|
|
for( j = 0; j < pPlane1->w; ++j )
|
|
{
|
|
if( pPlane1->msk[j+k*pPlane1->w] == 1 )
|
|
{
|
|
sint32 nNbNormals = pPlane1->col[j + k*pPlane1->w].p[nLayerNb].A;
|
|
pPlane1->col[j + k*pPlane1->w].p[nLayerNb].R /= nNbNormals;
|
|
pPlane1->col[j + k*pPlane1->w].p[nLayerNb].G /= nNbNormals;
|
|
pPlane1->col[j + k*pPlane1->w].p[nLayerNb].B /= nNbNormals;
|
|
pPlane1->col[j + k*pPlane1->w].p[nLayerNb].A = 1.0f;
|
|
pPlane1->msk[j + k*pPlane1->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( fabsf(TextureSurf) < 0.000001 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
CAABBox getMeshBBox( CMesh::CMeshBuild& rMB, bool bNeedToTransform )
|
|
{
|
|
CAABBox meshBox;
|
|
if( bNeedToTransform )
|
|
{
|
|
CMatrix MBMatrix = getObjectToWorldMatrix( &rMB );
|
|
|
|
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, CMesh::CMeshBuild &rMB )
|
|
{
|
|
CAABBox meshBox;
|
|
|
|
if( rSLB.Type == SLightBuild::LightAmbient )
|
|
return true;
|
|
meshBox = getMeshBBox( rMB, true );
|
|
return isLightCanCastShadowOnBox( rSLB, meshBox );
|
|
}
|
|
|
|
bool isInteractionLightMeshWithoutAmbient( SLightBuild &rSLB, CMesh::CMeshBuild &rMB )
|
|
{
|
|
CAABBox meshBox;
|
|
|
|
if( rSLB.Type == SLightBuild::LightAmbient )
|
|
return false;
|
|
meshBox = getMeshBBox( rMB, true );
|
|
return isLightCanCastShadowOnBox( rSLB, meshBox );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Get all lights that can cast shadows on the current mesh
|
|
void getLightInteract( CMesh::CMeshBuild* pMB, 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 ) )
|
|
{
|
|
// Is the light name already exist
|
|
for( j = 0; j < nNbGroup; ++j )
|
|
if( AllLights[vvLights[j].operator[](0)].GroupName == AllLights[i].GroupName )
|
|
break;
|
|
// The light name does not exist create a new group
|
|
if( ( j == nNbGroup ) && ( nNbGroup < 8 ) )
|
|
{
|
|
vvLights.push_back( vlbTmp ); // Static lighting
|
|
vvLights[nNbGroup].push_back( i );
|
|
++nNbGroup;
|
|
}
|
|
else
|
|
{ // The light name already exist or there is not enought groups
|
|
if( j == nNbGroup )
|
|
j = nNbGroup - 1;
|
|
vvLights[j].push_back( i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void GetAllNodeInScene( vector< CMesh::CMeshBuild* > &Meshes, vector<SLightBuild> &AllLights, INode* pNode = NULL )
|
|
{
|
|
if( pNode == NULL )
|
|
pNode = theCNelExport.ip->GetRootNode();
|
|
|
|
// Get a pointer on the object's node
|
|
TimeValue tvTime = theCNelExport.ip->GetTime();
|
|
|
|
if( ! RPO::isZone( *pNode, tvTime ) )
|
|
if( CExportNel::isMesh( *pNode, tvTime ) )
|
|
{
|
|
CMesh::CMeshBuild *pMB;
|
|
pMB = CExportNel::createMeshBuild( *pNode, tvTime );
|
|
// If the mesh has no interaction with one of the light selected we do not need it
|
|
bool bInteract = false;
|
|
for( uint32 i = 0; i < AllLights.size(); ++i )
|
|
if( isInteractionLightMeshWithoutAmbient( AllLights[i], *pMB ) )
|
|
{
|
|
bInteract = true;
|
|
break;
|
|
}
|
|
if( bInteract )
|
|
Meshes.push_back( pMB );
|
|
else
|
|
delete pMB; // No interaction so delete the mesh
|
|
}
|
|
|
|
for( sint32 i = 0; i < pNode->NumberOfChildren(); ++i )
|
|
GetAllNodeInScene( Meshes, AllLights, pNode->GetChildNode(i) );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void buildWorldRT( SWorldRT &wrt, vector<SLightBuild> &AllLights )
|
|
{
|
|
uint32 i, j, k;
|
|
|
|
// Get all the nodes in the scene
|
|
GetAllNodeInScene( wrt.vMB, AllLights );
|
|
|
|
// Transform the meshbuilds vertices and normals to have world coordinates
|
|
for( i = 0; i < wrt.vMB.size(); ++i )
|
|
{
|
|
CMatrix MBMatrix = getObjectToWorldMatrix( wrt.vMB[i] );
|
|
// Update vertices
|
|
for( j = 0; j < wrt.vMB[i]->Vertices.size(); ++j )
|
|
wrt.vMB[i]->Vertices[j] = MBMatrix * wrt.vMB[i]->Vertices[j];
|
|
// Update normals
|
|
MBMatrix.invert();
|
|
MBMatrix.transpose();
|
|
for( j = 0; j < wrt.vMB[i]->Faces.size(); ++j )
|
|
for( k = 0; k < 3 ; ++k )
|
|
wrt.vMB[i]->Faces[j].Corner[k].Normal =
|
|
MBMatrix.mulVector( wrt.vMB[i]->Faces[j].Corner[k].Normal );
|
|
}
|
|
|
|
// Construct all cube grids from all lights
|
|
wrt.cgAccel.resize( AllLights.size() );
|
|
for( i = 0; i < AllLights.size(); ++i )
|
|
{
|
|
wrt.cgAccel[i].create( 64 ); // width of each grid in number of square
|
|
switch( AllLights[i].Type )
|
|
{
|
|
case SLightBuild::LightAmbient:
|
|
// No ambient handled for the moment
|
|
break;
|
|
case SLightBuild::LightSpot: // For the moment spot like point
|
|
case SLightBuild::LightPoint:
|
|
{
|
|
for( j = 0; j < wrt.vMB.size(); ++j )
|
|
{
|
|
for( k = 0; k < wrt.vMB[j]->Faces.size(); ++k )
|
|
{
|
|
SCubeGridCell cell;
|
|
cell.pF = &(wrt.vMB[j]->Faces[k]);
|
|
cell.pMB = wrt.vMB[j];
|
|
CTriangle tri = CTriangle(
|
|
cell.pMB->Vertices[cell.pF->Corner[0].Vertex] - AllLights[i].Position,
|
|
cell.pMB->Vertices[cell.pF->Corner[1].Vertex] - AllLights[i].Position,
|
|
cell.pMB->Vertices[cell.pF->Corner[2].Vertex] - AllLights[i].Position );
|
|
if( intersectionTriangleSphere( tri, CBSphere(CVector(0,0,0), AllLights[i].rRadiusMax) ) )
|
|
wrt.cgAccel[i].insert( tri, cell );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SLightBuild::LightDir:
|
|
// No directionnal handled for the moment
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Construct the aabboxes of the buildmeshes
|
|
/* wrt.bbBoxes.resize( wrt.vMB.size() );
|
|
for( i = 0; i < wrt.vMB.size(); ++i )
|
|
{
|
|
wrt.bbBoxes[i] = getMeshBBox( *wrt.vMB[i], false );
|
|
} */
|
|
|
|
/* // Construct the quad tree for each mesh
|
|
wrt.qtAccel.resize( wrt.vMB.size() );
|
|
for( i = 0; i < wrt.vMB.size(); ++i )
|
|
{
|
|
wrt.qtAccel[i].create( 4, wrt.bbBoxes[i].getCenter(), max(wrt.bbBoxes[i].getSize().x, wrt.bbBoxes[i].getSize().y) );
|
|
|
|
// Put the quadtree in the XY basis
|
|
CMatrix tmp;
|
|
CVector I( 1, 0, 0 );
|
|
CVector J( 0, 0, -1 );
|
|
CVector K( 0, 1, 0 );
|
|
tmp.identity();
|
|
tmp.setRot( I, J, K, true );
|
|
wrt.qtAccel[i].changeBase( tmp );
|
|
|
|
for( j = 0; j < wrt.vMB[i]->Faces.size(); ++j )
|
|
{
|
|
SWorldRTCell cell;
|
|
CAABBox bbFace;
|
|
cell.pF = &(wrt.vMB[i]->Faces[j]);
|
|
cell.pMB = wrt.vMB[i];
|
|
bbFace.setCenter( cell.pMB->Vertices[cell.pF->Corner[0].Vertex] );
|
|
bbFace.extend( cell.pMB->Vertices[cell.pF->Corner[1].Vertex] );
|
|
bbFace.extend( cell.pMB->Vertices[cell.pF->Corner[2].Vertex] );
|
|
wrt.qtAccel[i].insert( bbFace.getMin(), bbFace.getMax(), cell );
|
|
}
|
|
} */
|
|
|
|
/* // Construct the bsp tree for each mesh
|
|
wrt.btAccel.resize( wrt.vMB.size() );
|
|
for( i = 0; i < wrt.vMB.size(); ++i )
|
|
{
|
|
for( j = 0; j < wrt.vMB[i]->Faces.size(); ++j )
|
|
{
|
|
SWorldRTCell cell;
|
|
CTriangle tri;
|
|
cell.pF = &(wrt.vMB[i]->Faces[j]);
|
|
cell.pMB = wrt.vMB[i];
|
|
tri.V0 = cell.pMB->Vertices[cell.pF->Corner[0].Vertex];
|
|
tri.V1 = cell.pMB->Vertices[cell.pF->Corner[1].Vertex];
|
|
tri.V2 = cell.pMB->Vertices[cell.pF->Corner[2].Vertex];
|
|
wrt.btAccel[i].insert( tri, cell );
|
|
}
|
|
sint32 nNbNode = wrt.btAccel[i].getNbNode();
|
|
} */
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// 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 supprLightNoInteract( vector<SLightBuild> &vLights,
|
|
vector< pair < CMesh::CMeshBuild*,INode* > > &AllSelectedMeshes )
|
|
{
|
|
uint32 i, j;
|
|
|
|
for( i = 0; i < vLights.size(); ++i )
|
|
{
|
|
bool bInteract = false;
|
|
|
|
for( j = 0; j < AllSelectedMeshes.size(); ++j )
|
|
if( isInteractionLightMesh( vLights[i], *AllSelectedMeshes[j].first ) )
|
|
{
|
|
bInteract = true;
|
|
break;
|
|
}
|
|
if( !bInteract )
|
|
{
|
|
// Suppress the light because it has no interaction with selected meshes
|
|
for( j = i; j < (vLights.size()-1); ++j )
|
|
vLights[j] = vLights[j+1];
|
|
vLights.resize(vLights.size()-1);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void supprLightNoInteractOne( vector<SLightBuild> &vLights, CMesh::CMeshBuild* pMB)
|
|
{
|
|
uint32 i, j;
|
|
|
|
for( i = 0; i < vLights.size(); ++i )
|
|
{
|
|
bool bInteract = false;
|
|
|
|
if( isInteractionLightMesh( vLights[i], *pMB ) )
|
|
{
|
|
bInteract = true;
|
|
break;
|
|
}
|
|
if( !bInteract )
|
|
{
|
|
// Suppress the light because it has no interaction with selected meshes
|
|
for( j = i; j < (vLights.size()-1); ++j )
|
|
vLights[j] = vLights[j+1];
|
|
vLights.resize(vLights.size()-1);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Construct the world accelerator for raytrace
|
|
void buildWorldRTAccel( SWorldRT &wrt, CMesh::CMeshBuild* pMB, vector< vector<sint32> > &vvLights )
|
|
{
|
|
sint32 i, j, k;
|
|
// Determine all meshes that can cast shadows on the current mesh with current lights
|
|
/*
|
|
vector<sint32> allmesh;
|
|
CAABBox curBBox = getMeshBBox( *pMB, true );
|
|
for( j = 0; j < wrt.vMB.size(); ++j )
|
|
{
|
|
for( k = 0; k < vvLights.size(); ++k )
|
|
{
|
|
vector<SLightBuild*> &vLights = vvLights[k];
|
|
for( i = 0; i < vLights.size(); ++i )
|
|
{
|
|
if( isBoxCanCastShadowOnBoxWithLight( wrt.bbBoxes[j] , curBBox, *vLights[i] ) )
|
|
{
|
|
allmesh.push_back( j );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the container
|
|
wrt.qtAccel.clear();
|
|
// Construct the accel
|
|
|
|
CAABBox bbWorld;
|
|
sint32 nNbVertices = 0;
|
|
// Get the BBox of all meshes that can cast shadow on the current mesh
|
|
for( i = 0; i < allmesh.size(); ++i )
|
|
{
|
|
nNbVertices += wrt.vMB[allmesh[i]]->Vertices.size();
|
|
for( j = 0; j < wrt.vMB[allmesh[i]]->Vertices.size(); ++j )
|
|
if( (j == 0) && ( i == 0 ) )
|
|
bbWorld.setCenter( wrt.vMB[allmesh[i]]->Vertices[j] );
|
|
else
|
|
bbWorld.extend( wrt.vMB[allmesh[i]]->Vertices[j] );
|
|
}
|
|
|
|
wrt.qtAccel.create( 9, bbWorld.getCenter(), max(bbWorld.getSize().x,bbWorld.getSize().y) );
|
|
|
|
// Put the quadtree in the XY basis
|
|
CMatrix tmp;
|
|
CVector I( 1, 0, 0 );
|
|
CVector J( 0, 0, -1 );
|
|
CVector K( 0, 1, 0 );
|
|
tmp.identity();
|
|
tmp.setRot( I, J, K, true );
|
|
wrt.qtAccel.changeBase( tmp );
|
|
|
|
for( i = 0; i < allmesh.size(); ++i )
|
|
{
|
|
for( j = 0; j < wrt.vMB[allmesh[i]]->Faces.size(); ++j )
|
|
{
|
|
SWorldRTCell cell;
|
|
CAABBox bbFace;
|
|
cell.pF = &(wrt.vMB[allmesh[i]]->Faces[j]);
|
|
cell.pMB = wrt.vMB[allmesh[i]];
|
|
bbFace.setCenter( cell.pMB->Vertices[cell.pF->Corner[0].Vertex] );
|
|
bbFace.extend( cell.pMB->Vertices[cell.pF->Corner[1].Vertex] );
|
|
bbFace.extend( cell.pMB->Vertices[cell.pF->Corner[2].Vertex] );
|
|
wrt.qtAccel.insert( bbFace.getMin(), bbFace.getMax(), cell );
|
|
}
|
|
}*/
|
|
}
|
|
|
|
volatile bool bCancelCalculation = false;
|
|
float gRatioCalculated;
|
|
DWORD gTimeBegin;
|
|
|
|
int CALLBACK CalculatingDialogCallback (
|
|
HWND hwndDlg, // handle to dialog box
|
|
UINT uMsg, // message
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam // second message parameter
|
|
)
|
|
{
|
|
DWORD TimeCurrent = timeGetTime();
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
CenterWindow( hwndDlg, theCNelExport.ip->GetMAXHWnd() );
|
|
ShowWindow( hwndDlg, SW_SHOWNORMAL );
|
|
gRatioCalculated = 0.0;
|
|
gTimeBegin = timeGetTime();
|
|
bCancelCalculation = false;
|
|
}
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
char temp[256];
|
|
SendMessage( GetDlgItem( hwndDlg, IDC_PROGRESS1 ), PBM_SETPOS, gRatioCalculated*100, 0 );
|
|
|
|
if( gRatioCalculated > 0.0 )
|
|
{
|
|
DWORD TimeLeft = ((TimeCurrent - gTimeBegin) / gRatioCalculated) * (1.0-gRatioCalculated);
|
|
sprintf( temp, "Time remaining : %02d h %02d m %02d s", TimeLeft/3600000,
|
|
(TimeLeft/60000)%60,
|
|
(TimeLeft/1000)%60 );
|
|
|
|
SendMessage( GetDlgItem( hwndDlg, IDC_STATICTIMELEFT ), WM_SETTEXT, 0, (long)temp );
|
|
SendMessage( GetDlgItem( hwndDlg, IDC_BUTTONCANCEL ), WM_PAINT, 0, 0 );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
bCancelCalculation = true;
|
|
break;
|
|
case WM_COMMAND:
|
|
{
|
|
switch( LOWORD(wParam) )
|
|
{
|
|
// ---
|
|
case IDC_BUTTONCANCEL:
|
|
if( HIWORD(wParam) == BN_CLICKED )
|
|
bCancelCalculation = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
void AddLightInfo( CMesh::CMeshBuild *pMB, string &LightName, uint8 nMatNb, uint8 nStageNb )
|
|
{
|
|
CMesh::CMatStage ms;
|
|
ms.nMatNb = nMatNb;
|
|
ms.nStageNb = nStageNb;
|
|
CMesh::CLightInfoMapList listTemp;
|
|
//list< pair< uint8, uint8 > > listTemp;
|
|
CMesh::TLightInfoMap::iterator itMap = pMB->LightInfoMap.find( LightName );
|
|
if( itMap == pMB->LightInfoMap.end() )
|
|
{
|
|
listTemp.push_back( ms );
|
|
pMB->LightInfoMap.insert( pair< string, CMesh::CLightInfoMapList >(LightName, listTemp) );
|
|
}
|
|
else
|
|
{
|
|
itMap->second.push_back( ms );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
bool isAllBlack( SLMPlane &Plane, uint8 nLayerNb )
|
|
{
|
|
for( sint32 i = 0; i < Plane.w*Plane.h; ++i )
|
|
if( (Plane.col[i].p[nLayerNb].R > 0.06f) || // around 15/255
|
|
(Plane.col[i].p[nLayerNb].G > 0.06f) ||
|
|
(Plane.col[i].p[nLayerNb].B > 0.06f) )
|
|
return false; // Not all is black
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// -----------------------------------------------------------------------------------------------
|
|
// -----------------------------------------------------------------------------------------------
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
void CNelExport::deleteLM(INode& ZeNode)
|
|
{
|
|
sint32 i;
|
|
|
|
// Suppress all lightmap files
|
|
for( i = 0; i < 8; ++i )
|
|
{
|
|
string sSaveName;
|
|
sSaveName = theExportSceneStruct.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() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//bool CNelExport::exportMeshLM(const char *sPath, INode& ZeNode, Interface& ip, TimeValue tvTime)
|
|
bool CNelExport::calculateLM(CMesh::CMeshBuild *pZeMeshBuild, INode& ZeNode, Interface& ip, TimeValue tvTime, bool absolutePath)
|
|
{
|
|
// 1 -> Build all MeshBuild structure
|
|
// ----------------------------------
|
|
//vector< pair < CMesh::CMeshBuild*,INode* > > AllMeshBuilds;
|
|
int nNbMesh = 0;
|
|
int i, j;
|
|
/*
|
|
if( RPO::isZone( ZeNode, tvTime ) )
|
|
return false;
|
|
if( ! CExportNel::isMesh( ZeNode, tvTime ) )
|
|
return false;
|
|
|
|
pZeMeshBuild = CExportNel::createMeshBuild( ZeNode, tvTime );
|
|
*/
|
|
// 2 -> LightMapping V2
|
|
// --------------------
|
|
SWorldRT WorldRT; // The static world for raytrace
|
|
vector<SLightBuild> AllLights;
|
|
|
|
// Select meshes to test for raytrace
|
|
// Get all lights from MAX
|
|
getLightBuilds( AllLights, tvTime, *theCNelExport.ip );
|
|
// Get all lights L that have influence over the mesh selected
|
|
// supprLightNoInteract( AllLights, AllMeshBuilds );
|
|
supprLightNoInteractOne( AllLights, pZeMeshBuild );
|
|
// Get all meshes that are influenced by the lights L
|
|
buildWorldRT( WorldRT, AllLights );
|
|
|
|
//for( nNode=0; nNode < nNbMesh; ++nNode )
|
|
{
|
|
// First order face by Material and by texture surface
|
|
CMesh::CMeshBuild *pMB = pZeMeshBuild;
|
|
vector<CMesh::CFace*> AllFaces;
|
|
CMatrix MBMatrix = getObjectToWorldMatrix( pMB );
|
|
vector<CVector> AllVertices; // All vertices in world space
|
|
vector<sint32> FaceGroupByMat; // Number of faces with the same properties
|
|
sint32 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, AllLights, vvLights );
|
|
// Construct the raytrace accelerator from those lights
|
|
buildWorldRTAccel( WorldRT, pMB, vvLights );
|
|
|
|
//gRatioCalculated = ((float)nNode) / ((float)nNbMesh);
|
|
//for( i = 0; i < 32; ++i )
|
|
//{
|
|
// MSG msg;
|
|
// PeekMessage(&msg,(HWND)hwndCalculating,0,0,PM_REMOVE);
|
|
// {
|
|
// if( IsDialogMessage(hwndCalculating,&msg) )
|
|
// {
|
|
// TranslateMessage(&msg);
|
|
// DispatchMessage(&msg);
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
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)
|
|
SortFaceByMaterialId( FaceGroupByMat, AllFaces.begin(), AllFaces.size() );
|
|
|
|
if( ! IsAllFaceMapped( AllFaces.begin(), AllFaces.size() ) )
|
|
{
|
|
string thetext;
|
|
//thetext = "Object ";
|
|
thetext += ZeNode.GetName();
|
|
thetext = "have not all this faces mapped";
|
|
MessageBox( NULL, thetext.c_str(), "Warning", MB_OK|MB_ICONERROR );
|
|
return false;
|
|
}
|
|
|
|
// PATCH
|
|
FaceGroupByMat.resize(1);
|
|
FaceGroupByMat[0] = AllFaces.size();
|
|
|
|
|
|
|
|
offsetMat = 0;
|
|
for( uint32 nMat = 0; nMat < FaceGroupByMat.size(); ++nMat )
|
|
{
|
|
vector<sint32> FaceGroupBySmooth;
|
|
|
|
// Sort faces by smoothing group
|
|
SortFaceBySMoothGroup( FaceGroupBySmooth, AllFaces.begin()+offsetMat, FaceGroupByMat[nMat] );
|
|
|
|
offsetSmooth = offsetMat;
|
|
for( uint32 nSmoothNb = 0; nSmoothNb < FaceGroupBySmooth.size(); ++nSmoothNb )
|
|
{
|
|
uint32 nPlaneNb, nLight;
|
|
vector<sint32> FaceGroupByPlane;
|
|
|
|
if( ! PutFaceUV1InLumelCoord( theExportSceneStruct.rLumelSize, AllVertices,
|
|
AllFaces.begin()+offsetSmooth, FaceGroupBySmooth[nSmoothNb] ) )
|
|
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 < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
{
|
|
AllPlanes[AllPlanesPrevSize+nPlaneNb] = new SLMPlane;
|
|
AllPlanes[AllPlanesPrevSize+nPlaneNb]->nNbLayerUsed = vvLights.size();
|
|
// Fill planes (part of lightmap)
|
|
CreateLMPlaneFromFaceGroup( *AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
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 < vvLights.size(); ++nLight )
|
|
{
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
{
|
|
// Light the LightMap for the plane (interior only)
|
|
FirstLight( pMB, *AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
}
|
|
SecondLight( pMB, AllPlanes.begin()+AllPlanesPrevSize, FaceGroupByPlane.size(),
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
}
|
|
if( theExportSceneStruct.nOverSampling > 1 )
|
|
{
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
ModifyLMPlaneWithOverSampling( AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
theExportSceneStruct.nOverSampling );
|
|
for( nLight = 0; nLight < vvLights.size(); ++nLight )
|
|
{
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
FirstLight( pMB, *AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
SecondLight( pMB, AllPlanes.begin()+AllPlanesPrevSize, FaceGroupByPlane.size(),
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
}
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
ModifyLMPlaneWithOverSampling( AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
1.0/((double)theExportSceneStruct.nOverSampling) );
|
|
}
|
|
// 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
|
|
|
|
SLMPlane LightMap;
|
|
SortPlanesBySurface( AllPlanes );
|
|
for( i = 0; i < AllPlanes.size(); ++i )
|
|
{
|
|
// 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
|
|
|
|
// Update UV coords to Texture space
|
|
PutFaceUV1InTextureCoord( LightMap.w, LightMap.h, AllFaces.begin(), nNbFace );
|
|
sint32 nLightMapNb = 0;
|
|
for( j = 0; j < LightMap.nNbLayerUsed; ++j )
|
|
if( ! isAllBlack( LightMap, j ) )
|
|
{
|
|
CTextureFile *pLightMap = new CTextureFile();
|
|
//string sSaveName = AllMeshBuilds[nNode].second->GetName();
|
|
string sSaveName = ZeNode.GetName();
|
|
char tmp[32];
|
|
sprintf( tmp, "%d", nLightMapNb );
|
|
sSaveName += tmp;
|
|
sSaveName += ".tga";
|
|
pLightMap->setFileName( sSaveName );
|
|
sSaveName = theExportSceneStruct.sExportLighting;
|
|
if( sSaveName[sSaveName.size()-1] != '\\' ) sSaveName += "\\";
|
|
sSaveName += pLightMap->getFileName();
|
|
CopyPlaneColToBitmap32( pLightMap, LightMap, j );
|
|
COFile f( sSaveName );
|
|
pLightMap->writeTGA( f, 32 );
|
|
|
|
for( i = 0; i < pMB->Materials.size(); ++i )
|
|
{
|
|
pMB->Materials[i].setLightMap( nLightMapNb, pLightMap );
|
|
//AllMeshBuilds[nNode].first->Materials[i].setLighting( false );
|
|
AddLightInfo( pMB, AllLights[vvLights[j].operator[](0)].GroupName, i, nLightMapNb );
|
|
int a = pMB->LightInfoMap.size();
|
|
}
|
|
++nLightMapNb;
|
|
}
|
|
// Next mesh
|
|
|
|
//if( bCancelCalculation )
|
|
// break;
|
|
}
|
|
for( i = 0; i < WorldRT.vMB.size(); ++i )
|
|
delete WorldRT.vMB[i];
|
|
|
|
// End of the lighting process for this node we have to export the data
|
|
//for( nNode=0; nNode < nNbMesh; ++nNode )
|
|
{
|
|
// First order face by Material and by texture surface
|
|
//CMesh::CMeshBuild *pMB = AllMeshBuilds[nNode].first;
|
|
CMesh::CMeshBuild *pMB = pZeMeshBuild;
|
|
CMesh* mesh = new CMesh;
|
|
pMB->VertexFlags |= CVertexBuffer::TexCoord1Flag;
|
|
// Build the mesh with the build interface
|
|
|
|
for( i = 0; i < pMB->Materials.size(); ++i )
|
|
{
|
|
pMB->Materials[i].setLighting( false );
|
|
pMB->Materials[i].setColor( CRGBA(255,255,255,255) );
|
|
}
|
|
/*
|
|
mesh->build( *pMB );
|
|
|
|
COFile file;
|
|
if (file.open( sPath ))
|
|
{
|
|
try
|
|
{
|
|
// Create a streamable shape
|
|
CShapeStream shapeStream( mesh );
|
|
// Serial the shape
|
|
shapeStream.serial (file);
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
// Delete the pointer
|
|
delete mesh;
|
|
*/
|
|
}
|
|
// Delete the window
|
|
//DestroyWindow( hwndCalculating );
|
|
|
|
// ? -> Ending deletion des objets temporaires
|
|
//for( nNode = 0; nNode < nNbMesh; ++nNode )
|
|
// delete AllMeshBuilds[nNode].first;
|
|
//delete pZeMeshBuild;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CNelExport::exportScene(std::vector<INode*>& vectNode)
|
|
{
|
|
// All inputs are transfered throught the global theExportSceneStruct structure
|
|
|
|
// Export the Instance Group (aka scene description)
|
|
// *************************
|
|
if( theExportSceneStruct.bExportInstanceGroup )
|
|
exportInstanceGroup( theExportSceneStruct.sExportInstanceGroup, vectNode );
|
|
|
|
// Export all the shapes
|
|
// *********************
|
|
// If lighting enabled we export the shape after lightmapping modification
|
|
if( theExportSceneStruct.bExportShapes && (!theExportSceneStruct.bExportLighting) )
|
|
{
|
|
// Get time
|
|
TimeValue tvTime = theCNelExport.ip->GetTime();
|
|
|
|
// Get node count
|
|
int nNumSelNode = vectNode.size();
|
|
|
|
// Save all selected objects
|
|
for (int nNode=0; nNode<nNumSelNode; nNode++)
|
|
{
|
|
// Get the node
|
|
INode* pNode=vectNode[nNode];
|
|
|
|
// It is a zone ?
|
|
if (RPO::isZone (*pNode, tvTime))
|
|
{
|
|
// Nothing to do if this is a zone
|
|
}
|
|
// Try to export a mesh
|
|
else if (CExportNel::isMesh (*pNode, tvTime))
|
|
{
|
|
// Build the name of the file to export
|
|
char sSavePath[512];
|
|
strcpy( sSavePath, theExportSceneStruct.sExportShapes.c_str() );
|
|
if(theExportSceneStruct.sExportShapes[theExportSceneStruct.sExportShapes.size()-1] != '\\' )
|
|
strcat( sSavePath, "\\" );
|
|
strcat( sSavePath, pNode->GetName() );
|
|
strcat( sSavePath, ".shape" );
|
|
|
|
// Export the mesh
|
|
if (!theCNelExport.exportMesh (sSavePath, *pNode, *theCNelExport.ip, tvTime))
|
|
{
|
|
// Error message
|
|
char sErrorMsg[512];
|
|
sprintf (sErrorMsg, "Error exporting the mesh %s in the file\n%s", pNode->GetName(), sSavePath);
|
|
MessageBox (theCNelExport.ip->GetMAXHWnd(), sErrorMsg, "NeL export scene", MB_OK|MB_ICONEXCLAMATION );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Export the lighting (aka compute all light maps)
|
|
// *******************
|
|
if( theExportSceneStruct.bExportLighting )
|
|
{
|
|
TTicks zeTime = CTime::getPerformanceTime();
|
|
// 1 -> Build all MeshBuild structure
|
|
// ----------------------------------
|
|
vector< pair < CMesh::CMeshBuild*,INode* > > AllMeshBuilds;
|
|
TimeValue tvTime = theCNelExport.ip->GetTime();
|
|
int nNumSelNode = vectNode.size();
|
|
int nNbMesh = 0;
|
|
int nNode, i, j;
|
|
|
|
|
|
// Launch the dialog box
|
|
bCancelCalculation = true;
|
|
HWND hwndCalculating = CreateDialog( CNelExportDesc.HInstance(),
|
|
MAKEINTRESOURCE(IDD_CALCULATING),
|
|
theCNelExport.ip->GetMAXHWnd(),
|
|
CalculatingDialogCallback );
|
|
|
|
// Count number of mesh in the selection
|
|
for( nNode = 0; nNode < nNumSelNode; ++nNode )
|
|
{
|
|
INode* pNode = vectNode[nNode];
|
|
if( ! RPO::isZone( *pNode, tvTime ) )
|
|
if( CExportNel::isMesh( *pNode, tvTime ) )
|
|
nNbMesh++;
|
|
}
|
|
AllMeshBuilds.resize(nNbMesh);
|
|
nNbMesh = 0;
|
|
// Create all the MeshBuild used to place lightmaps and to raytrace lightmaps
|
|
for( nNode=0; nNode < nNumSelNode; ++nNode )
|
|
{
|
|
INode* pNode = vectNode[nNode];
|
|
if( ! RPO::isZone( *pNode, tvTime ) )
|
|
if( CExportNel::isMesh( *pNode, tvTime ) )
|
|
{
|
|
AllMeshBuilds[nNbMesh].second = pNode;
|
|
AllMeshBuilds[nNbMesh].first = CExportNel::createMeshBuild( *pNode, tvTime );
|
|
nNbMesh++;
|
|
}
|
|
}
|
|
|
|
// Suppress all lightmap files
|
|
for( nNode=0; nNode < nNbMesh; ++nNode )
|
|
for( i = 0; i < 8; ++i )
|
|
{
|
|
string sSaveName;
|
|
sSaveName = theExportSceneStruct.sExportLighting;
|
|
if( sSaveName[sSaveName.size()-1] != '\\' ) sSaveName += "\\";
|
|
sSaveName += AllMeshBuilds[nNode].second->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() );
|
|
}
|
|
}
|
|
|
|
|
|
// 2 -> LightMapping V2
|
|
// --------------------
|
|
SWorldRT WorldRT; // The static world for raytrace
|
|
// vector< pair < CMesh::CMeshBuild*, CBSphere > > MeshBuildForRaytrace;
|
|
vector<SLightBuild> AllLights;
|
|
|
|
// Select meshes to test for raytrace
|
|
// Get all lights from MAX
|
|
getLightBuilds( AllLights, tvTime, *theCNelExport.ip );
|
|
// Get all lights L that have influence over the mesh selected
|
|
supprLightNoInteract( AllLights, AllMeshBuilds );
|
|
// Get all meshes that are influenced by the lights L
|
|
buildWorldRT( WorldRT, AllLights );
|
|
|
|
for( nNode=0; nNode < nNbMesh; ++nNode )
|
|
{
|
|
// First order face by Material and by texture surface
|
|
CMesh::CMeshBuild *pMB = AllMeshBuilds[nNode].first;
|
|
vector<CMesh::CFace*> AllFaces;
|
|
CMatrix MBMatrix = getObjectToWorldMatrix( pMB );
|
|
vector<CVector> AllVertices; // All vertices in world space
|
|
vector<sint32> FaceGroupByMat; // Number of faces with the same properties
|
|
sint32 nNbFace = pMB->Faces.size(), nNbVertex = pMB->Vertices.size();
|
|
sint32 offsetMat, offsetSmooth, offsetPlane;
|
|
vector<SLMPlane*> AllPlanes;
|
|
sint32 AllPlanesPrevSize;
|
|
vector< vector<sint32> > vvLights;
|
|
|
|
TTicks zeTime2 = CTime::getPerformanceTime();
|
|
|
|
// Select Lights interacting with the node
|
|
getLightInteract( pMB, AllLights, vvLights );
|
|
// Construct the raytrace accelerator from those lights
|
|
buildWorldRTAccel( WorldRT, pMB, vvLights );
|
|
|
|
gRatioCalculated = ((float)nNode) / ((float)nNbMesh);
|
|
|
|
for( i = 0; i < 32; ++i )
|
|
{
|
|
MSG msg;
|
|
PeekMessage(&msg,(HWND)hwndCalculating,0,0,PM_REMOVE);
|
|
{
|
|
if( IsDialogMessage(hwndCalculating,&msg) )
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
SortFaceByMaterialId( FaceGroupByMat, AllFaces.begin(), AllFaces.size() );
|
|
|
|
if( ! IsAllFaceMapped( AllFaces.begin(), AllFaces.size() ) )
|
|
{
|
|
string thetext;
|
|
//thetext = "Object ";
|
|
//thetext += AllMeshBuilds[nNode].second->GetName();
|
|
//thetext = "have not all this faces mapped";
|
|
//MessageBox( NULL, thetext.c_str(), "Warning", MB_OK );
|
|
continue;
|
|
}
|
|
|
|
// PATCH
|
|
FaceGroupByMat.resize(1);
|
|
FaceGroupByMat[0] = AllFaces.size();
|
|
|
|
timerInit += CTime::getPerformanceTime() - zeTime2;
|
|
|
|
zeTime2 = CTime::getPerformanceTime();
|
|
offsetMat = 0;
|
|
for( uint32 nMat = 0; nMat < FaceGroupByMat.size(); ++nMat )
|
|
{
|
|
vector<sint32> FaceGroupBySmooth;
|
|
|
|
// Sort faces by smoothing group
|
|
SortFaceBySMoothGroup( FaceGroupBySmooth, AllFaces.begin()+offsetMat, FaceGroupByMat[nMat] );
|
|
|
|
offsetSmooth = offsetMat;
|
|
for( uint32 nSmoothNb = 0; nSmoothNb < FaceGroupBySmooth.size(); ++nSmoothNb )
|
|
{
|
|
uint32 nPlaneNb, nLight;
|
|
vector<sint32> FaceGroupByPlane;
|
|
|
|
if( ! PutFaceUV1InLumelCoord( theExportSceneStruct.rLumelSize, AllVertices,
|
|
AllFaces.begin()+offsetSmooth, FaceGroupBySmooth[nSmoothNb] ) )
|
|
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 < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
{
|
|
AllPlanes[AllPlanesPrevSize+nPlaneNb] = new SLMPlane;
|
|
AllPlanes[AllPlanesPrevSize+nPlaneNb]->nNbLayerUsed = vvLights.size();
|
|
// Fill planes (part of lightmap)
|
|
CreateLMPlaneFromFaceGroup( *AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
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 < vvLights.size(); ++nLight )
|
|
{
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
{
|
|
// Light the LightMap for the plane (interior only)
|
|
FirstLight( pMB, *AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
}
|
|
SecondLight( pMB, AllPlanes.begin()+AllPlanesPrevSize, FaceGroupByPlane.size(),
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
}
|
|
if( theExportSceneStruct.nOverSampling > 1 )
|
|
{
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
ModifyLMPlaneWithOverSampling( AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
theExportSceneStruct.nOverSampling );
|
|
for( nLight = 0; nLight < vvLights.size(); ++nLight )
|
|
{
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
FirstLight( pMB, *AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
SecondLight( pMB, AllPlanes.begin()+AllPlanesPrevSize, FaceGroupByPlane.size(),
|
|
AllVertices, MBMatrix, vvLights[nLight], AllLights,
|
|
nLight, WorldRT );
|
|
}
|
|
for( nPlaneNb = 0; nPlaneNb < FaceGroupByPlane.size(); ++nPlaneNb )
|
|
ModifyLMPlaneWithOverSampling( AllPlanes[AllPlanesPrevSize+nPlaneNb],
|
|
1.0/((double)theExportSceneStruct.nOverSampling) );
|
|
}
|
|
// 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];
|
|
}
|
|
timerCalc += CTime::getPerformanceTime() - zeTime2;
|
|
// Create the lightmap
|
|
zeTime2 = CTime::getPerformanceTime();
|
|
SLMPlane LightMap;
|
|
SortPlanesBySurface( AllPlanes );
|
|
for( i = 0; i < AllPlanes.size(); ++i )
|
|
{
|
|
// 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];
|
|
}
|
|
timerPlac += CTime::getPerformanceTime() - zeTime2;
|
|
// Save the lightmap
|
|
// Assign the name of the lightmap and get the complete save name
|
|
|
|
zeTime2 = CTime::getPerformanceTime();
|
|
// Update UV coords to Texture space
|
|
PutFaceUV1InTextureCoord( LightMap.w, LightMap.h, AllFaces.begin(), nNbFace );
|
|
sint32 nLightMapNb = 0;
|
|
for( j = 0; j < LightMap.nNbLayerUsed; ++j )
|
|
if( ! isAllBlack( LightMap, j ) )
|
|
{
|
|
CTextureFile *pLightMap = new CTextureFile();
|
|
string sSaveName = AllMeshBuilds[nNode].second->GetName();
|
|
char tmp[32];
|
|
sprintf( tmp, "%d", nLightMapNb );
|
|
sSaveName += tmp;
|
|
sSaveName += ".tga";
|
|
pLightMap->setFileName( sSaveName );
|
|
sSaveName = theExportSceneStruct.sExportLighting;
|
|
if( sSaveName[sSaveName.size()-1] != '\\' ) sSaveName += "\\";
|
|
sSaveName += pLightMap->getFileName();
|
|
CopyPlaneColToBitmap32( pLightMap, LightMap, j );
|
|
COFile f( sSaveName );
|
|
pLightMap->writeTGA( f, 32 );
|
|
|
|
for( i = 0; i < pMB->Materials.size(); ++i )
|
|
{
|
|
pMB->Materials[i].setLightMap( nLightMapNb, pLightMap );
|
|
//AllMeshBuilds[nNode].first->Materials[i].setLighting( false );
|
|
AddLightInfo( pMB, AllLights[vvLights[j].operator[](0)].GroupName, i, nLightMapNb );
|
|
int a = pMB->LightInfoMap.size();
|
|
}
|
|
++nLightMapNb;
|
|
}
|
|
timerSave += CTime::getPerformanceTime() - zeTime2;
|
|
// Next mesh
|
|
//delete pLightMap; // TODO vector<> de lightmap
|
|
if( bCancelCalculation )
|
|
break;
|
|
}
|
|
for( i = 0; i < WorldRT.vMB.size(); ++i )
|
|
delete WorldRT.vMB[i];
|
|
|
|
// End of the lighting process for this node we have to export the data
|
|
for( nNode=0; nNode < nNbMesh; ++nNode )
|
|
{
|
|
// First order face by Material and by texture surface
|
|
CMesh::CMeshBuild *pMB = AllMeshBuilds[nNode].first;
|
|
CMesh* mesh = new CMesh;
|
|
pMB->VertexFlags |= CVertexBuffer::TexCoord1Flag;
|
|
// Build the mesh with the build interface
|
|
|
|
for( i = 0; i < pMB->Materials.size(); ++i )
|
|
{
|
|
pMB->Materials[i].setLighting( false );
|
|
pMB->Materials[i].setColor( CRGBA(255,255,255,255) );
|
|
}
|
|
|
|
mesh->build( *pMB );
|
|
|
|
COFile file;
|
|
char sSavePath[512];
|
|
strcpy( sSavePath, theExportSceneStruct.sExportShapes.c_str() );
|
|
if(theExportSceneStruct.sExportShapes[theExportSceneStruct.sExportShapes.size()-1] != '\\' )
|
|
strcat( sSavePath, "\\" );
|
|
strcat( sSavePath, AllMeshBuilds[nNode].second->GetName() );
|
|
strcat( sSavePath, ".shape" );
|
|
if (file.open( sSavePath ))
|
|
{
|
|
try
|
|
{
|
|
// Create a streamable shape
|
|
CShapeStream shapeStream( mesh );
|
|
// Serial the shape
|
|
shapeStream.serial (file);
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
// Delete the pointer
|
|
delete mesh;
|
|
}
|
|
// Delete the window
|
|
DestroyWindow( hwndCalculating );
|
|
|
|
// ? -> Ending deletion des objets temporaires
|
|
for( nNode = 0; nNode < nNbMesh; ++nNode )
|
|
delete AllMeshBuilds[nNode].first;
|
|
|
|
timerExportLighting = CTime::getPerformanceTime() - zeTime;
|
|
{
|
|
string toDisp;
|
|
char tam[1024];
|
|
sprintf( tam, "timerExport = %f sec\n", (float)CTime::ticksToSecond( timerExportLighting ) );
|
|
toDisp += tam;
|
|
sprintf( tam, "timerInit = %f %%\n", 100*((float)timerInit)/((float)timerExportLighting) );
|
|
toDisp += tam;
|
|
sprintf( tam, "timerCalc = %f %%\n", 100*((float)timerCalc)/((float)timerExportLighting) );
|
|
toDisp += tam;
|
|
sprintf( tam, " timerCalcRT = %f %%\n", 100*((float)timerCalcRT)/((float)timerExportLighting) );
|
|
toDisp += tam;
|
|
sprintf( tam, "timerPlac = %f %%\n", 100*((float)timerPlac)/((float)timerExportLighting) );
|
|
toDisp += tam;
|
|
sprintf( tam, "timerSave = %f %%\n", 100*((float)timerSave)/((float)timerExportLighting) );
|
|
toDisp += tam;
|
|
MessageBox( theCNelExport.ip->GetMAXHWnd(), toDisp.c_str(), "Info", MB_OK );
|
|
}
|
|
|
|
} // End if lighting process asked
|
|
|
|
return true;
|
|
}
|