void end( const TWallVertexVector& vb, TIntVector& polygon, TIntVector& ib ) { assert( polygonCount ); if( polygonCount == 1 ) { // trivial case, just output input polygon polygon.resize( 0 ); polygon.reserve( vertices.size() ); int idx0 = *vertices.begin(); int idx = idx0; do { polygon.push_back( idx ); const TIntVector& vnext = vertexNexts[idx]; assert( vnext.size() == 1 ); idx = vnext[0]; } while( idx != idx0 ); triangulator::process( vb, polygon, ib ); } else { // mark vertex types markVertexTypes(); // trace and triangulate the polygon(s) traceBorder( vb, polygon, ib ); } }
void CWall3D::fracturePiecesInYRange( float t, float y1, float y2, TIntVector& pcs ) { if( !mPiecesInited ) initPieces(); // fetch the pieces pcs.resize( 0 ); // TODO: optimize, right now linear search! float pieceRestoreTime = t + 1.0e6f; int n = mWall2D.getPieceCount(); for( int i = 0; i < n; ++i ) { const CWallPiece2D& p = mWall2D.getPiece( i ); SVector2 c = p.getAABB().getCenter(); float y = c.y; if( mMatrix.getAxisY().y < 0.5f ) { SVector3 wc; D3DXVec3TransformCoord( &wc, &SVector3(c.x,c.y,0), &mMatrix ); y = wc.y; } if( y >= y1 && y <= y2 ) { mPieceRestoreTimes[i] = pieceRestoreTime; if( mFracturedPieces[i] ) continue; pcs.push_back( i ); fractureOutPiece( i ); } } }
void begin( int totalVerts ) { polygonCount = 0; vertices.clear(); borderVertices.clear(); vertexNexts.clear(); vertexPrevs.clear(); vertexUseCount.resize( 0 ); vertexUseCount.resize( totalVerts, 0 ); vertexTraceID.resize( totalVerts, -1 ); vertexTypes.resize( 0 ); vertexTypes.resize( totalVerts, VTYPE_NONE ); }
void CWall3D::fracturePiecesInSphere( float t, const SVector3& pos, float radius, TIntVector& pcs, float restoreAfter, float restoreDuration, bool noRestore ) { if( !mPiecesInited ) initPieces(); pcs.resize( 0 ); // to local space SVector3 locPos; D3DXVec3TransformCoord( &locPos, &pos, &mInvMatrix ); if( locPos.z < -radius || locPos.z > radius ) return; // remember restore times if( mResTimeGrid && !noRestore ) { float rad = radius*2.0f; float lx1 = (locPos.x - rad) / mWall2D.getSize().x * RESGRID_X; float lx2 = (locPos.x + rad) / mWall2D.getSize().x * RESGRID_X; float ly1 = (locPos.y - rad) / mWall2D.getSize().y * RESGRID_Y; float ly2 = (locPos.y + rad) / mWall2D.getSize().y * RESGRID_Y; int ix1 = (int)clamp( lx1, 0, RESGRID_X-1 ); int ix2 = (int)clamp( lx2, 0, RESGRID_X-1 ); int iy1 = (int)clamp( ly1, 0, RESGRID_Y-1 ); int iy2 = (int)clamp( ly2, 0, RESGRID_Y-1 ); float dx = mWall2D.getSize().x / RESGRID_X; float dy = mWall2D.getSize().y / RESGRID_Y; for( int iy = iy1; iy <= iy2; ++iy ) { float* resval = mResTimeGrid + RESGRID_X*iy + ix1; float fy = iy * dy; for( int ix = ix1; ix <= ix2; ++ix, ++resval ) { float fx = ix * dx; // don't touch restore grid outside the circle float diffX = fx-locPos.x; float diffY = fy-locPos.y; float diffR2 = diffX*diffX + diffY*diffY; if( diffR2 > rad*rad ) continue; // restore time for this grid point - start at // t+restoreAfter at circle boundaries, later at circle // center float resTime = t + restoreAfter + (1.0f-diffR2/(rad*rad)) * restoreDuration; if( *resval < 0.0f ) *resval = resTime; else *resval = max( (*resval), resTime ); } } } // fetch the pieces float pieceRestoreTime; if( noRestore ) { pieceRestoreTime = t + 1.0e9f; } else { pieceRestoreTime = t + restoreAfter + restoreDuration; } // TODO: optimize, right now linear search! int n = mWall2D.getPieceCount(); for( int i = 0; i < n; ++i ) { const CWallPiece2D& p = mWall2D.getPiece( i ); SVector2 c = p.getAABB().getCenter(); SVector3 tocenter = locPos - SVector3(c.x,c.y,0); if( tocenter.lengthSq() < radius*radius ) { mPieceRestoreTimes[i] = pieceRestoreTime; if( mFracturedPieces[i] ) continue; pcs.push_back( i ); fractureOutPiece( i ); } } }
void CWallPieceCombined::initEnd( TWallQuadNode* quadtree ) { assert( mInitPiece == this ); assert( mInitWall ); // if we're root, just construct the full-wall quad if( mInitRoot ) { assert( mQuadNode == quadtree ); mVB.resize( 4 ); mIB.resize( 6 ); const SVector2& bmin = mBounds.getMin(); const SVector2& bmax = mBounds.getMax(); // VB const SMatrix4x4& mat = mInitWall->getMatrix(); { DWORD nrmCol = gVectorToColor( mat.getAxisZ() ); SVertexXyzDiffuse vtx; vtx.p.set( bmin.x, bmin.y, 0 ); D3DXVec3TransformCoord( &vtx.p, &vtx.p, &mat ); vtx.diffuse = nrmCol; mVB[0] = vtx; vtx.p.set( bmin.x, bmax.y, 0 ); D3DXVec3TransformCoord( &vtx.p, &vtx.p, &mat ); vtx.diffuse = nrmCol; mVB[1] = vtx; vtx.p.set( bmax.x, bmin.y, 0 ); D3DXVec3TransformCoord( &vtx.p, &vtx.p, &mat ); vtx.diffuse = nrmCol; mVB[2] = vtx; vtx.p.set( bmax.x, bmax.y, 0 ); D3DXVec3TransformCoord( &vtx.p, &vtx.p, &mat ); vtx.diffuse = nrmCol; mVB[3] = vtx; } // IB { mIB[0] = 0; mIB[1] = 1; mIB[2] = 2; mIB[3] = 1; mIB[4] = 3; mIB[5] = 2; } mVBSizeNoCaps = mVB.size(); mIBSizeNoCaps = mIB.size(); } else { // for non-roots, construct proper polygon const TWallVertexVector& wallVerts = mInitWall->getWall2D().getVerts(); // merge the polygons TIntVector polygon; TIntVector ibFront; polygon_merger::end( wallVerts, polygon, ibFront ); static TIntVector vertRemap; vertRemap.resize(0); vertRemap.resize( wallVerts.size(), -1 ); int i; int nidx = ibFront.size(); mIB.reserve( nidx + polygon.size()*6 ); mVB.reserve( polygon.size()*3 ); // construct the front side for( i = 0; i < nidx; ++i ) { int oldIdx = ibFront[i]; int newIdx = vertRemap[oldIdx]; if( newIdx < 0 ) { newIdx = mVB.size(); vertRemap[oldIdx] = newIdx; SVector2 pos = wallVerts[oldIdx]; SVector3 pos3( pos.x, pos.y, 0.0f ); D3DXVec3TransformCoord( &pos3, &pos3, &mInitWall->getMatrix() ); SVertexXyzDiffuse vtx; vtx.p = pos3; vtx.diffuse = gVectorToColor( mInitWall->getMatrix().getAxisZ() ); mVB.push_back( vtx ); } mIB.push_back( newIdx ); } // reverse the ordering of triangles for( i = 0; i < nidx/3; ++i ) { int iii = mIB[i*3+1]; mIB[i*3+1] = mIB[i*3+2]; mIB[i*3+2] = iii; } mVBSizeNoCaps = mVB.size(); mIBSizeNoCaps = mIB.size(); // Don't construct the side caps for non-leaf pieces. // I think they never can be seen (not sure) if( !mQuadNode ) { int nverts = mVB.size(); int npolygon = polygon.size(); // Construct side caps. To conserve the geometry amount, // treat them as a single smoothing group. for( i = 0; i < nverts; ++i ) { int oldIdx0 = polygon[i]; int oldIdx1 = polygon[(i+1)%npolygon]; int oldIdx2 = polygon[(i+nverts-1)%npolygon]; int idx0 = vertRemap[oldIdx0]; int idx1 = vertRemap[oldIdx1]; int idx2 = vertRemap[oldIdx2]; assert( idx0 >= 0 && idx0 < nverts ); assert( idx1 >= 0 && idx1 < nverts ); assert( idx2 >= 0 && idx2 < nverts ); SVertexXyzDiffuse v0 = mVB[idx0]; SVertexXyzDiffuse v1 = v0; v1.p -= mInitWall->getMatrix().getAxisZ() * (HALF_THICK*2); const SVertexXyzDiffuse& vnear1 = mVB[idx1]; const SVertexXyzDiffuse& vnear2 = mVB[idx2]; SVector3 edge1 = vnear2.p - vnear1.p; SVector3 edge2 = v1.p - v0.p; SVector3 normal = edge1.cross( edge2 ).getNormalized(); v0.diffuse = v1.diffuse = gVectorToColor( normal ); mVB.push_back( v0 ); mVB.push_back( v1 ); int ibidx0 = i*2 + 0; int ibidx1 = i*2 + 1; int ibidx2 = ((i+1)%nverts)*2 + 0; int ibidx3 = ((i+1)%nverts)*2 + 1; mIB.push_back( ibidx0 ); mIB.push_back( ibidx2 ); mIB.push_back( ibidx1 ); mIB.push_back( ibidx1 ); mIB.push_back( ibidx2 ); mIB.push_back( ibidx3 ); } } } if( !mQuadNode ) { assert( mCombinedPieces.size()==1 ); TWallQuadNode* node = quadtree->getNode( mBounds ); assert( node ); while( node ) { node->getData().leafs.push_back( this ); node = node->getParent(); } } else { // record ourselves in the quadtree assert( !mQuadNode->getData().combined ); mQuadNode->getData().combined = this; } mInitPiece = NULL; mInitWall = NULL; }
void CWallPiece3D::init( const CWall3D& w, int idx ) { const CWallPiece2D& piece = w.getWall2D().getPiece(idx); // construct VB/IB for this piece, with positions centered SVector2 pcenter = piece.getAABB().getCenter(); static TIntVector vertRemap; vertRemap.resize(0); vertRemap.resize( w.getWall2D().getVerts().size(), -1 ); int i; int nidx = piece.getTriCount()*3; mIB.reserve( nidx * 2 + piece.getVertexCount()*6 ); mVB.reserve( piece.getVertexCount()*4 ); // construct one side for( i = 0; i < nidx; ++i ) { int oldIdx = piece.getIB()[i]; int newIdx = vertRemap[oldIdx]; if( newIdx < 0 ) { newIdx = mVB.size(); vertRemap[oldIdx] = newIdx; SVector2 pos = w.getWall2D().getVerts()[oldIdx]; pos -= pcenter; SVertexXyzNormal vtx; vtx.p.set( pos.x, pos.y, HALF_THICK ); vtx.n.set( 0, 0, 1 ); mVB.push_back( vtx ); } mIB.push_back( newIdx ); } for( i = 0; i < nidx/3; ++i ) { int iii = mIB[i*3+1]; mIB[i*3+1] = mIB[i*3+2]; mIB[i*3+2] = iii; } // construct another side int nverts = mVB.size(); //int npolygon = piece.getPolygon().size(); for( i = 0; i < nverts; ++i ) { SVertexXyzNormal vtx = mVB[i]; vtx.p.z = -vtx.p.z; vtx.n.set( 0,0, -1 ); mVB.push_back( vtx ); } for( i = 0; i < nidx/3; ++i ) { int idx0 = mIB[i*3+0]; int idx1 = mIB[i*3+1]; int idx2 = mIB[i*3+2]; mIB.push_back( idx0 + nverts ); mIB.push_back( idx2 + nverts ); mIB.push_back( idx1 + nverts ); } // construct side caps. To conserve the geometry amount, // treat them as a single smoothing group. assert( nverts == piece.getVertexCount() ); for( i = 0; i < nverts; ++i ) { int oldIdx0 = piece.getPolygon()[i]; int oldIdx1 = piece.getPolygon()[(i+1)%nverts]; int oldIdx2 = piece.getPolygon()[(i+nverts-1)%nverts]; int idx0 = vertRemap[oldIdx0]; int idx1 = vertRemap[oldIdx1]; int idx2 = vertRemap[oldIdx2]; assert( idx0 >= 0 && idx0 < nverts ); assert( idx1 >= 0 && idx1 < nverts ); assert( idx2 >= 0 && idx2 < nverts ); SVertexXyzNormal v0 = mVB[idx0]; SVertexXyzNormal v1 = v0; v1.p.z = -v1.p.z; const SVertexXyzNormal& vnear1 = mVB[idx1]; const SVertexXyzNormal& vnear2 = mVB[idx2]; SVector3 edge1 = vnear2.p - vnear1.p; SVector3 edge2 = v1.p - v0.p; SVector3 normal = edge1.cross( edge2 ).getNormalized(); v0.n = v1.n = normal; mVB.push_back( v0 ); mVB.push_back( v1 ); int ibidx0 = nverts*2 + i*2 + 0; int ibidx1 = nverts*2 + i*2 + 1; int ibidx2 = nverts*2 + ((i+1)%nverts)*2 + 0; int ibidx3 = nverts*2 + ((i+1)%nverts)*2 + 1; mIB.push_back( ibidx0 ); mIB.push_back( ibidx2 ); mIB.push_back( ibidx1 ); mIB.push_back( ibidx1 ); mIB.push_back( ibidx2 ); mIB.push_back( ibidx3 ); } // construct initial mMatrix mMatrix.identify(); mMatrix = w.getMatrix(); mMatrix.getOrigin() += mMatrix.getAxisX() * pcenter.x; mMatrix.getOrigin() += mMatrix.getAxisY() * pcenter.y; mSize.set( piece.getAABB().getSize().x, piece.getAABB().getSize().y, HALF_THICK*2 ); }
void traceBorder( const TWallVertexVector& vb, TIntVector& polygon, TIntVector& ib ) { static int traceID = 0; ++traceID; int idx0 = getBorderIndex(); assert( idx0 >= 0 && idx0 < vertexTypes.size() ); ib.resize( 0 ); polygon.resize( 0 ); polygon.reserve( vertices.size()/2 ); TIntVector localPolygon; localPolygon.reserve( 128 ); TIntVector localIB; localIB.reserve( 128 ); int idxPrev = idx0; int idx = idx0; int debugCounter = 0; int debugLoopCounter = 0; do { localIB.resize( 0 ); bool willFormLoop; do{ localPolygon.push_back( idx ); borderVertices.erase( idx ); vertexTraceID[idx] = traceID; assert( ++debugCounter <= vertices.size()*2 ); // Next vertex is the neighbor of current, that is not interior // and that is not the previous one. // When there are many possible ones, trace based on angle. int idxNext = -1; SVector2 prevToCurr = vb[idx] - vb[idxPrev]; if( prevToCurr.lengthSq() < 1.0e-6f ) prevToCurr.set( -0.01f, -0.01f ); TIntIntsMap::const_iterator it; it = vertexNexts.find( idx ); assert( it != vertexNexts.end() ); const TIntVector& vnext = it->second; int n = vnext.size(); float bestAngle = 100.0f; for( int i = 0; i < n; ++i ) { int idx1 = vnext[i]; if( idx1 != idxPrev && vertexTypes[idx1] != VTYPE_INTERIOR ) { //if( idx1 != idxPrev ) { SVector2 currToNext = vb[idx1] - vb[idx]; float ang = signedAngle2D( prevToCurr, currToNext ); if( ang < bestAngle ) { bestAngle = ang; idxNext = idx1; } } } assert( bestAngle > -4.0f && bestAngle < 4.0f ); assert( idxNext >= 0 ); willFormLoop = (vertexTraceID[idxNext] == traceID); // Optimization: if best angle is zero, then we're walking // in a straight line. Optimize out the current vertex. if( bestAngle == 0.0f && idx != idx0 && !willFormLoop ) { localPolygon.pop_back(); } idxPrev = idx; idx = idxNext; } while( !willFormLoop ); assert( localPolygon.size() >= 3 ); //if( localPolygon.size() < 3 ) { // return; //} assert( ++debugLoopCounter < vertices.size() ); if( idx == idx0 ) { // The polygon is simple or we found the last loop. // Triangulate local and append to results. triangulator::process( vb, localPolygon, localIB ); polygon.insert( polygon.end(), localPolygon.begin(), localPolygon.end() ); ib.insert( ib.end(), localIB.begin(), localIB.end() ); // We can have separated other loops. Try fetching them as well. idx0 = getBorderIndex(); if( idx0 == -1 ) { return; } else { localPolygon.resize( 0 ); idxPrev = idx0; idx = idx0; } } else { // The polygon must be complex, and we just found a closed loop. // Take only the loop, triangulate it, append to results, continue. TIntVector::const_iterator itLoopStart = std::find( localPolygon.begin(), localPolygon.end(), idx ); assert( itLoopStart != localPolygon.end() ); // append to results TIntVector loopPolygon( itLoopStart, localPolygon.end() ); triangulator::process( vb, loopPolygon, localIB ); polygon.insert( polygon.end(), loopPolygon.begin(), loopPolygon.end() ); ib.insert( ib.end(), localIB.begin(), localIB.end() ); // continue - remove the looped polygon from local localPolygon.resize( itLoopStart - localPolygon.begin() ); } } while( true ); }