/****************************************************************************** * subdivide tree *****************************************************************************/ void ntlTree::subdivide(BSPNode *node, int depth, int axis) { int nextAxis=0; /* next axis to partition */ int allTriDistSet = (1<<0)|(1<<1); // all mpTriDist flags set? //errorOut(" "<<node<<" depth:"<<depth<<" m:"<<node->members->size() <<" "<<node->min<<" - "<<node->max ); if(depth>mCurrentDepth) mCurrentDepth = depth; node->child[0] = node->child[1] = NULL; if( ( (int)node->members->size() > mMaxListLength) && (depth < mMaxDepth ) && (node->cloneVec<10) && (!mAbortSubdiv) ) { gfxReal planeDiv = 0.499999; // position of plane division // determine next subdivision axis int newaxis = 0; gfxReal extX = node->max[0]-node->min[0]; gfxReal extY = node->max[1]-node->min[1]; gfxReal extZ = node->max[2]-node->min[2]; if( extY>extX ) { if( extZ>extY ) { newaxis = 2; } else { newaxis = 1; } } else { if( extZ>extX ) { newaxis = 2; } else { newaxis = 0; } } axis = node->axis = newaxis; // init child nodes for( int i=0; i<2; i++) { /* status output */ mCurrentNodes++; if(mCurrentNodes % 13973 ==0) { debugOutInter( "NTL Generating BSP Tree ("<<doSort<<","<<chooseAxis<<") ... (Nodes "<< mCurrentNodes << ", Depth "<<mCurrentDepth<< ") " , 2, 2000); } /* create new node */ node->child[i] = new BSPNode; node->child[i]->min = node->min; node->child[i]->max = node->max; node->child[i]->max = node->max; node->child[i]->child[0] = NULL; node->child[i]->child[1] = NULL; node->child[i]->members = NULL; nextAxis = (axis+1)%3; node->child[i]->axis = nextAxis; mNumNodes++; // abort when using 256MB only for tree... if(mNumNodes*sizeof(BSPNode)> 1024*1024*512) mAbortSubdiv = 1; /* current division plane */ if(!i) { node->child[i]->min[axis] = node->min[axis]; node->child[i]->max[axis] = node->min[axis] + planeDiv* (node->max[axis]-node->min[axis]); } else { node->child[i]->min[axis] = node->min[axis] + planeDiv* (node->max[axis]-node->min[axis]); node->child[i]->max[axis] = node->max[axis]; } } /* process the two children */ int thisTrisFor[2] = {0,0}; int thisTriDoubles[2] = {0,0}; for(int t=0;t<(int)node->members->size();t++) mpTriDist[t] = 0; for( int i=0; i<2; i++) { /* distribute triangles */ int t = 0; for (vector<ntlTriangle *>::iterator iter = node->members->begin(); iter != node->members->end(); iter++ ) { /* add triangle, check bounding box axis */ TriangleBBox *bbox = &mpTBB[ (*iter)->getBBoxId() ]; bool isintersect = true; if( bbox->end[axis] < node->child[i]->min[axis] ) isintersect = false; else if( bbox->start[axis] > node->child[i]->max[axis] ) isintersect = false; if(isintersect) { // add flag to vector mpTriDist[t] |= (1<<i); // count no. of triangles for vector init thisTrisFor[i]++; } if(mpTriDist[t] == allTriDistSet) { thisTriDoubles[i]++; mTriDoubles++; // TODO check for small geo tree?? } t++; } /* end of loop over all triangles */ } // i /* distribute triangles */ bool haveCloneVec[2] = {false, false}; for( int i=0; i<2; i++) { node->child[i]->members = new vector<ntlTriangle *>( thisTrisFor[i] ); node->child[i]->cloneVec = 0; } int tind0 = 0; int tind1 = 0; if( (!haveCloneVec[0]) || (!haveCloneVec[1]) ){ int t = 0; // triangle index counter for (vector<ntlTriangle *>::iterator iter = node->members->begin(); iter != node->members->end(); iter++ ) { if(!haveCloneVec[0]) { if( (mpTriDist[t] & 1) == 1) { (*node->child[0]->members)[tind0] = (*iter); // dont use push_back for preinited size! tind0++; } } if(!haveCloneVec[1]) { if( (mpTriDist[t] & 2) == 2) { (*node->child[1]->members)[tind1] = (*iter); // dont use push_back for preinited size! tind1++; } } t++; } /* end of loop over all triangles */ } // subdivide children for( int i=0; i<2; i++) { /* recurse */ subdivide( node->child[i], depth+1, nextAxis ); } /* if we are here, this are childs, so we dont need the members any more... */ /* delete unecessary members */ if( (!haveCloneVec[0]) && (!haveCloneVec[1]) && (node->cloneVec == 0) ){ delete node->members; } node->members = NULL; } /* subdivision necessary */ }
/****************************************************************************** * Render the current scene * uses the global variables from the parser *****************************************************************************/ int ntlWorld::renderScene( void ) { #ifndef ELBEEM_PLUGIN char nrStr[5]; // nr conversion std::ostringstream outfn_conv(""); // converted ppm with other suffix ntlRenderGlobals *glob; // storage for global rendering parameters myTime_t timeStart,totalStart,timeEnd; // measure user running time myTime_t rendStart,rendEnd; // measure user rendering time glob = mpGlob; // deactivate for all with index!=0 if((glob_mpactive)&&(glob_mpindex>0)) return(0); /* check if picture already exists... */ if(!glob->getSingleFrameMode() ) { snprintf(nrStr, 5, "%04d", glob->getAniCount() ); if(glob_mpactive) { outfn_conv << glob->getOutFilename() <<"_"<<glob_mpindex<<"_" << nrStr << ".png"; /// DEBUG! } else { // ORG outfn_conv << glob->getOutFilename() <<"_" << nrStr << ".png"; } //if((mpGlob->getDisplayMode() == DM_RAY)&&(mpGlob->getFrameSkip())) { if(mpGlob->getFrameSkip()) { struct stat statBuf; if(stat(outfn_conv.str().c_str(),&statBuf) == 0) { errorOut("ntlWorld::renderscene Warning: file "<<outfn_conv.str()<<" already exists - skipping frame..."); glob->setAniCount( glob->getAniCount() +1 ); return(2); } } // RAY mode } else { // single frame rendering, overwrite if necessary... outfn_conv << glob->getSingleFrameFilename(); } /* start program */ timeStart = getTime(); /* build scene geometry, calls buildScene(t,false) */ glob->getRenderScene()->prepareScene(mSimulationTime); /* start program */ totalStart = getTime(); /* view parameters are currently not animated */ /* calculate rays through projection plane */ ntlVec3Gfx direction = glob->getLookat() - glob->getEye(); /* calculate width of screen using perpendicular triangle diven by * viewing direction and screen plane */ gfxReal screenWidth = norm(direction)*tan( (glob->getFovy()*0.5/180.0)*M_PI ); /* calculate vector orthogonal to up and viewing direction */ ntlVec3Gfx upVec = glob->getUpVec(); ntlVec3Gfx rightVec( cross(upVec,direction) ); normalize(rightVec); /* calculate screen plane up vector, perpendicular to viewdir and right vec */ upVec = ntlVec3Gfx( cross(rightVec,direction) ); normalize(upVec); /* check if vectors are valid */ if( (equal(upVec,ntlVec3Gfx(0.0))) || (equal(rightVec,ntlVec3Gfx(0.0))) ) { errMsg("ntlWorld::renderScene","Invalid viewpoint vectors! up="<<upVec<<" right="<<rightVec); return(1); } /* length from center to border of screen plane */ rightVec *= (screenWidth*glob->getAspect() * -1.0); upVec *= (screenWidth * -1.0); /* screen traversal variables */ ntlVec3Gfx screenPos; /* current position on virtual screen */ int Xres = glob->getResX(); /* X resolution */ int Yres = glob->getResY(); /* Y resolution */ ntlVec3Gfx rightStep = (rightVec/(Xres/2.0)); /* one step right for a pixel */ ntlVec3Gfx upStep = (upVec/(Yres/2.0)); /* one step up for a pixel */ /* anti alias init */ char showAAPic = 0; int aaDepth = glob->getAADepth(); int aaLength; if(aaDepth>=0) aaLength = (2<<aaDepth); else aaLength = 0; float aaSensRed = 0.1; float aaSensGreen = 0.1; float aaSensBlue = 0.1; int aaArrayX = aaLength*Xres+1; int aaArrayY = ( aaLength+1 ); ntlColor *aaCol = new ntlColor[ aaArrayX*aaArrayY ]; char *aaUse = new char[ aaArrayX*aaArrayY ]; /* picture storage */ int picX = Xres; int picY = Yres; if(showAAPic) { picX = Xres *aaLength+1; picY = Yres *aaLength+1; } ntlColor *finalPic = new ntlColor[picX * picY]; /* reset picture vars */ for(int j=0;j<aaArrayY;j++) { for(int i=0;i<aaArrayX;i++) { aaCol[j*aaArrayX+i] = ntlColor(0.0, 0.0, 0.0); aaUse[j*aaArrayX+i] = 0; } } for(int j=0;j<picY;j++) { for(int i=0;i<picX;i++) { finalPic[j*picX+i] = ntlColor(0.0, 0.0, 0.0); } } /* loop over all y lines in screen, from bottom to top because * ppm format wants 0,0 top left */ rendStart = getTime(); glob->setCounterShades(0); glob->setCounterSceneInter(0); for (int scanline=Yres ; scanline > 0 ; --scanline) { debugOutInter( "ntlWorld::renderScene: Line "<<scanline<< " ("<< ((Yres-scanline)*100/Yres) <<"%) ", 2, 2000 ); screenPos = glob->getLookat() + upVec*((2.0*scanline-Yres)/Yres) - rightVec; /* loop over all pixels in line */ for (int sx=0 ; sx < Xres ; ++sx) { if((sx==glob->getDebugPixelX())&&(scanline==(Yres-glob->getDebugPixelY()) )) { // DEBUG!!! glob->setDebugOut(10); } else glob->setDebugOut(0); /* compute ray from eye through current pixel into scene... */ ntlColor col; if(aaDepth<0) { ntlVec3Gfx dir(screenPos - glob->getEye()); ntlRay the_ray(glob->getEye(), getNormalized(dir), 0, 1.0, glob ); /* ...and trace it */ col = the_ray.shade(); } else { /* anti alias */ int ai,aj; /* position in grid */ int aOrg = sx*aaLength; /* grid offset x */ int currStep = aaLength; /* step size */ char colDiff = 1; /* do colors still differ too much? */ ntlColor minCol,maxCol; /* minimum and maximum Color Values */ minCol = ntlColor(1.0,1.0,1.0); maxCol = ntlColor(0.0,0.0,0.0); while((colDiff) && (currStep>0)) { colDiff = 0; for(aj = 0;aj<=aaLength;aj+= currStep) { for(ai = 0;ai<=aaLength;ai+= currStep) { /* shade pixel if not done */ if(aaUse[aj*aaArrayX +ai +aOrg] == 0) { aaUse[aj*aaArrayX +ai +aOrg] = 1; ntlVec3Gfx aaPos( screenPos + (rightStep * (ai- aaLength/2)/(gfxReal)aaLength ) + (upStep * (aj- aaLength/2)/(gfxReal)aaLength ) ); ntlVec3Gfx dir(aaPos - glob->getEye()); ntlRay the_ray(glob->getEye(), getNormalized(dir), 0, 1.0, glob ); /* ...and trace it */ ntlColor newCol= the_ray.shade(); aaCol[aj*aaArrayX +ai +aOrg]= newCol; } /* not used? */ } } /* check color differences */ for(aj = 0;aj<aaLength;aj+= currStep) { for(ai = 0;ai<aaLength;ai+= currStep) { char thisColDiff = 0; if( (fabs(aaCol[aj*aaArrayX +ai +aOrg][0] - aaCol[(aj+0)*aaArrayX +(ai+currStep) +aOrg][0])> aaSensRed ) || (fabs(aaCol[aj*aaArrayX +ai +aOrg][1] - aaCol[(aj+0)*aaArrayX +(ai+currStep) +aOrg][1])> aaSensGreen ) || (fabs(aaCol[aj*aaArrayX +ai +aOrg][2] - aaCol[(aj+0)*aaArrayX +(ai+currStep) +aOrg][2])> aaSensBlue ) ) { thisColDiff = 1; } else if( (fabs(aaCol[aj*aaArrayX +ai +aOrg][0] - aaCol[(aj+currStep)*aaArrayX +(ai+0) +aOrg][0])> aaSensRed ) || (fabs(aaCol[aj*aaArrayX +ai +aOrg][1] - aaCol[(aj+currStep)*aaArrayX +(ai+0) +aOrg][1])> aaSensGreen ) || (fabs(aaCol[aj*aaArrayX +ai +aOrg][2] - aaCol[(aj+currStep)*aaArrayX +(ai+0) +aOrg][2])> aaSensBlue ) ) { thisColDiff = 1; } else if( (fabs(aaCol[aj*aaArrayX +ai +aOrg][0] - aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg][0])> aaSensRed ) || (fabs(aaCol[aj*aaArrayX +ai +aOrg][1] - aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg][1])> aaSensGreen ) || (fabs(aaCol[aj*aaArrayX +ai +aOrg][2] - aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg][2])> aaSensBlue ) ) { thisColDiff = 1; } //colDiff =1; if(thisColDiff) { /* set diff flag */ colDiff = thisColDiff; for(int bj=aj;bj<=aj+currStep;bj++) { for(int bi=ai;bi<=ai+currStep;bi++) { if(aaUse[bj*aaArrayX +bi +aOrg]==2) { //if(showAAPic) aaUse[bj*aaArrayX +bi +aOrg] = 0; } } } } else { /* set all values */ ntlColor avgCol = ( aaCol[(aj+0 )*aaArrayX +(ai+0 ) +aOrg] + aaCol[(aj+0 )*aaArrayX +(ai+currStep) +aOrg] + aaCol[(aj+currStep)*aaArrayX +(ai+0 ) +aOrg] + aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg] ) *0.25; for(int bj=aj;bj<=aj+currStep;bj++) { for(int bi=ai;bi<=ai+currStep;bi++) { if(aaUse[bj*aaArrayX +bi +aOrg]==0) { aaCol[bj*aaArrayX +bi +aOrg] = avgCol; aaUse[bj*aaArrayX +bi +aOrg] = 2; } } } } /* smaller values set */ } } /* half step size */ currStep /= 2; } /* repeat until diff not too big */ /* get average color */ gfxReal colNum = 0.0; col = ntlColor(0.0, 0.0, 0.0); for(aj = 0;aj<=aaLength;aj++) { for(ai = 0;ai<=aaLength;ai++) { col += aaCol[aj*aaArrayX +ai +aOrg]; colNum += 1.0; } } col /= colNum; } /* mark pixels with debugging */ if( glob->getDebugOut() > 0) col = ntlColor(0,1,0); /* store pixel */ if(!showAAPic) { finalPic[(scanline-1)*picX+sx] = col; } screenPos += rightStep; } /* foreach x */ /* init aa array */ if(showAAPic) { for(int j=0;j<=aaArrayY-1;j++) { for(int i=0;i<=aaArrayX-1;i++) { if(aaUse[j*aaArrayX +i]==1) finalPic[((scanline-1)*aaLength +j)*picX+i][0] = 1.0; } } } for(int i=0;i<aaArrayX;i++) { aaCol[(aaArrayY-1)*aaArrayX+i] = aaCol[0*aaArrayX+i]; aaUse[(aaArrayY-1)*aaArrayX+i] = aaUse[0*aaArrayX+i]; } for(int j=0;j<aaArrayY-1;j++) { for(int i=0;i<aaArrayX;i++) { aaCol[j*aaArrayX+i] = ntlColor(0.0, 0.0, 0.0); aaUse[j*aaArrayX+i] = 0; } } } /* foreach y */ rendEnd = getTime(); /* write png file */ { int w = picX; int h = picY; unsigned rowbytes = w*4; unsigned char *screenbuf, **rows; screenbuf = (unsigned char*)malloc( h*rowbytes ); rows = (unsigned char**)malloc( h*sizeof(unsigned char*) ); unsigned char *filler = screenbuf; // cutoff color values 0..1 for(int j=0;j<h;j++) { for(int i=0;i<w;i++) { ntlColor col = finalPic[j*w+i]; for (unsigned int cc=0; cc<3; cc++) { if(col[cc] <= 0.0) col[cc] = 0.0; if(col[cc] >= 1.0) col[cc] = 1.0; } *filler = (unsigned char)( col[0]*255.0 ); filler++; *filler = (unsigned char)( col[1]*255.0 ); filler++; *filler = (unsigned char)( col[2]*255.0 ); filler++; *filler = (unsigned char)( 255.0 ); filler++; // alpha channel } } for(int i = 0; i < h; i++) rows[i] = &screenbuf[ (h - i - 1)*rowbytes ]; writePng(outfn_conv.str().c_str(), rows, w, h); } // next frame glob->setAniCount( glob->getAniCount() +1 ); // done timeEnd = getTime(); char resout[1024]; snprintf(resout,1024, "NTL Done %s, frame %d/%d (took %s scene, %s raytracing, %s total, %d shades, %d i.s.'s)!\n", outfn_conv.str().c_str(), (glob->getAniCount()), (glob->getAniFrames()+1), getTimeString(totalStart-timeStart).c_str(), getTimeString(rendEnd-rendStart).c_str(), getTimeString(timeEnd-timeStart).c_str(), glob->getCounterShades(), glob->getCounterSceneInter() ); debMsgStd("ntlWorld::renderScene",DM_MSG, resout, 1 ); /* clean stuff up */ delete [] aaCol; delete [] aaUse; delete [] finalPic; glob->getRenderScene()->cleanupScene(); if(mpGlob->getSingleFrameMode() ) { debMsgStd("ntlWorld::renderScene",DM_NOTIFY, "Single frame mode done...", 1 ); return 1; } #endif // ELBEEM_PLUGIN return 0; }
//ntlTree::ntlTree(int depth, int objnum, vector<ntlVec3Gfx> *vertices, vector<ntlVec3Gfx> *normals, vector<ntlTriangle> *trilist) : ntlTree::ntlTree(int depth, int objnum, ntlScene *scene, int triFlagMask) : mStart(0.0), mEnd(0.0), mMaxDepth( depth ), mMaxListLength( objnum ), mpRoot( NULL) , mpNodeStack( NULL), mpTBB( NULL ), mTriangleMask( 0xFFFF ), mCurrentDepth(0), mCurrentNodes(0), mTriDoubles(0) { // init scene data pointers mpVertices = scene->getVertexPointer(); mpVertNormals = scene->getVertexNormalPointer(); mpTriangles = scene->getTrianglePointer(); mTriangleMask = triFlagMask; if(mpTriangles == NULL) { errFatal( "ntlTree Cons","no triangle list!\n",SIMWORLD_INITERROR); return; } if(mpTriangles->size() == 0) { warnMsg( "ntlTree::ntlTree","No triangles ("<< mpTriangles->size() <<")!\n"); mStart = mEnd = ntlVec3Gfx(0,0,0); return; } if(depth>=BSP_STACK_SIZE) { errFatal( "ntlTree::ntlTree","Depth to high ("<< mMaxDepth <<")!\n", SIMWORLD_INITERROR ); return; } /* check triangles (a bit inefficient, but we dont know which vertices belong to this tree), and generate bounding boxes */ mppTriangles = new vector<ntlTriangle *>; int noOfTriangles = mpTriangles->size(); mpTBB = new TriangleBBox[ noOfTriangles ]; int bbCount = 0; mStart = mEnd = (*mpVertices)[ mpTriangles->front().getPoints()[0] ]; //errMsg("TreeDebug","Start"); for (vector<ntlTriangle>::iterator iter = mpTriangles->begin(); iter != mpTriangles->end(); iter++ ) { //errorOut(" d "<< convertFlags2String((int)(*iter).getFlags()) <<" "<< convertFlags2String( (int)mTriangleMask)<<" add? "<<( ((int)(*iter).getFlags() & (int)mTriangleMask) != 0 ) ); // discard triangles that dont match mask if( ((int)(*iter).getFlags() & (int)mTriangleMask) == 0 ) { continue; } // test? TODO ntlVec3Gfx tnormal = (*mpVertNormals)[ (*iter).getPoints()[0] ]+ (*mpVertNormals)[ (*iter).getPoints()[1] ]+ (*mpVertNormals)[ (*iter).getPoints()[2] ]; ntlVec3Gfx triangleNormal = (*iter).getNormal(); if( equal(triangleNormal, ntlVec3Gfx(0.0)) ) continue; if( equal( tnormal, ntlVec3Gfx(0.0)) ) continue; // */ ntlVec3Gfx bbs, bbe; //errMsg("TreeDebug","Triangle"); for(int i=0;i<3;i++) { int index = (*iter).getPoints()[i]; ntlVec3Gfx tp = (*mpVertices)[ index ]; //errMsg("TreeDebug"," Point "<<i<<" = "<<tp<<" "); if(tp[0] < mStart[0]) mStart[0]= tp[0]; if(tp[0] > mEnd[0]) mEnd[0]= tp[0]; if(tp[1] < mStart[1]) mStart[1]= tp[1]; if(tp[1] > mEnd[1]) mEnd[1]= tp[1]; if(tp[2] < mStart[2]) mStart[2]= tp[2]; if(tp[2] > mEnd[2]) mEnd[2]= tp[2]; if(i==0) { bbs = bbe = tp; } else { if( tp[0] < bbs[0] ) bbs[0] = tp[0]; if( tp[0] > bbe[0] ) bbe[0] = tp[0]; if( tp[1] < bbs[1] ) bbs[1] = tp[1]; if( tp[1] > bbe[1] ) bbe[1] = tp[1]; if( tp[2] < bbs[2] ) bbs[2] = tp[2]; if( tp[2] > bbe[2] ) bbe[2] = tp[2]; } } mppTriangles->push_back( &(*iter) ); //errMsg("TreeDebug","Triangle "<<(*mpVertices)[(*iter).getPoints()[0]]<<" "<<(*mpVertices)[(*iter).getPoints()[1]]<<" "<<(*mpVertices)[(*iter).getPoints()[2]]<<" "); // add BB mpTBB[ bbCount ].start = bbs; mpTBB[ bbCount ].end = bbe; (*iter).setBBoxId( bbCount ); bbCount++; } /* slighlty enlarge bounding tolerance for tree to avoid problems with triangles paralell to slabs */ mStart -= ntlVec3Gfx( getVecEpsilon() ); mEnd += ntlVec3Gfx( getVecEpsilon() ); /* init root node and stack */ mpNodeStack = new BSPStack; mpRoot = new BSPNode; mpRoot->min = mStart; mpRoot->max = mEnd; mpRoot->axis = AXIS_X; mpRoot->members = mppTriangles; mpRoot->child[0] = mpRoot->child[1] = NULL; mpRoot->cloneVec = 0; globalSortingPoints = mpVertices; mpTriDist = new char[ mppTriangles->size() ]; mNumNodes = 1; mAbortSubdiv = 0; /* create tree */ debugOutInter( "Generating BSP Tree... (Nodes "<< mCurrentNodes << ", Depth "<<mCurrentDepth<< ") ", 2, 2000 ); subdivide(mpRoot, 0, AXIS_X); debMsgStd("ntlTree::ntlTree",DM_MSG,"Generated Tree: Nodes "<< mCurrentNodes << ", Depth "<<mCurrentDepth<< " with "<<noOfTriangles<<" triangles", 2 ); delete [] mpTriDist; delete [] mpTBB; mpTriDist = NULL; mpTBB = NULL; /* calculate some stats about tree */ int noLeafs = 0; gfxReal avgDepth = 0.0; gfxReal triPerLeaf = 0.0; int totalTris = 0; calcStats(mpRoot,0, noLeafs, avgDepth, triPerLeaf, totalTris); avgDepth /= (gfxReal)noLeafs; triPerLeaf /= (gfxReal)noLeafs; debMsgStd("ntlTree::ntlTree",DM_MSG,"Tree ("<<doSort<<","<<chooseAxis<<") Stats: Leafs:"<<noLeafs<<", avgDepth:"<<avgDepth<< ", triPerLeaf:"<<triPerLeaf<<", triDoubles:"<<mTriDoubles<<", totalTris:"<<totalTris <<" nodes:"<<mNumNodes //<<" T"<< (totalTris%3) // 0=ich, 1=f, 2=a , 2 ); if(mAbortSubdiv) { errMsg("ntlTree::ntlTree","Aborted... "<<mNumNodes); deleteNode(mpRoot); mpRoot = NULL; } }