// Old Lightmap process void SortFaceByTextureName(vector &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(pT); string namei = "Default"; if( pTF != NULL ) namei = pTF->getFileName(); pT = pMB->Materials[AllFaces[j]->MaterialId].getTexture(0); pTF = dynamic_cast(pT); string namej = "Default"; if( pTF != NULL ) namej = pTF->getFileName(); if( namei < namej ) { CMesh::CFace *pFaceTemp = AllFaces[i]; AllFaces[i] = AllFaces[j]; AllFaces[j] = pFaceTemp; } } } // TextureNames is an array which indicates the number of faces that follows which have the same texture name void ComputeAreaOfTextureName(vector &TextureNames, vector &AllFaces, CMesh::CMeshBuild *pMB) { int i, nNbFace = AllFaces.size(); TextureNames.resize(nNbFace); ITexture *pT = pMB->Materials[AllFaces[0]->MaterialId].getTexture(0); CTextureFile *pTF = dynamic_cast(pT); string CurrentName = "Default"; sint32 lastface = 0, nNbTexName = 0; if( pTF != NULL ) CurrentName = pTF->getFileName(); for( i = 0; i < nNbFace; ++i ) { ITexture *pT = pMB->Materials[AllFaces[i]->MaterialId].getTexture(0); CTextureFile *pTF = dynamic_cast(pT); string namei = "Default"; if( pTF != NULL ) namei = pTF->getFileName(); if( ( namei != CurrentName ) || ( i == (nNbFace-1) ) ) { CurrentName = namei; TextureNames[nNbTexName] = i-lastface; nNbTexName++; lastface = i; } } TextureNames[nNbTexName-1] += 1; TextureNames.resize( nNbTexName ); } void SortFaceByMaterialId( int offset, int nNbFace, vector &AllFaces ) { int i, j; for( i = 0; i < nNbFace-1; ++i ) for( j = i+1; j < nNbFace; ++j ) if( AllFaces[offset+i]->MaterialId < AllFaces[offset+j]->MaterialId ) { CMesh::CFace *pFaceTemp = AllFaces[offset+i]; AllFaces[offset+i] = AllFaces[offset+j]; AllFaces[offset+j] = pFaceTemp; } } void SortFaceByTextureSurface( int offset, int nNbFace, vector &AllFaces ) { int i, j; for( i = 0; i < nNbFace-1; ++i ) for( j = i+1; j < nNbFace; ++j ) if( AllFaces[i]->MaterialId == AllFaces[j]->MaterialId ) { // Texture surface of the i face = .5*|(u1-u0)*(v2-v0)-(v1-v0)*(u2-u0)| // in fact this is lightmap mapping surface double surfacei = 0.5*fabs( (AllFaces[i]->Corner[1].Uvws[1].U - AllFaces[i]->Corner[0].Uvws[1].U)* (AllFaces[i]->Corner[2].Uvws[1].V - AllFaces[i]->Corner[0].Uvws[1].V)- (AllFaces[i]->Corner[1].Uvws[1].V - AllFaces[i]->Corner[0].Uvws[1].V)* (AllFaces[i]->Corner[2].Uvws[1].U - AllFaces[i]->Corner[0].Uvws[1].U) ); double surfacej = 0.5*fabs( (AllFaces[j]->Corner[1].Uvws[1].U - AllFaces[j]->Corner[0].Uvws[1].U)* (AllFaces[j]->Corner[2].Uvws[1].V - AllFaces[j]->Corner[0].Uvws[1].V)- (AllFaces[j]->Corner[1].Uvws[1].V - AllFaces[j]->Corner[0].Uvws[1].V)* (AllFaces[j]->Corner[2].Uvws[1].U - AllFaces[j]->Corner[0].Uvws[1].U) ); if( surfacei < surfacej ) { CMesh::CFace *pFaceTemp = AllFaces[i]; AllFaces[i] = AllFaces[j]; AllFaces[j] = pFaceTemp; } } } bool isInTriangle(double x, double y, double xt1, double yt1, double xt2, double yt2, double xt3, double yt3) { // Test vecteur T1X avec T1T2 double sign1 = ((xt2-xt1)*(y-yt1) - (yt2-yt1)*(x-xt1)); // Test vecteur T2X avec T2T3 double sign2 = ((xt3-xt2)*(y-yt2) - (yt3-yt2)*(x-xt2)); // Test vecteur T3X avec 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); 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; } void CreatePiece( vector& 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( isInTriangle(k+0.5,j+0.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) || isInTriangle(k+1.5,j+0.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) || isInTriangle(k+0.5,j+1.5,lumx1,lumy1,lumx2,lumy2,lumx3,lumy3) || isInTriangle(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 &vBitmap, sint32 &nSizeX, sint32 &nSizeY, sint32 nNewSizeX, sint32 nNewSizeY ) { vector 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; } bool PutPieceInLightMap( vector& Piece, sint32 nPieceSizeX, sint32 nPieceSizeY, vector& 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 get 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 &Vertices, double 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(Pi/2); QuantizationTbl[2].identity(); QuantizationTbl[2].rotateY(Pi/2); float fMax = 0.0f; int pos = 0; for( int i = 0; i < 3; ++i ) { if( fMax < fabs(QuantizationTbl[i].getI()*n) ) { fMax = fabs(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 sqrt( (UV2.U - UV1.U)*(UV2.U - UV1.U) + (UV2.V - UV1.V)*(UV2.V - UV1.V) ); } struct SLightBuild { enum EType { LightAmbient, LightPoint, LightDir, LightSpot }; EType Type; CVector Position; // Applicable to LightPoint and LightSpot CVector Direction; // Applicable to LightSpot and LightDir float rRadiusMin, rRadiusMax; // Applicable to LightPoint and LightSpot CRGBA Ambient; CRGBA Diffuse; CRGBA Specular; 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); } }; void getLightBuildList(std::vector& 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; // 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 (); */ if (maxLight->GetUseAtten()) { nelLight.rRadiusMin = maxLight->GetAtten (tvTime, ATTEN_START); nelLight.rRadiusMax = maxLight->GetAtten (tvTime, ATTEN_END); } // Add the light in the list vectLight.push_back (nelLight); } if( deleteIt ) delete maxLight; } } // Recurse sub node for (int i=0; iNumberOfChildren(); i++) getLightBuildList(vectLight, tvTime, ip, node->GetChildNode(i)); } void getLightBuilds( vector &lights, TimeValue tvTime, Interface& ip ) { SLightBuild amb; amb.Type = SLightBuild::EType::LightAmbient; 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 ); } // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- bool CNelExport::exportScene(std::vector& 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; nNodeGetName() ); 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) // ******************* #define RATIOLIGHTMAP 0.05 // 0.5 meters in real world represent 1 Lumel in UV world #define MAXLIGHTMAPSIZE 1024 if( theExportSceneStruct.bExportLighting ) { // 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, k; // 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++; } } // 2 -> Place lightmap for all MeshBuild (this operation does not need interaction between MeshBuilds) for( nNode=0; nNode < nNbMesh; ++nNode ) { // First order face by Material and by texture surface CMesh::CMeshBuild *pMB = AllMeshBuilds[nNode].first; CMatrix MBMatrix = getObjectToWorldMatrix( pMB ); vector AllFaces; vector AllVertices; // All vertices in world space vector AreaTextureNames; // Number of faces with the same texture name sint32 nNbFace = pMB->Faces.size(), nNbVertex = pMB->Vertices.size(); sint32 offset, nNewPosX, nNewPosY; // 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]; AllVertices.resize(nNbVertex); for( i = 0; i < nNbVertex; ++i ) AllVertices[i] = MBMatrix * pMB->Vertices[i]; // Bubble sort pointer to the faces (Material sorting by texture name) SortFaceByTextureName( AllFaces, pMB ); // Get the number of faces in Allfaces for each different texture name ComputeAreaOfTextureName( AreaTextureNames, AllFaces, pMB ); // For all faces that has the same texture name offset = 0; for( int nTexName = 0; nTexName < AreaTextureNames.size(); ++nTexName ) { nNbFace = AreaTextureNames[nTexName]; // Bubble sort pointer to the faces (Material sorting) SortFaceByMaterialId( offset, nNbFace, AllFaces ); // Sort by texture surface if the faces have the same material SortFaceByTextureSurface( offset, nNbFace, AllFaces ); offset += nNbFace; } // Here is the placement // TEST 1 : One all big lightmap for this object //sint32 CurrentMaterialId = AllFaces[0]->MaterialId; //sint32 BeginningFace = 0; vector TheLightMap; sint32 nLightMapSizeX = 1, nLightMapSizeY = 1; TheLightMap.resize(1); nNbFace = pMB->Faces.size(); // Calculation of the texture ratio double SpaceDist = 0.0, TextureDist = 0.0; for( i = 0; i < nNbFace; ++i ) { CVector v = AllVertices[AllFaces[i]->Corner[1].Vertex] - AllVertices[AllFaces[i]->Corner[0].Vertex]; SpaceDist += v.norm(); v = AllVertices[AllFaces[i]->Corner[2].Vertex] - AllVertices[AllFaces[i]->Corner[1].Vertex]; SpaceDist += v.norm(); v = AllVertices[AllFaces[i]->Corner[0].Vertex] - AllVertices[AllFaces[i]->Corner[2].Vertex]; SpaceDist += v.norm(); TextureDist += getUVDist( AllFaces[i]->Corner[1].Uvws[1], AllFaces[i]->Corner[0].Uvws[1] ); TextureDist += getUVDist( AllFaces[i]->Corner[2].Uvws[1], AllFaces[i]->Corner[1].Uvws[1] ); TextureDist += getUVDist( AllFaces[i]->Corner[0].Uvws[1], AllFaces[i]->Corner[2].Uvws[1] ); } double LMTextRatio = SpaceDist / TextureDist; LMTextRatio = LMTextRatio / RATIOLIGHTMAP; for( i = 0; i < nNbFace; ++i ) { // Piece creation vector Piece; sint32 nPieceSizeX, nPieceSizeY; // Try to map this face with quantized vector and express uv in lumel // MapFace( AllFaces[i], AllVertices, RATIOLIGHTMAP ); for( j = 0; j < 3; ++j ) // Express the UVs in lumel with a ratio { // For each corner AllFaces[i]->Corner[j].Uvws[1].U *= LMTextRatio; AllFaces[i]->Corner[j].Uvws[1].V *= LMTextRatio; } // Create the related piece CreatePiece( Piece, nPieceSizeX, nPieceSizeY, nNewPosX, nNewPosY, AllFaces[i]->Corner[0].Uvws[1].U, // lumx1 AllFaces[i]->Corner[0].Uvws[1].V, // lumy1 AllFaces[i]->Corner[1].Uvws[1].U, // lumx2 AllFaces[i]->Corner[1].Uvws[1].V, // lumy2 AllFaces[i]->Corner[2].Uvws[1].U, // lumx3 AllFaces[i]->Corner[2].Uvws[1].V, // lumy3 (i%254)+1 ); // color // Set the UV of the face in same basis as the piece (0->nPieceSize) for( j = 0; j < 3; ++j ) { // For each corner AllFaces[i]->Corner[j].Uvws[1].U -= nNewPosX; AllFaces[i]->Corner[j].Uvws[1].V -= nNewPosY; } while( true ) { if( !PutPieceInLightMap( Piece, nPieceSizeX, nPieceSizeY, TheLightMap, nLightMapSizeX, nLightMapSizeY, nNewPosX, nNewPosY ) ) { if( ( nLightMapSizeX < MAXLIGHTMAPSIZE ) || ( nLightMapSizeY < MAXLIGHTMAPSIZE ) ) { if( nLightMapSizeX < nLightMapSizeY ) ResizeBitmap( TheLightMap, nLightMapSizeX, nLightMapSizeY, nLightMapSizeX*2, nLightMapSizeY ); else ResizeBitmap( TheLightMap, nLightMapSizeX, nLightMapSizeY, nLightMapSizeX, nLightMapSizeY*2 ); } else { // ERROR: we reached the maximum texture size nlstop; } } else { // We found a position for this piece, update texture coord in lumel format for( j = 0; j < 3; ++j ) { AllFaces[i]->Corner[j].Uvws[1].U += nNewPosX; AllFaces[i]->Corner[j].Uvws[1].V += nNewPosY; } break; } } } // All faces have been put in the lightmap so update All texture coord with the size of the lightmap // Convert all faces from lumels to lumels/sizeofLightMap to get uv from 0.0 to 1.0 for( i = 0; i < nNbFace; ++i ) for( j = 0; j < 3; ++j ) { AllFaces[i]->Corner[j].Uvws[1].U /= nLightMapSizeX; AllFaces[i]->Corner[j].Uvws[1].V /= nLightMapSizeY; } // Write the new lightmap and update the buildmesh materials { CTextureFile *pTexture = new CTextureFile; string sTemp = AllMeshBuilds[nNode].second->GetName(); sTemp += ".tga"; pTexture->setFileName( sTemp ); sTemp = theExportSceneStruct.sExportLighting; if(sTemp[sTemp.size()-1] != '\\' ) sTemp += "\\"; sTemp += AllMeshBuilds[nNode].second->GetName(); sTemp += ".tga"; pTexture->resize( nLightMapSizeX, nLightMapSizeY ); CObjectVector &rText = pTexture->getPixels(); for( j = 0; j < nLightMapSizeY; ++j ) for( i = 0; i < nLightMapSizeX; ++i ) if( TheLightMap[i+j*nLightMapSizeX] != 0 ) { rText[(i+j*nLightMapSizeX)*4+0] = 63+((TheLightMap[i+j*nLightMapSizeX]*15)%190); rText[(i+j*nLightMapSizeX)*4+1] = 63+((TheLightMap[i+j*nLightMapSizeX]*27+63)%190); rText[(i+j*nLightMapSizeX)*4+2] = 63+((TheLightMap[i+j*nLightMapSizeX]*13+127)%190); rText[(i+j*nLightMapSizeX)*4+3] = 255; } else { rText[(i+j*nLightMapSizeX)*4+0] = 0; rText[(i+j*nLightMapSizeX)*4+1] = 0; rText[(i+j*nLightMapSizeX)*4+2] = 0; rText[(i+j*nLightMapSizeX)*4+3] = 0; } // Write texture to disk //{ // COFile f; // f.open( sTemp ); // pTexture->writeTGA( f, 32 ); //} for( i = 0; i < AllMeshBuilds[nNode].first->Materials.size(); ++i ) { AllMeshBuilds[nNode].first->Materials[i].setTexture( 1, pTexture ); //AllMeshBuilds[nNode].first->Materials[i].setLighting( false ); } } } // End for all nodes // All nodes are mapped correctly with a lightmap and lightmap is created on disk // -------- // RAYTRACE the scene // -------- // 0 - Get Lights from 3DSMAX vector vLights; getLightBuilds( vLights, tvTime, *theCNelExport.ip ); // Old version (with lighting for the Nel Core Engine) // vector vLights; // CExportNel::getLights( vLights, tvTime, *theCNelExport.ip ); // 1 - Light with the normal and the lights for( nNode=0; nNode < nNbMesh; ++nNode ) { CMesh::CMeshBuild *pMB = AllMeshBuilds[nNode].first; CMatrix MBMatrix = getObjectToWorldMatrix( pMB ); vector AllVertices; // All vertices in world space AllVertices.resize(pMB->Vertices.size()); for( i = 0; i < pMB->Vertices.size(); ++i ) AllVertices[i] = MBMatrix * pMB->Vertices[i]; // Select all lights that interact with this mesh sint32 nNbFace = pMB->Faces.size(); MBMatrix.invert(); MBMatrix.transpose(); for( i = 0; i < nNbFace; ++i ) { vector Piece; sint32 nPieceSizeX, nPieceSizeY, nPiecePosX, nPiecePosY; // Get the lightmap from the material of the face ITexture *pText = pMB->Materials[pMB->Faces[i].MaterialId].getTexture( 1 ); CTextureFile *pLightMap = dynamic_cast(pText); if( pLightMap == NULL ) continue; double u1 = pMB->Faces[i].Corner[0].Uvws[1].U * pLightMap->getWidth(), v1 = pMB->Faces[i].Corner[0].Uvws[1].V * pLightMap->getHeight(), u2 = pMB->Faces[i].Corner[1].Uvws[1].U * pLightMap->getWidth(), v2 = pMB->Faces[i].Corner[1].Uvws[1].V * pLightMap->getHeight(), u3 = pMB->Faces[i].Corner[2].Uvws[1].U * pLightMap->getWidth(), v3 = pMB->Faces[i].Corner[2].Uvws[1].V * pLightMap->getHeight(); CVector p1 = AllVertices[pMB->Faces[i].Corner[0].Vertex], p2 = AllVertices[pMB->Faces[i].Corner[1].Vertex], p3 = AllVertices[pMB->Faces[i].Corner[2].Vertex]; CVector n1 = MBMatrix.mulVector( pMB->Faces[i].Corner[0].Normal ), n2 = MBMatrix.mulVector( pMB->Faces[i].Corner[1].Normal ), n3 = MBMatrix.mulVector( pMB->Faces[i].Corner[2].Normal ); n1.normalize(); n2.normalize(); n3.normalize(); // Denominator Gradient along u and v double GradDen = 1.0 / ( (u3-u1)*(v2-v1) - (u2-u1)*(v3-v1) ); // Get the mask CreatePiece( Piece, nPieceSizeX, nPieceSizeY, nPiecePosX, nPiecePosY, u1, v1, u2, v2, u3, v3, 1 ); // Parse the mask for( k = 0; k < nPieceSizeY; ++k ) for( j = 0; j < nPieceSizeX; ++j ) if( Piece[j+k*nPieceSizeX] != 0 ) // Must this point be lighted ? { // Yes CRGBAF theColor; // Convert the (u,v) to world space coord (use gradient of x along u and v) (same for y,z...) double theU = nPiecePosX + j + 0.5; double theV = nPiecePosY + k + 0.5; CVector p, n; // p is the point in the world space double GradU = ( (p3.x-p1.x)*(v2-v1)-(p2.x-p1.x)*(v3-v1) ) * GradDen; double GradV = ( (p2.x-p1.x)*(u3-u1)-(p3.x-p1.x)*(u2-u1) ) * GradDen; p.x = GradU*(theU-u1) + GradV*(theV-v1) + p1.x; GradU = ( (p3.y-p1.y)*(v2-v1)-(p2.y-p1.y)*(v3-v1) ) * GradDen; GradV = ( (p2.y-p1.y)*(u3-u1)-(p3.y-p1.y)*(u2-u1) ) * GradDen; p.y = GradU*(theU-u1) + GradV*(theV-v1) + p1.y; GradU = ( (p3.z-p1.z)*(v2-v1)-(p2.z-p1.z)*(v3-v1) ) * GradDen; GradV = ( (p2.z-p1.z)*(u3-u1)-(p3.z-p1.z)*(u2-u1) ) * GradDen; p.z = GradU*(theU-u1) + GradV*(theV-v1) + p1.z; // The same for the normal GradU = ( (n3.x-n1.x)*(v2-v1)-(n2.x-n1.x)*(v3-v1) ) * GradDen; GradV = ( (n2.x-n1.x)*(u3-u1)-(n3.x-n1.x)*(u2-u1) ) * GradDen; n.x = GradU*(theU-u1) + GradV*(theV-v1) + n1.x; GradU = ( (n3.y-n1.y)*(v2-v1)-(n2.y-n1.y)*(v3-v1) ) * GradDen; GradV = ( (n2.y-n1.y)*(u3-u1)-(n3.y-n1.y)*(u2-u1) ) * GradDen; n.y = GradU*(theU-u1) + GradV*(theV-v1) + n1.y; GradU = ( (n3.z-n1.z)*(v2-v1)-(n2.z-n1.z)*(v3-v1) ) * GradDen; GradV = ( (n2.z-n1.z)*(u3-u1)-(n3.z-n1.z)*(u2-u1) ) * GradDen; n.z = GradU*(theU-u1) + GradV*(theV-v1) + n1.z; // --- n = n1 + n2 + n3; n.normalize(); theColor.R = theColor.G = theColor.B = theColor.A = 0.0; // Color calculation for( sint32 nLight = 0; nLight < vLights.size(); ++nLight ) { SLightBuild rLight = vLights[nLight]; CRGBAF lightAmbiCol = CRGBAF(0.0, 0.0, 0.0, 0.0); CRGBAF lightDiffCol = CRGBAF(0.0, 0.0, 0.0, 0.0); CRGBAF lightSpecCol = CRGBAF(0.0, 0.0, 0.0, 0.0); switch( rLight.Type ) { case SLightBuild::LightAmbient: lightAmbiCol.R = rLight.Ambient.R / 255.0; lightAmbiCol.G = rLight.Ambient.G / 255.0; lightAmbiCol.B = rLight.Ambient.B / 255.0; lightAmbiCol.A = rLight.Ambient.A / 255.0; break; case SLightBuild::LightPoint: { CVector p_light = rLight.Position - p; double p_light_distance = p_light.norm(); double light_intensity; if( p_light_distance < rLight.rRadiusMin ) light_intensity = 1.0; else if( p_light_distance > rLight.rRadiusMax ) light_intensity = 0.0; else light_intensity = 1.0 - (p_light_distance-rLight.rRadiusMin)/(rLight.rRadiusMax-rLight.rRadiusMin); p_light.normalize(); // ??? light_intensity *= light_intensity * light_intensity; lightDiffCol.R = light_intensity * rLight.Diffuse.R * max(0.0f, n*p_light) / 255.0; lightDiffCol.G = light_intensity * rLight.Diffuse.G * max(0.0f, n*p_light) / 255.0; lightDiffCol.B = light_intensity * rLight.Diffuse.B * max(0.0f, n*p_light) / 255.0; lightDiffCol.A = light_intensity * rLight.Diffuse.A * max(0.0f, n*p_light) / 255.0; } break; case SLightBuild::LightDir: { CVector light = - rLight.Direction; light.normalize(); lightDiffCol.R = rLight.Diffuse.R * max(0.0f, n*light) / 255.0; lightDiffCol.G = rLight.Diffuse.G * max(0.0f, n*light) / 255.0; lightDiffCol.B = rLight.Diffuse.B * max(0.0f, n*light) / 255.0; lightDiffCol.A = rLight.Diffuse.A * max(0.0f, n*light) / 255.0; } break; case SLightBuild::LightSpot: // TODO break; default: break; } theColor.R += lightAmbiCol.R + lightDiffCol.R;// * matDiffCol.R; if( theColor.R > 1.0 ) theColor.R = 1.0; theColor.G += lightAmbiCol.G + lightDiffCol.G;// * matDiffCol.G; if( theColor.G > 1.0 ) theColor.G = 1.0; theColor.B += lightAmbiCol.B + lightDiffCol.B;// * matDiffCol.B; if( theColor.B > 1.0 ) theColor.B = 1.0; theColor.A += lightAmbiCol.A + lightDiffCol.A;// * matDiffCol.A; if( theColor.A > 1.0 ) theColor.A = 1.0; } // Affect the color theColor.R = (1+n.x)/2; theColor.G = (1+n.y)/2; theColor.B = (1+n.z)/2; theColor.A = 1.; CObjectVector &pixel = pLightMap->getPixels(); pixel[(j+nPiecePosX + (k+nPiecePosY)*pLightMap->getWidth())*4+0] = theColor.R*255; pixel[(j+nPiecePosX + (k+nPiecePosY)*pLightMap->getWidth())*4+1] = theColor.G*255; pixel[(j+nPiecePosX + (k+nPiecePosY)*pLightMap->getWidth())*4+2] = theColor.B*255; pixel[(j+nPiecePosX + (k+nPiecePosY)*pLightMap->getWidth())*4+3] = theColor.A*255; } } // Write the new LightMap { COFile f; ITexture *pText = pMB->Materials[0].getTexture( 1 ); CTextureFile *pLightMap = dynamic_cast(pText); string sTemp = theExportSceneStruct.sExportLighting; if(sTemp[sTemp.size()-1] != '\\' ) sTemp += "\\"; sTemp += pLightMap->getFileName(); f.open( sTemp ); pLightMap->writeTGA( f, 32 ); } } // 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 // TEMP YOYO (DU) DE SA RACE pMB->VertexFlags |= CVertexBuffer::PrimaryColorFlag; CMatrix MBMatrix = getObjectToWorldMatrix( pMB ); for( i = 0; i < pMB->Faces.size(); ++i ) { CVector n = MBMatrix.mulVector(pMB->Faces[i].Corner[0].Normal); pMB->Faces[i].Corner[0].Color.R = 255.0 * (n.x+1.0)/2.0; pMB->Faces[i].Corner[0].Color.G = 255.0 * (n.y+1.0)/2.0; pMB->Faces[i].Corner[0].Color.B = 255.0 * (n.z+1.0)/2.0; pMB->Faces[i].Corner[0].Color.A = 255.0; n = MBMatrix.mulVector(pMB->Faces[i].Corner[1].Normal); pMB->Faces[i].Corner[1].Color.R = 255.0 * (n.x+1.0)/2.0; pMB->Faces[i].Corner[1].Color.G = 255.0 * (n.y+1.0)/2.0; pMB->Faces[i].Corner[1].Color.B = 255.0 * (n.z+1.0)/2.0; pMB->Faces[i].Corner[1].Color.A = 255.0; n = MBMatrix.mulVector(pMB->Faces[i].Corner[2].Normal); pMB->Faces[i].Corner[2].Color.R = 255.0 * (n.x+1.0)/2.0; pMB->Faces[i].Corner[2].Color.G = 255.0 * (n.y+1.0)/2.0; pMB->Faces[i].Corner[2].Color.B = 255.0 * (n.z+1.0)/2.0; pMB->Faces[i].Corner[2].Color.A = 255.0; } // TEMP YOYO (DU) DE SA RACE end for( i = 0; i < pMB->Materials.size(); ++i ) { pMB->Materials[i].setLighting( false ); pMB->Materials[i].setColor( CRGBA(255,255,255,255) ); } // *** TMP : force material to be animatable // TODO: check if material are animated //for (uint i=0; isetAnimatedMaterial (i, materialNames[i]); //} 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; } // ? -> Ending deletion des objets temporaires for( nNode = 0; nNode < nNbMesh; ++nNode ) delete AllMeshBuilds[nNode].first; } // End if lighting process asked return true; }