// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdafx.h" // Precompiled header #include "export_nel.h" #include "export_appdata.h" #include "calc_lm_rt.h" using namespace std; using namespace NL3D; using namespace NLMISC; // *********************************************************************************************** // CRTWorld // *********************************************************************************************** // ----------------------------------------------------------------------------------------------- CRTWorld::CRTWorld (bool errorInDialog, bool view, bool absolutePath, Interface *ip, std::string errorTitle, CExportNel *exp) { _Ip = ip; _AbsolutePath = absolutePath; _View = view; _ErrorInDialog = errorInDialog; _ErrorTitle = errorTitle; _Export = exp; } // ----------------------------------------------------------------------------------------------- CRTWorld::~CRTWorld() { uint32 i; for (i = 0; i < vMB.size(); ++i) { delete vMB[i]; delete vMBB[i]; } for (i = 0; i < vLightAccel.size(); ++i) if (vLightAccel[i] != NULL) delete vLightAccel[i]; } // ----------------------------------------------------------------------------------------------- void CRTWorld::build (vector &AllLights, CVector &trans, bool bExcludeNonSelected, const set &excludeNode, const set &includeNode) { uint32 i, j, k; TTicks ttTemp = CTime::getPerformanceTime(); DWORD t = timeGetTime(); GlobalTrans = trans; // Get all the nodes in the scene if( bExcludeNonSelected ) getAllSelectedNode (vMB, vMBB, vINode, AllLights, excludeNode, includeNode); else getAllNodeInScene (vMB, vMBB, vINode, AllLights, excludeNode); // Transform the meshbuilds vertices and normals to have world coordinates for( i = 0; i < vMB.size(); ++i ) { convertToWorldCoordinate (vMB[i], vMBB[i], GlobalTrans); } if (InfoLog) InfoLog->display("Object Construction : %d ms\n", timeGetTime()-t); // Construct all cube grids from all lights //wrt.cgAccel.resize( AllLights.size() ); //wrt.dirAccel.resize( AllLights.size() ); vLight.resize (AllLights.size()); vLightAccel.resize (AllLights.size()); for (i = 0; i < AllLights.size(); ++i) { SLightBuild &rLight = AllLights[i]; vLight[i].Position = rLight.Position; vLight[i].Direction = rLight.Direction; vLight[i].rSoftShadowRadius = rLight.rSoftShadowRadius; vLight[i].rSoftShadowConeLength = rLight.rSoftShadowConeLength; vLightAccel[i] = NULL; switch (rLight.Type) { // ------------------------ case SLightBuild::LightAmbient: vLight[i].Type = CRTLight::RTLightAmbient; break; // -------------------------- case SLightBuild::LightSpot: // For the moment spot like point vLight[i].Type = CRTLight::RTLightSpot; case SLightBuild::LightPoint: { vLight[i].Type = CRTLight::RTLightPoint; CRTLightAccelPoint *pLAP = new CRTLightAccelPoint; pLAP->create( 64 ); // width of each grid in number of square for( j = 0; j < vMB.size(); ++j ) { if (rLight.setExclusion.find (vINode[j]->GetName()) != rLight.setExclusion.end()) continue; for (k = 0; k < vMB[j]->Faces.size(); ++k) { SGridCell cell; cell.pF = &(vMB[j]->Faces[k]); cell.pMB = vMB[j]; cell.pMBB = vMBB[j]; CTriangle tri = CTriangle( cell.pMB->Vertices[cell.pF->Corner[0].Vertex] - rLight.Position, cell.pMB->Vertices[cell.pF->Corner[1].Vertex] - rLight.Position, cell.pMB->Vertices[cell.pF->Corner[2].Vertex] - rLight.Position ); if (intersectionTriangleSphere (tri, CBSphere(CVector(0,0,0), rLight.rRadiusMax))) pLAP->insert( tri, cell ); } } vLightAccel[i] = pLAP; } break; // ------------------------ case SLightBuild::LightDir: { vLight[i].Type = CRTLight::RTLightDir; CRTLightAccelDir *pLAD = new CRTLightAccelDir; pLAD->create (64, rLight.rDirRadius/64.0f, rLight.Direction); for( j = 0; j < vMB.size(); ++j ) { if (rLight.setExclusion.find (vINode[j]->GetName()) != rLight.setExclusion.end()) continue; for (k = 0; k < vMB[j]->Faces.size(); ++k) { SGridCell cell; cell.pF = &(vMB[j]->Faces[k]); cell.pMB = vMB[j]; cell.pMBB = vMBB[j]; CTriangle tri = CTriangle( cell.pMB->Vertices[cell.pF->Corner[0].Vertex], cell.pMB->Vertices[cell.pF->Corner[1].Vertex], cell.pMB->Vertices[cell.pF->Corner[2].Vertex] ); // Convert the triangle into a sphere CBSphere s; s.Center = (tri.V0 + tri.V1 + tri.V2)/3.0f; s.Radius = (tri.V0-s.Center).norm(); float tmp = (tri.V1-s.Center).norm(); if (tmp > s.Radius) s.Radius = tmp; tmp = (tri.V2-s.Center).norm(); if (tmp > s.Radius) s.Radius = tmp; if (intersectionSphereCylinder (s, rLight.Position, rLight.Direction, rLight.rDirRadius)) { float t = ((s.Center - rLight.Position)*rLight.Direction) / (rLight.Direction*rLight.Direction); if ((t-s.Radius) < pLAD->rMin) pLAD->rMin = t - s.Radius; if ((t+s.Radius) > pLAD->rMax) pLAD->rMax = t + s.Radius; pLAD->insert (tri, cell); } } } vLightAccel[i] = pLAD; } break; } } } // ----------------------------------------------------------------------------------------------- CRGBAF CRTWorld::raytrace (NLMISC::CVector &vVertex, sint32 nLightNb, uint8& rtVal, bool bSoftShadow) { CRGBAF Factor = CRGBAF(1.0f, 1.0f, 1.0f, 1.0f); CVector vVertexPos, vLightPos, vSelect; CRTLight &rLight = vLight[nLightNb]; switch (rLight.Type) { case SLightBuild::LightAmbient: Factor = CRGBAF(1.0f, 1.0f, 1.0f, 1.0f); break; case SLightBuild::LightSpot: case SLightBuild::LightPoint: { CVector light_p = vVertex - rLight.Position; float light_p_distance = light_p.norm(); light_p_distance = light_p_distance - (0.01f+(0.05f*light_p_distance/100.0f)); // Substract n centimeter light_p.normalize(); light_p *= light_p_distance; vLightPos = rLight.Position; vVertexPos = rLight.Position + light_p; vSelect = vVertexPos - vLightPos; } break; case SLightBuild::LightDir: { float lightdist = ((vVertex - rLight.Position)*rLight.Direction) / (rLight.Direction.norm()); CRTLightAccelDir *pLAD = (CRTLightAccelDir*)(vLightAccel[nLightNb]); lightdist = lightdist - pLAD->rMin; vLightPos = vVertex - rLight.Direction*lightdist; vVertexPos = vVertex - (0.01f+(0.05f*lightdist/100.0f))*rLight.Direction; vSelect = vVertexPos; } break; default: break; } float rSoftShadowRadius = rLight.rSoftShadowRadius; // Work with the directionnal light only for now if ((bSoftShadow) && (rLight.Type == SLightBuild::LightDir)) { CRTRay RayOfLight; float rAreaInit; RayOfLight.initDirectionnal(8, vVertex, rLight.Direction, rSoftShadowRadius, rLight.rSoftShadowConeLength); rAreaInit = RayOfLight.getArea(); vLightAccel[nLightNb]->select (vSelect, rSoftShadowRadius); while (!vLightAccel[nLightNb]->isEndSel()) { // Get selected element SGridCell cell = vLightAccel[nLightNb]->getSel(); 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] ); RayOfLight.clip (t); if (RayOfLight.Shapes.size() == 0) return CRGBAF(0.0f, 0.0f, 0.0f, 0.0f); // Next selected element vLightAccel[nLightNb]->nextSel(); } float rVal = RayOfLight.getArea() / rAreaInit; // rVal = AreaNotShadowed / AreaInit return CRGBAF(rVal, rVal, rVal, 255.0f); } else // Normal shadowing { vLightAccel[nLightNb]->select (vSelect); while (!vLightAccel[nLightNb]->isEndSel()) { // Get selected element SGridCell cell = vLightAccel[nLightNb]->getSel(); testCell (Factor, cell, vLightPos, vVertexPos, rtVal); if ((Factor.R == 0.0f) && (Factor.G == 0.0f) && (Factor.B == 0.0f)) return Factor; // Next selected element vLightAccel[nLightNb]->nextSel(); } } return Factor; } // ----------------------------------------------------------------------------------------------- void CRTWorld::testCell (CRGBAF &retValue, SGridCell &cell, CVector &vLightPos, CVector &vVertexPos, uint8& rtVal) { 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 ) ) { if( cell.pMBB->Materials[cell.pF->MaterialId].getBlend() || cell.pMBB->Materials[cell.pF->MaterialId].getAlphaTest() ) { // This is a transparent face we have to look in the texture ITexture *pT = cell.pMBB->Materials[cell.pF->MaterialId].getTexture(0); CRGBAF cPixMap; if( pT == NULL ) { retValue *= 1.0f - (cell.pMBB->Materials[cell.pF->MaterialId].getOpacity()/255.0f); cPixMap = CRGBAF(1.0f, 1.0f, 1.0f, 0.0f); } else { CVector gradU, gradV; t.computeGradient( cell.pF->Corner[0].Uvws[0].U, cell.pF->Corner[1].Uvws[0].U, cell.pF->Corner[2].Uvws[0].U, gradU ); t.computeGradient( cell.pF->Corner[0].Uvws[0].V, cell.pF->Corner[1].Uvws[0].V, cell.pF->Corner[2].Uvws[0].V, gradV ); float u = cell.pF->Corner[0].Uvws[0].U+gradU*(hit-t.V0); float v = cell.pF->Corner[0].Uvws[0].V+gradV*(hit-t.V0); u = fmodf( u, 1.0f ); if( u < 0.0f ) u += 1.0f; v = fmodf( v, 1.0f ); if( v < 0.0f ) v += 1.0f; if( pT->getWidth() == 0 ) ((CTextureFile*)pT)->generate(); cPixMap = pT->getColor( u,v ); } cPixMap.A *= cell.pMBB->Materials[cell.pF->MaterialId].getOpacity()/255.0f; cPixMap.R *= cell.pMBB->Materials[cell.pF->MaterialId].getDiffuse().R/255.0f; cPixMap.G *= cell.pMBB->Materials[cell.pF->MaterialId].getDiffuse().G/255.0f; cPixMap.B *= cell.pMBB->Materials[cell.pF->MaterialId].getDiffuse().B/255.0f; if (cell.pMBB->Materials[cell.pF->MaterialId].getStainedGlassWindow()) { retValue = (1.0f - cPixMap.A)*( retValue*(1.0f-cPixMap.A) + retValue*cPixMap*cPixMap.A ); } else { retValue *= (1.0f - cPixMap.A); } rtVal = 255; } else { // This is not a transparent face so if we intersect we get shadow if( rtVal < 255 ) rtVal += 1; retValue = CRGBAF(0.0f, 0.0f, 0.0f, 0.0f); } } } // ----------------------------------------------------------------------------------------------- void CRTWorld::addNode (INode *pNode, vector< CMesh::CMeshBuild* > &Meshes, vector< CMeshBase::CMeshBaseBuild* > &MeshesBase, vector< INode* > &INodes, vector &AllLights, const set &excludeNode, TimeValue tvTime) { if (! RPO::isZone (*pNode, tvTime) ) if (CExportNel::isMesh (*pNode, tvTime)) { // Nel export CExportNel exportNel (_ErrorInDialog, _View, true, _Ip, _ErrorTitle, NULL); CAABBox aabbox; exportNel.buildMeshAABBox(*pNode, aabbox, tvTime); aabbox.setCenter(aabbox.getCenter()+GlobalTrans); // Not an excluded node ? bool bInteract = false; if (excludeNode.find (pNode) == excludeNode.end()) { for( uint32 i = 0; i < AllLights.size(); ++i ) { if( isInteractionWithLight (AllLights[i], aabbox)) { bInteract = true; break; } } } if( bInteract ) { int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType&3) == 0) // If not an accelerator { CMesh::CMeshBuild *pMB; CMeshBase::CMeshBaseBuild *pMBB; pMB = exportNel.createMeshBuild ( *pNode, tvTime, pMBB); if( pMBB->bCastShadows ) { Meshes.push_back( pMB ); MeshesBase.push_back( pMBB ); INodes.push_back( pNode ); } else { delete pMB; // No interaction so delete the mesh delete pMBB; // No interaction so delete the mesh } } } } } // ----------------------------------------------------------------------------------------------- void CRTWorld::getAllSelectedNode (vector< CMesh::CMeshBuild* > &Meshes, vector< CMeshBase::CMeshBaseBuild* > &MeshesBase, vector< INode* > &INodes, vector &AllLights, const set &excludeNode, const set &includeNode) { // Get time TimeValue tvTime = _Ip->GetTime(); // Get node count int nNumSelNode = _Ip->GetSelNodeCount(); // Save all selected objects for (int nNode=0; nNodeGetSelNode (nNode); // Already in the include list ? if (includeNode.find (pNode) == includeNode.end()) { // Add the node addNode (pNode, Meshes, MeshesBase, INodes, AllLights, excludeNode, tvTime); } } // Add the included set::const_iterator ite = includeNode.begin(); while (ite != includeNode.end()) { // Add the included object addNode (*ite, Meshes, MeshesBase, INodes, AllLights, excludeNode, tvTime); // Next node ite++; } } // ----------------------------------------------------------------------------------------------- void CRTWorld::getAllNodeInScene (vector< CMesh::CMeshBuild* > &Meshes, vector< CMeshBase::CMeshBaseBuild* > &BaseMeshes, vector< INode* > &INodes, vector &AllLights, const set &excludeNode, INode* pNode) { if( pNode == NULL ) pNode = _Ip->GetRootNode(); // Get a pointer on the object's node TimeValue tvTime = _Ip->GetTime(); // Add the node addNode (pNode, Meshes, BaseMeshes, INodes, AllLights, excludeNode, tvTime); for( sint32 i = 0; i < pNode->NumberOfChildren(); ++i ) getAllNodeInScene( Meshes, BaseMeshes, INodes, AllLights, excludeNode, pNode->GetChildNode(i) ); } // ----------------------------------------------------------------------------------------------- bool CRTWorld::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; } // ----------------------------------------------------------------------------------------------- bool CRTWorld::intersectionSphereCylinder (CBSphere &s, CVector &cyCenter, CVector &cyDir, float cyRadius) { float t = ((s.Center - cyCenter)*cyDir) / (cyDir*cyDir); CVector Xp = cyCenter + t*cyDir; float d = (s.Center-Xp).norm(); if (d <= (cyRadius+s.Radius)) return true; else return false; } // ----------------------------------------------------------------------------------------------- bool CRTWorld::isInteractionWithLight (SLightBuild &rSLB, CAABBox &meshBox) { switch( rSLB.Type ) { case SLightBuild::LightAmbient: // No need an ambient light... return false; break; case SLightBuild::LightSpot: // For the moment spot like point case SLightBuild::LightPoint: { // Get light bbox CAABBox lightBox; lightBox.setCenter( rSLB.Position ); lightBox.extend( rSLB.Position - CVector(rSLB.rRadiusMax,0,0) ); lightBox.extend( rSLB.Position + CVector(rSLB.rRadiusMax,0,0) ); lightBox.extend( rSLB.Position - CVector(0,rSLB.rRadiusMax,0) ); lightBox.extend( rSLB.Position + CVector(0,rSLB.rRadiusMax,0) ); lightBox.extend( rSLB.Position - CVector(0,0,rSLB.rRadiusMax) ); lightBox.extend( rSLB.Position + CVector(0,0,rSLB.rRadiusMax) ); if( lightBox.intersect( meshBox ) ) return true; if( meshBox.include( lightBox.getMin() ) ) return true; if( lightBox.include( meshBox.getMin() ) ) return true; } break; case SLightBuild::LightDir: { // Use acceleration for dir light to not select all nodes in scene CBSphere s; s.Radius = meshBox.getHalfSize().norm(); s.Center = meshBox.getCenter(); // Test against the cylinder if (intersectionSphereCylinder (s, rSLB.Position, rSLB.Direction, rSLB.rDirRadius)) return true; } break; } return false; } // ----------------------------------------------------------------------------------------------- bool CRTWorld::isInteractionLightMesh (SLightBuild &rSLB, NL3D::CMesh::CMeshBuild &rMB, NL3D::CMeshBase::CMeshBaseBuild &rMBB) { if (rSLB.Type == SLightBuild::LightAmbient) return false; // Get the mesh bbox. Warning the vertices need to be transformed. CAABBox meshBox; CMatrix MBMatrix = getObjectToWorldMatrix (&rMB, &rMBB); MBMatrix.movePos (GlobalTrans); 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] ); return isInteractionWithLight(rSLB, meshBox); } // *********************************************************************************************** // Ray representation for soft shadow // *********************************************************************************************** // ----------------------------------------------------------------------------------------------- void CRTRay::initDirectionnal (uint32 nNbSide, const NLMISC::CVector &vVertex, const NLMISC::CVector &lightDir, float rRadius, float rDistCyl) { // Construct the matrix with K= vertex to light direction CVector vDirection = -lightDir; vDirection.normalize(); CVector I = (fabs(vDirection*CVector(1.f,0,0))>0.99)?CVector(0.f,1.f,0.f):CVector(1.f,0.f,0.f); CVector K = vDirection; CVector J = K^I; J.normalize(); I=J^K; I.normalize(); // Setup the Inv Vertex Basis _InvVertexMat.identity(); _InvVertexMat.setRot (I, J, K, true); _InvVertexMat.setPos (vVertex); _InvVertexMat.invert(); // Build the clipping pyramids, in vertex space CVector lb(-rRadius, -rRadius, rDistCyl); CVector lt(-rRadius, rRadius, rDistCyl); CVector rb(rRadius, -rRadius, rDistCyl); CVector rt(rRadius, rRadius, rDistCyl); // Cone pyramid: perspective and capped at near (for precision and z division) and far (where start the cylinder) _ConePyramid[0].make(-CVector::K, CVector(0,0,0.01f)); // near _ConePyramid[1].make( CVector::K, CVector(0,0,rDistCyl)); // far // NB: don't need the full clip (faster) /*_ConePyramid[2].make(CVector::Null, lt, lb); // left _ConePyramid[3].make(CVector::Null, rt, lt); // top _ConePyramid[4].make(CVector::Null, rb, rt); // right _ConePyramid[5].make(CVector::Null, lb, rb); // bottom*/ // Cylinder pyramid: orthogonal and capped at near (ie rDistCyl, where start the cylinder) only _CylinderPyramid[0].make(-CVector::K, CVector(0,0,rDistCyl)); // near // NB: don't need the full clip (faster) /*_CylinderPyramid[1].make(-CVector::I, lb); // left _CylinderPyramid[2].make( CVector::I, rb); // right _CylinderPyramid[3].make(-CVector::J, lb); // bottom _CylinderPyramid[4].make( CVector::J, lt); // top*/ // Build the initial shape Shapes.resize (1); SConvexShape &rShp = Shapes[0]; rShp.Vertices.resize (nNbSide); for (uint32 i = 0; i < rShp.Vertices.size(); ++i) { rShp.Vertices[i].U = rRadius * cosf(i*2.0f*(float)Pi / rShp.Vertices.size()); rShp.Vertices[i].V = rRadius * sinf(i*2.0f*(float)Pi / rShp.Vertices.size()); } _DistCyl = rDistCyl; } // ----------------------------------------------------------------------------------------------- void CRTRay::clip (const CTriangle& t) { sint i; CVector tv0= _InvVertexMat.mulPoint (t.V0); CVector tv1= _InvVertexMat.mulPoint (t.V1); CVector tv2= _InvVertexMat.mulPoint (t.V2); // **** Contribution in the cone part nlctassert(NumConePlanes>NumCylinderPlanes); CVector apolyIn[3+NumConePlanes]; CVector apolyOut[3+NumConePlanes]; CVector *polyIn= apolyIn; CVector *polyOut= apolyOut; sint numVerts; polyIn[0]= tv0; polyIn[1]= tv1; polyIn[2]= tv2; numVerts= 3; for(i=0;i shapesOut; for (uint32 i = 0; i < Shapes.size(); ++i) { SConvexShape &rShp = Shapes[i]; // Clip each shapes with the triangle if (isShapeMustBeClippedByTriangle (rShp, tri)) { std::vector shapesTmp; shapesTmp.resize (0); clipShape (rShp, tri, shapesTmp); for (uint32 j = 0; j < shapesTmp.size(); ++j) shapesOut.push_back (shapesTmp[j]); } else // The shape is not clipped by the triangle so must be add it directly { shapesOut.push_back (rShp); } } // We are now like shapesOut Shapes = shapesOut; } // ----------------------------------------------------------------------------------------------- float CRTRay::getArea () { float rSize = 0.0f; // For all shapes for (uint32 i = 0; i < Shapes.size(); ++i) { SConvexShape &rShp = Shapes[i]; // Get all triangles from the shapes for (uint32 j = 0; j < rShp.Vertices.size()-2; ++j) { CUV v1 = rShp.Vertices[j+1] - rShp.Vertices[0]; CUV v2 = rShp.Vertices[j+2] - rShp.Vertices[0]; rSize += 0.5f * fabsf(v1.U*v2.V - v1.V*v2.U); } } return rSize; } // ----------------------------------------------------------------------------------------------- bool CRTRay::isShapeMustBeClippedByTriangle (CRTRay::SConvexShape& scs, CUV tri[3]) { uint32 i,j; bool bAllVertExt; // Is the convex shape vertices are all on a side of the triangle for (i = 0; i < 3; ++i) { CUV &p0 = tri[i]; CUV &p1 = tri[(i+1)%3]; bAllVertExt = true; for (j = 0; j < scs.Vertices.size(); ++j) // Does the vertex j is interior ? if (((p1.U-p0.U) * (scs.Vertices[j].V-p0.V) - (p1.V-p0.V) * (scs.Vertices[j].U-p0.U)) > 0.0f) { bAllVertExt = false; // Yes so all vertex are NOT exterior break; } if (bAllVertExt) return false; // Clip not needed because all vertices exterior } // Is all vertices of the triangle on a side of the convex shape side for (i = 0; i < scs.Vertices.size(); ++i) { CUV &p0 = scs.Vertices[i]; CUV &p1 = scs.Vertices[(i+1)%scs.Vertices.size()]; bAllVertExt = true; for (j = 0; j < 3; ++j) // Does the vertex j is interior ? if ((p1.U-p0.U) * (tri[j].V-p0.V) - (p1.V-p0.V) * (tri[j].U-p0.U) > 0.0f) { bAllVertExt = false; // Yes so all vertex are NOT exterior break; } if (bAllVertExt) return false; // Clip not needed because all vertices exterior } return true; } // ----------------------------------------------------------------------------------------------- void CRTRay::clipShape (CRTRay::SConvexShape& ShapeIn, CUV Tri[3], vector &ShapesOut) { uint32 i, j, lastj; SConvexShape Front, Back, BackInput; BackInput = ShapeIn; // For each side of the triangle for (i = 0; i < 3; ++i) { CUV &sideV0 = Tri[i]; CUV &sideV1 = Tri[(i+1)%3]; // Front is the polygon exterior to the side (which does not contain the triangle) Front.Vertices.resize (0); Back.Vertices.resize (0); lastj = BackInput.Vertices.size()-1; bool previousExt = ((sideV1.U-sideV0.U) * (BackInput.Vertices[lastj].V-sideV0.V) - (sideV1.V-sideV0.V) * (BackInput.Vertices[lastj].U-sideV0.U) < 0.0f); // BackInput -> Back, Front split from the current edge for (j = 0; j < BackInput.Vertices.size(); ++j) { bool currentExt = ((sideV1.U-sideV0.U) * (BackInput.Vertices[j].V-sideV0.V) - (sideV1.V-sideV0.V) * (BackInput.Vertices[j].U-sideV0.U) < 0.0f); if (currentExt) { // Vertex j exterior if (!previousExt) // Previous vertex interior ? { // Ok, intersect CUV tmp = getLineIntersection (BackInput.Vertices[lastj], BackInput.Vertices[j], sideV0, sideV1); Front.Vertices.push_back (tmp); Back.Vertices.push_back (tmp); } // Store new vertex front Front.Vertices.push_back (BackInput.Vertices[j]); } else { // Vertex j is interior if (previousExt) // Previous vertex exterior ? { // Ok, intersect CUV tmp = getLineIntersection (BackInput.Vertices[lastj], BackInput.Vertices[j], sideV0, sideV1); Front.Vertices.push_back (tmp); Back.Vertices.push_back (tmp); } // Store new vertex back Back.Vertices.push_back (BackInput.Vertices[j]); } previousExt = currentExt; lastj = j; } // Run through the front and back to weld vertices weldVertices (Front); weldVertices (Back); if(Front.Vertices.size() > 2) // Front is a poly that we will out ShapesOut.push_back (Front); // Get the back poly to the next iteration BackInput = Back; if (BackInput.Vertices.size() < 3) return; } } // ----------------------------------------------------------------------------------------------- void CRTRay::weldVertices (CRTRay::SConvexShape& Shp) { uint32 curVertex = 0; for (uint32 j = 0; j < Shp.Vertices.size(); ++j) { CUV &p0 = Shp.Vertices[j]; CUV &p1 = Shp.Vertices[(j+1)%Shp.Vertices.size()]; if (((p1.U-p0.U)*(p1.U-p0.U)+(p1.V-p0.V)*(p1.V-p0.V)) > 0.0001f) { Shp.Vertices[curVertex] = p0; curVertex++; } } Shp.Vertices.resize (curVertex); } // ----------------------------------------------------------------------------------------------- CUV CRTRay::getLineIntersection (const CUV &l1p1, const CUV &l1p2, const CUV &l2p1, const CUV &l2p2) { float denominator = (l1p2.U-l1p1.U)*(l2p2.V-l2p1.V) - (l1p2.V-l1p1.V)*(l2p2.U-l2p1.U); if (denominator == 0.0) return l1p1; // The segment are colinear float k = ((l2p1.U-l1p1.U)*(l2p2.V-l2p1.V) - (l2p1.V-l1p1.V)*(l2p2.U-l2p1.U) ) / denominator; if (k <= 0.0f) return l1p1; if (k >= 1.0f) return l1p2; return l1p1 + (l1p2-l1p1) * k; } // *********************************************************************************************** // Light accelerator for directionnal lights // *********************************************************************************************** // ----------------------------------------------------------------------------------------------- CRTLightAccelDir::CRTLightAccelDir() { rMin = 100000.0f; rMax = -100000.0f; } // ----------------------------------------------------------------------------------------------- CRTLightAccelDir::~CRTLightAccelDir() { } // ----------------------------------------------------------------------------------------------- void CRTLightAccelDir::create (int nSize, float rRadius, CVector &vDirection) { grid.create ( nSize, 2*rRadius ); CMatrix tmp; CVector I = (fabs(vDirection*CVector(1.f,0,0))>0.99)?CVector(0.f,1.f,0.f):CVector(1.f,0.f,0.f); CVector K = vDirection; CVector J = K^I; J.normalize(); I=J^K; I.normalize(); tmp.identity(); tmp.setRot (I, J, K, true); invMat = tmp; invMat.invert (); tmp.identity(); grid.changeBase (tmp); } // ----------------------------------------------------------------------------------------------- void CRTLightAccelDir::insert (CTriangle &tri, SGridCell &cell) { CVector vMin; CVector vMax; CTriangle t = tri; t.V0 = invMat.mulPoint (t.V0); t.V1 = invMat.mulPoint (t.V1); t.V2 = invMat.mulPoint (t.V2); vMin = t.V0; vMax = t.V0; if (vMin.x > t.V1.x) vMin.x = t.V1.x; if (vMin.y > t.V1.y) vMin.y = t.V1.y; if (vMin.z > t.V1.z) vMin.z = t.V1.z; if (vMin.x > t.V2.x) vMin.x = t.V2.x; if (vMin.y > t.V2.y) vMin.y = t.V2.y; if (vMin.z > t.V2.z) vMin.z = t.V2.z; if (vMax.x < t.V1.x) vMax.x = t.V1.x; if (vMax.y < t.V1.y) vMax.y = t.V1.y; if (vMax.z < t.V1.z) vMax.z = t.V1.z; if (vMax.x < t.V2.x) vMax.x = t.V2.x; if (vMax.y < t.V2.y) vMax.y = t.V2.y; if (vMax.z < t.V2.z) vMax.z = t.V2.z; grid.insert (vMin, vMax, cell); } // ----------------------------------------------------------------------------------------------- void CRTLightAccelDir::select (CVector &v) { CVector vSel = v; vSel = invMat.mulPoint (vSel); grid.select (vSel, vSel); itSel = grid.begin(); } // ----------------------------------------------------------------------------------------------- void CRTLightAccelDir::select (CVector &v, float rRadius) { CVector vSelMin = v, vSelMax; vSelMin = invMat.mulPoint (vSelMin); vSelMax = vSelMin; vSelMin.x -= sqrtf(2.0f)*rRadius; vSelMin.y -= sqrtf(2.0f)*rRadius; vSelMax.x += sqrtf(2.0f)*rRadius; vSelMax.y += sqrtf(2.0f)*rRadius; grid.select (vSelMin, vSelMax); itSel = grid.begin(); } // ----------------------------------------------------------------------------------------------- SGridCell CRTLightAccelDir::getSel () { return *itSel; } // ----------------------------------------------------------------------------------------------- void CRTLightAccelDir::nextSel () { ++itSel; } // ----------------------------------------------------------------------------------------------- bool CRTLightAccelDir::isEndSel () { return (itSel == grid.end()); } // *********************************************************************************************** // Light accelerator for point and spot lights // *********************************************************************************************** // ----------------------------------------------------------------------------------------------- CRTLightAccelPoint::CRTLightAccelPoint () { 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 ); } // ----------------------------------------------------------------------------------------------- CRTLightAccelPoint::~CRTLightAccelPoint () { } // ----------------------------------------------------------------------------------------------- void CRTLightAccelPoint::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 CRTLightAccelPoint::insert (CTriangle &tri, SGridCell &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 CRTLightAccelPoint::select (CVector &v) { SSelector sel; 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 ) ) { sel.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 ) ) { sel.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 ) ) { sel.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 ) ) { sel.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 ) ) { sel.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 ) ) { sel.nSelGrid = kBack; gp.make( CVector(0,1,0), CVector(0,0.5,0) ); } nlassert(sel.nSelGrid!=-1); CVector newV = gp.intersect( CVector(0,0,0), v ); _Grids[sel.nSelGrid].select(newV, newV); sel.itSel = _Grids[sel.nSelGrid].begin(); _Selection.clear(); _Selection.push_back (sel); _CurSel = 0; // Point on the first non-empty grid while ( (_CurSel < _Selection.size()) && (_Selection[_CurSel].itSel == _Grids[_Selection[_CurSel].nSelGrid].end()) ) ++_CurSel; } // ----------------------------------------------------------------------------------------------- void CRTLightAccelPoint::select (CVector &v, float rRadius) { // Perpendicular projection of the circle on each plane // TODO // TODO _CurSel = 0; // Point on the first non-empty grid while ( (_CurSel < _Selection.size()) && (_Selection[_CurSel].itSel == _Grids[_Selection[_CurSel].nSelGrid].end()) ) ++_CurSel; } // ----------------------------------------------------------------------------------------------- SGridCell CRTLightAccelPoint::getSel () { return *_Selection[_CurSel].itSel; } // ----------------------------------------------------------------------------------------------- void CRTLightAccelPoint::nextSel () { ++(_Selection[_CurSel].itSel); while ( (_CurSel < _Selection.size()) && (_Selection[_CurSel].itSel == _Grids[_Selection[_CurSel].nSelGrid].end()) ) ++_CurSel; } // ----------------------------------------------------------------------------------------------- bool CRTLightAccelPoint::isEndSel () { return (_CurSel == _Selection.size()); } // ----------------------------------------------------------------------------------------------- void CRTLightAccelPoint::project (CTriangle &tri, CPlane pyr[4], CPlane &gridPlane, sint32 nGridNb, SGridCell &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 ); } }