void TerrCell::cullCells( const SceneRenderState *state, const Point3F &objLodPos, Vector<TerrCell*> *outCells ) { // If we have a VB and no children then just add // ourselves to the results and return. if ( mVertexBuffer.isValid() && !mChildren[0] ) { outCells->push_back( this ); return; } const F32 screenError = mTerrain->getScreenError(); const BitVector &zoneState = state->getCullingState().getZoneVisibilityFlags(); for ( U32 i = 0; i < 4; i++ ) { TerrCell *cell = mChildren[i]; // Test cell visibility for interior zones. const bool visibleInside = !cell->getZoneOverlap().empty() ? zoneState.testAny( cell->getZoneOverlap() ) : false; // Test cell visibility for outdoor zone, but only // if we need to. bool visibleOutside = false; if( !mIsInteriorOnly && !visibleInside ) { U32 outdoorZone = SceneZoneSpaceManager::RootZoneId; visibleOutside = !state->getCullingState().isCulled( cell->mOBB, &outdoorZone, 1 ); } // Skip cell if neither visible indoors nor outdoors. if( !visibleInside && !visibleOutside ) continue; // Lod based on screen error... // If far enough, just add this child cells vb ( skipping its children ). F32 dist = cell->getDistanceTo( objLodPos ); F32 errorMeters = ( cell->mSize / smMinCellSize ) * mTerrain->getSquareSize(); U32 errorPixels = mCeil( state->projectRadius( dist, errorMeters ) ); if ( errorPixels < screenError ) { if ( cell->mVertexBuffer.isValid() ) outCells->push_back( cell ); } else cell->cullCells( state, objLodPos, outCells ); } }
/** * Expands the capacity of the array so that it automatically has * enough space. It is 1.5x size growth */ void expand() { mAllocSize = mCeil(mMax(1, mAllocSize) * 1.5f); mArray = reinterpret_cast<T*>(realloc(mArray, mAllocSize * sizeof(T))); // we could potentially be allocating a LOT of memory. Check to make sure // the allocation was successful. if (mArray == nullptr) { // TODO: Implement some message box in here to say we are out of // memory. exit(-1); } }
bool LightAnimData::AnimValue<COUNT>::animate(F32 time, F32 *output, bool multiply) { F32 scaledTime, lerpFactor, valueRange, keyFrameLerp; U32 posFrom, posTo; S32 keyFrameFrom, keyFrameTo; F32 initialValue = *output; if (!multiply) initialValue = 1; bool wasAnimated = false; for ( U32 i=0; i < COUNT; i++ ) { if ( mIsZero( timeScale[i] ) ) continue; wasAnimated = true; scaledTime = mFmod( time, period[i] ) * timeScale[i]; posFrom = mFloor( scaledTime ); posTo = mCeil( scaledTime ); keyFrameFrom = dToupper( keys[i][posFrom] ) - 65; keyFrameTo = dToupper( keys[i][posTo] ) - 65; valueRange = ( value2[i] - value1[i] ) / 25.0f; if ( !smooth[i] ) output[i] = (value1[i] + (keyFrameFrom * valueRange)) * initialValue; else { lerpFactor = scaledTime - posFrom; keyFrameLerp = ( keyFrameTo - keyFrameFrom ) * lerpFactor; output[i] = (value1[i] + ((keyFrameFrom + keyFrameLerp) * valueRange)) * initialValue; } } return wasAnimated; }
void DecalRoad::_generateEdges() { PROFILE_SCOPE( DecalRoad_generateEdges ); //Con::warnf( "%s - generateEdges", isServerObject() ? "server" : "client" ); if ( mNodes.size() > 0 ) { // Set our object position to the first node. const Point3F &nodePt = mNodes.first().point; MatrixF mat( true ); mat.setPosition( nodePt ); Parent::setTransform( mat ); // The server object has global bounds, which Parent::setTransform // messes up so we must reset it. if ( isServerObject() ) { mObjBox.minExtents.set(-1e10, -1e10, -1e10); mObjBox.maxExtents.set( 1e10, 1e10, 1e10); } } if ( mNodes.size() < 2 ) return; // Ensure nodes are above the terrain height at their xy position for ( U32 i = 0; i < mNodes.size(); i++ ) { _getTerrainHeight( mNodes[i].point ); } // Now start generating edges... U32 nodeCount = mNodes.size(); Point3F *positions = new Point3F[nodeCount]; for ( U32 i = 0; i < nodeCount; i++ ) { const RoadNode &node = mNodes[i]; positions[i].set( node.point.x, node.point.y, node.width ); } CatmullRom<Point3F> spline; spline.initialize( nodeCount, positions ); delete [] positions; mEdges.clear(); Point3F lastBreakVector(0,0,0); RoadEdge slice; Point3F lastBreakNode; lastBreakNode = spline.evaluate(0.0f); for ( U32 i = 1; i < mNodes.size(); i++ ) { F32 t1 = spline.getTime(i); F32 t0 = spline.getTime(i-1); F32 segLength = spline.arcLength( t0, t1 ); U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT ); numSegments = getMax( numSegments, (U32)1 ); F32 tstep = ( t1 - t0 ) / numSegments; U32 startIdx = 0; U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; for ( U32 j = startIdx; j < endIdx; j++ ) { F32 t = t0 + tstep * j; Point3F splineNode = spline.evaluate(t); F32 width = splineNode.z; _getTerrainHeight( splineNode ); Point3F toNodeVec = splineNode - lastBreakNode; toNodeVec.normalizeSafe(); if ( lastBreakVector.isZero() ) lastBreakVector = toNodeVec; F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) ); if ( j == startIdx || ( j == endIdx - 1 && i == mNodes.size() - 1 ) || angle > mBreakAngle ) { // Push back a spline node //slice.p1.set( splineNode.x, splineNode.y, 0.0f ); //_getTerrainHeight( slice.p1 ); slice.p1 = splineNode; slice.uvec.set(0,0,1); slice.width = width; slice.parentNodeIdx = i-1; mEdges.push_back( slice ); lastBreakVector = splineNode - lastBreakNode; lastBreakVector.normalizeSafe(); lastBreakNode = splineNode; } } } /* for ( U32 i = 1; i < nodeCount; i++ ) { F32 t0 = spline.getTime( i-1 ); F32 t1 = spline.getTime( i ); F32 segLength = spline.arcLength( t0, t1 ); U32 numSegments = mCeil( segLength / mBreakAngle ); numSegments = getMax( numSegments, (U32)1 ); F32 tstep = ( t1 - t0 ) / numSegments; AssertFatal( numSegments > 0, "DecalRoad::_generateEdges, got zero segments!" ); U32 startIdx = 0; U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; for ( U32 j = startIdx; j < endIdx; j++ ) { F32 t = t0 + tstep * j; Point3F val = spline.evaluate(t); RoadEdge edge; edge.p1.set( val.x, val.y, 0.0f ); _getTerrainHeight( val.x, val.y, edge.p1.z ); edge.uvec.set(0,0,1); edge.width = val.z; edge.parentNodeIdx = i-1; mEdges.push_back( edge ); } } */ // // Calculate fvec and rvec for all edges // RoadEdge *edge = NULL; RoadEdge *nextEdge = NULL; for ( U32 i = 0; i < mEdges.size() - 1; i++ ) { edge = &mEdges[i]; nextEdge = &mEdges[i+1]; edge->fvec = nextEdge->p1 - edge->p1; edge->fvec.normalize(); edge->rvec = mCross( edge->fvec, edge->uvec ); edge->rvec.normalize(); } // Must do the last edge outside the loop RoadEdge *lastEdge = &mEdges[mEdges.size()-1]; RoadEdge *prevEdge = &mEdges[mEdges.size()-2]; lastEdge->fvec = prevEdge->fvec; lastEdge->rvec = prevEdge->rvec; // // Calculate p0/p2 for all edges // for ( U32 i = 0; i < mEdges.size(); i++ ) { RoadEdge *edge = &mEdges[i]; edge->p0 = edge->p1 - edge->rvec * edge->width * 0.5f; edge->p2 = edge->p1 + edge->rvec * edge->width * 0.5f; _getTerrainHeight( edge->p0 ); _getTerrainHeight( edge->p2 ); } }
void GroundPlane::createGeometry( const Frustum& frustum ) { PROFILE_SCOPE( GroundPlane_createGeometry ); enum { MAX_WIDTH = 256, MAX_HEIGHT = 256 }; // Project the frustum onto the XY grid. Point2F min; Point2F max; projectFrustum( frustum, mSquareSize, min, max ); // Early out if the grid projection hasn't changed. if( mVertexBuffer.isValid() && min == mMin && max == mMax ) return; mMin = min; mMax = max; // Determine the grid extents and allocate the buffers. // Adjust square size permanently if with the given frustum, // we end up producing more than a certain limit of geometry. // This is to prevent this code from causing trouble with // long viewing distances. // This only affects the client object, of course, and thus // has no permanent effect. U32 width = mCeil( ( max.x - min.x ) / mSquareSize ); if( width > MAX_WIDTH ) { mSquareSize = mCeil( ( max.x - min.x ) / MAX_WIDTH ); width = MAX_WIDTH; } else if( !width ) width = 1; U32 height = mCeil( ( max.y - min.y ) / mSquareSize ); if( height > MAX_HEIGHT ) { mSquareSize = mCeil( ( max.y - min.y ) / MAX_HEIGHT ); height = MAX_HEIGHT; } else if( !height ) height = 1; const U32 numVertices = ( width + 1 ) * ( height + 1 ); const U32 numTriangles = width * height * 2; // Only reallocate if the buffers are too small. if ( mVertexBuffer.isNull() || numVertices > mVertexBuffer->mNumVerts ) { #if defined(TORQUE_OS_XENON) mVertexBuffer.set( GFX, numVertices, GFXBufferTypeVolatile ); #else mVertexBuffer.set( GFX, numVertices, GFXBufferTypeDynamic ); #endif } if ( mPrimitiveBuffer.isNull() || numTriangles > mPrimitiveBuffer->mPrimitiveCount ) { #if defined(TORQUE_OS_XENON) mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeVolatile ); #else mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeDynamic ); #endif } // Generate the grid. generateGrid( width, height, mSquareSize, min, max, mVertexBuffer, mPrimitiveBuffer ); // Set up GFX primitive. mPrimitive.type = GFXTriangleList; mPrimitive.numPrimitives = numTriangles; mPrimitive.numVertices = numVertices; }
bool TerrainBlock::buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF&) { if (box.maxExtents.z < -TerrainThickness || box.minExtents.z > fixedToFloat(gridMap[BlockShift]->maxHeight)) return false; // Transform the bounding sphere into the object's coord space. Note that this // not really optimal. Box3F osBox = box; mWorldToObj.mul(osBox); AssertWarn(mObjScale == Point3F::One, "Error, handle the scale transform on the terrain"); // Setup collision state data polyList->setTransform(&getTransform(), getScale()); polyList->setObject(this); S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize ); S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize ); S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize ); S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize ); if (!mTile && xStart<0) xStart = 0; S32 xExt = xEnd - xStart; if (xExt > MaxExtent) xExt = MaxExtent; xEnd = xStart + xExt; mHeightMax = floatToFixed(osBox.maxExtents.z); mHeightMin = (osBox.minExtents.z < 0.0f)? 0.0f: floatToFixed(osBox.minExtents.z); // Index of shared points U32 bp[(MaxExtent + 1) * 2],*vb[2]; vb[0] = &bp[0]; vb[1] = &bp[xExt + 1]; clrbuf(vb[1],xExt + 1); bool emitted = false; for (S32 y = yStart; y < yEnd; y++) { S32 yi = y & BlockMask; swap(vb[0],vb[1]); clrbuf(vb[1],xExt + 1); // for (S32 x = xStart; x < xEnd; x++) { S32 xi = x & BlockMask; GridSquare *gs = findSquare(0, Point2I(xi, yi)); // If we disable repeat, then skip non-primary if(!mTile && (x!=xi || y!=yi)) continue; // holes only in the primary terrain block if (((gs->flags & GridSquare::Empty) && x == xi && y == yi) || gs->minHeight > mHeightMax || gs->maxHeight < mHeightMin) continue; emitted = true; // Add the missing points U32 vi[5]; for (int i = 0; i < 4 ; i++) { S32 dx = i >> 1; S32 dy = dx ^ (i & 1); U32* vp = &vb[dy][x - xStart + dx]; if (*vp == U32_MAX) { Point3F pos; pos.x = (F32)((x + dx) * mSquareSize); pos.y = (F32)((y + dy) * mSquareSize); pos.z = fixedToFloat(getHeight(xi + dx, yi + dy)); *vp = polyList->addPoint(pos); } vi[i] = *vp; } U32* vp = &vi[0]; if (!(gs->flags & GridSquare::Split45)) vi[4] = vi[0], vp++; BaseMatInstance* material = getMaterialInst( xi, yi ); U32 surfaceKey = ((xi << 16) + yi) << 1; polyList->begin(material,surfaceKey); polyList->vertex(vp[0]); polyList->vertex(vp[1]); polyList->vertex(vp[2]); polyList->plane(vp[0],vp[1],vp[2]); polyList->end(); polyList->begin(material,surfaceKey + 1); polyList->vertex(vp[0]); polyList->vertex(vp[2]); polyList->vertex(vp[3]); polyList->plane(vp[0],vp[2],vp[3]); polyList->end(); } } return emitted; }
void TerrainBlock::buildConvex(const Box3F& box,Convex* convex) { sTerrainConvexList.collectGarbage(); // if (box.maxExtents.z < -TerrainThickness || box.minExtents.z > fixedToFloat(gridMap[BlockShift]->maxHeight)) return; // Transform the bounding sphere into the object's coord space. Note that this // not really optimal. Box3F osBox = box; mWorldToObj.mul(osBox); AssertWarn(mObjScale == Point3F(1, 1, 1), "Error, handle the scale transform on the terrain"); S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize ); S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize ); S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize ); S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize ); S32 xExt = xEnd - xStart; if (xExt > MaxExtent) xExt = MaxExtent; mHeightMax = floatToFixed(osBox.maxExtents.z); mHeightMin = (osBox.minExtents.z < 0)? 0: floatToFixed(osBox.minExtents.z); for (S32 y = yStart; y < yEnd; y++) { S32 yi = y & BlockMask; // for (S32 x = xStart; x < xEnd; x++) { S32 xi = x & BlockMask; GridSquare *gs = findSquare(0, Point2I(xi, yi)); // If we disable repeat, then skip non-primary if(!mTile && (x!=xi || y!=yi)) continue; // holes only in the primary terrain block if (((gs->flags & GridSquare::Empty) && x == xi && y == yi) || gs->minHeight > mHeightMax || gs->maxHeight < mHeightMin) continue; U32 sid = (x << 16) + (y & ((1 << 16) - 1)); Convex* cc = 0; // See if the square already exists as part of the working set. CollisionWorkingList& wl = convex->getWorkingList(); for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) if (itr->mConvex->getType() == TerrainConvexType && static_cast<TerrainConvex*>(itr->mConvex)->squareId == sid) { cc = itr->mConvex; break; } if (cc) continue; // Create a new convex. TerrainConvex* cp = new TerrainConvex; sTerrainConvexList.registerObject(cp); convex->addToWorkingList(cp); cp->halfA = true; cp->square = 0; cp->mObject = this; cp->squareId = sid; cp->material = getMaterial(xi,yi)->index;//RDTODO cp->box.minExtents.set((F32)(x * mSquareSize), (F32)(y * mSquareSize), fixedToFloat(gs->minHeight)); cp->box.maxExtents.x = cp->box.minExtents.x + mSquareSize; cp->box.maxExtents.y = cp->box.minExtents.y + mSquareSize; cp->box.maxExtents.z = fixedToFloat(gs->maxHeight); mObjToWorld.mul(cp->box); // Build points Point3F* pos = cp->point; for (int i = 0; i < 4 ; i++,pos++) { S32 dx = i >> 1; S32 dy = dx ^ (i & 1); pos->x = (F32)((x + dx) * mSquareSize); pos->y = (F32)((y + dy) * mSquareSize); pos->z = fixedToFloat(getHeight(xi + dx, yi + dy)); } // Build normals, then split into two Convex objects if the // square is concave if ((cp->split45 = gs->flags & GridSquare::Split45) == true) { VectorF *vp = cp->point; mCross(vp[0] - vp[1],vp[2] - vp[1],&cp->normal[0]); cp->normal[0].normalize(); mCross(vp[2] - vp[3],vp[0] - vp[3],&cp->normal[1]); cp->normal[1].normalize(); if (mDot(vp[3] - vp[1],cp->normal[0]) > 0) { TerrainConvex* nc = new TerrainConvex(*cp); sTerrainConvexList.registerObject(nc); convex->addToWorkingList(nc); nc->halfA = false; nc->square = cp; cp->square = nc; } } else { VectorF *vp = cp->point; mCross(vp[3] - vp[0],vp[1] - vp[0],&cp->normal[0]); cp->normal[0].normalize(); mCross(vp[1] - vp[2],vp[3] - vp[2],&cp->normal[1]); cp->normal[1].normalize(); if (mDot(vp[2] - vp[0],cp->normal[0]) > 0) { TerrainConvex* nc = new TerrainConvex(*cp); sTerrainConvexList.registerObject(nc); convex->addToWorkingList(nc); nc->halfA = false; nc->square = cp; cp->square = nc; } } } } }
bool TerrainBlock::buildPolyList(PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF&) { PROFILE_SCOPE( TerrainBlock_buildPolyList ); // First check to see if the query misses the // terrain elevation range. const Point3F &terrainPos = getPosition(); if ( box.maxExtents.z - terrainPos.z < -TerrainThickness || box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) ) return false; // Transform the bounding sphere into the object's coord // space. Note that this is really optimal. Box3F osBox = box; mWorldToObj.mul(osBox); AssertWarn(mObjScale == Point3F::One, "Error, handle the scale transform on the terrain"); // Setup collision state data polyList->setTransform(&getTransform(), getScale()); polyList->setObject(this); S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize ); S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize ); S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize ); S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize ); if ( xStart < 0 ) xStart = 0; S32 xExt = xEnd - xStart; if ( xExt > MaxExtent ) xExt = MaxExtent; xEnd = xStart + xExt; U32 heightMax = floatToFixed(osBox.maxExtents.z); U32 heightMin = (osBox.minExtents.z < 0.0f)? 0.0f: floatToFixed(osBox.minExtents.z); // Index of shared points U32 bp[(MaxExtent + 1) * 2],*vb[2]; vb[0] = &bp[0]; vb[1] = &bp[xExt + 1]; clrbuf(vb[1],xExt + 1); const U32 BlockMask = mFile->mSize - 1; bool emitted = false; for (S32 y = yStart; y < yEnd; y++) { S32 yi = y & BlockMask; swap(vb[0],vb[1]); clrbuf(vb[1],xExt + 1); // for (S32 x = xStart; x < xEnd; x++) { S32 xi = x & BlockMask; const TerrainSquare *sq = mFile->findSquare( 0, xi, yi ); if ( x != xi || y != yi ) continue; // holes only in the primary terrain block if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) || sq->minHeight > heightMax || sq->maxHeight < heightMin ) continue; emitted = true; // Add the missing points U32 vi[5]; for (int i = 0; i < 4 ; i++) { S32 dx = i >> 1; S32 dy = dx ^ (i & 1); U32* vp = &vb[dy][x - xStart + dx]; if (*vp == U32_MAX) { Point3F pos; pos.x = (F32)((x + dx) * mSquareSize); pos.y = (F32)((y + dy) * mSquareSize); pos.z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) ); *vp = polyList->addPoint(pos); } vi[i] = *vp; } U32* vp = &vi[0]; if ( !( sq->flags & TerrainSquare::Split45 ) ) vi[4] = vi[0], vp++; BaseMatInstance *material = NULL; //getMaterialInst( xi, yi ); U32 surfaceKey = ((xi << 16) + yi) << 1; polyList->begin(material,surfaceKey); polyList->vertex(vp[0]); polyList->vertex(vp[1]); polyList->vertex(vp[2]); polyList->plane(vp[0],vp[1],vp[2]); polyList->end(); polyList->begin(material,surfaceKey + 1); polyList->vertex(vp[0]); polyList->vertex(vp[2]); polyList->vertex(vp[3]); polyList->plane(vp[0],vp[2],vp[3]); polyList->end(); } } return emitted; }
void TerrainFile::import( const GBitmap &heightMap, F32 heightScale, const Vector<U8> &layerMap, const Vector<String> &materials, bool flipYAxis ) { AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" ); AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" ); const U32 newSize = heightMap.getWidth(); if ( newSize != mSize ) { mHeightMap.setSize( newSize * newSize ); mHeightMap.compact(); mSize = newSize; } // Convert the height map to heights. U16 *oBits = mHeightMap.address(); if ( heightMap.getFormat() == GFXFormatR5G6B5 ) { const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale ); const U16 *iBits = (const U16*)heightMap.getBits(); if ( flipYAxis ) { for ( U32 i = 0; i < mSize * mSize; i++ ) { U16 height = convertBEndianToHost( *iBits ); *oBits = (U16)mCeil( (F32)height * toFixedPoint ); ++oBits; ++iBits; } } else { for(S32 y = mSize - 1; y >= 0; y--) { for(U32 x = 0; x < mSize; x++) { U16 height = convertBEndianToHost( *iBits ); mHeightMap[x + y * mSize] = (U16)mCeil( (F32)height * toFixedPoint ); ++iBits; } } } } else { const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale ); const U8 *iBits = heightMap.getBits(); if ( flipYAxis ) { for ( U32 i = 0; i < mSize * mSize; i++ ) { *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint ); ++oBits; iBits += heightMap.getBytesPerPixel(); } } else { for(S32 y = mSize - 1; y >= 0; y--) { for(U32 x = 0; x < mSize; x++) { mHeightMap[x + y * mSize] = (U16)mCeil( ((F32)*iBits) * toFixedPoint ); iBits += heightMap.getBytesPerPixel(); } } } } // Copy over the layer map. AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" ); mLayerMap = layerMap; mLayerMap.compact(); // Resolve the materials. _resolveMaterials( materials ); // Rebuild the collision grid map. _buildGridMap(); }
void TerrainFile::smooth( F32 factor, U32 steps, bool updateCollision ) { const U32 blockSize = mSize * mSize; // Grab some temp buffers for our smoothing results. Vector<F32> h1, h2; h1.setSize( blockSize ); h2.setSize( blockSize ); // Fill the first buffer with the current heights. for ( U32 i=0; i < blockSize; i++ ) h1[i] = (F32)mHeightMap[i]; // factor of 0.0 = NO Smoothing // factor of 1.0 = MAX Smoothing const F32 matrixM = 1.0f - getMax(0.0f, getMin(1.0f, factor)); const F32 matrixE = (1.0f-matrixM) * (1.0f/12.0f) * 2.0f; const F32 matrixC = matrixE * 0.5f; // Now loop for our interations. F32 *src = h1.address(); F32 *dst = h2.address(); for ( U32 s=0; s < steps; s++ ) { for ( S32 y=0; y < mSize; y++ ) { for ( S32 x=0; x < mSize; x++ ) { F32 samples[9]; S32 c = 0; for (S32 i = y-1; i < y+2; i++) for (S32 j = x-1; j < x+2; j++) { if ( i < 0 || j < 0 || i >= mSize || j >= mSize ) samples[c++] = src[ x + ( y * mSize ) ]; else samples[c++] = src[ j + ( i * mSize ) ]; } // 0 1 2 // 3 x,y 5 // 6 7 8 dst[ x + ( y * mSize ) ] = ((samples[0]+samples[2]+samples[6]+samples[8]) * matrixC) + ((samples[1]+samples[3]+samples[5]+samples[7]) * matrixE) + (samples[4] * matrixM); } } // Swap! F32 *tmp = dst; dst = src; src = tmp; } // Copy the results back to the height map. for ( U32 i=0; i < blockSize; i++ ) mHeightMap[i] = (U16)mCeil( (F32)src[i] ); if ( updateCollision ) _buildGridMap(); }
void GFXUtil::DistanceField::makeDistanceField( const U8 * sourceData, S32 sourceSizeX, S32 sourceSizeY, U8 * targetData, S32 targetSizeX, S32 targetSizeY, F32 radius ) { static Vector<DistanceFieldSearchSpaceStruct> searchSpace; S32 targetToSourceScalarX = sourceSizeX / targetSizeX; S32 targetToSourceScalarY = sourceSizeY / targetSizeY; S32 targetToSourcePixOffsetX = targetToSourceScalarX / 2; S32 targetToSourcePixOffsetY = targetToSourceScalarY / 2; F32 radius2 = radius * 2.f; { S32 intRange = mCeil(radius); for(S32 spaceY = -intRange; spaceY < intRange; spaceY++) { for(S32 spaceX = -intRange; spaceX < intRange; spaceX++) { if(spaceX == 0 && spaceY == 0) continue; F32 distance = Point2F(spaceX,spaceY).len(); if(distance <= radius) { searchSpace.increment(); searchSpace.last().distance = distance; searchSpace.last().xOffset = spaceX; searchSpace.last().yOffset = spaceY; } } } } dQsort(searchSpace.address(), searchSpace.size(), sizeof(DistanceFieldSearchSpaceStruct), cmpSortDistanceFieldSearchSpaceStruct); for(S32 y = 0; y < targetSizeY; y++) { for(S32 x = 0; x < targetSizeX; x++) { S32 sourceX = x * targetToSourceScalarX + targetToSourcePixOffsetX; S32 sourceY = y * targetToSourceScalarY + targetToSourcePixOffsetY; bool thisPixelEmpty = sourceData[sourceY * sourceSizeX + sourceX] < 127; F32 closestDist = F32_MAX; for(DistanceFieldSearchSpaceStruct * seachSpaceStructPtr = searchSpace.begin(); seachSpaceStructPtr <= searchSpace.end(); seachSpaceStructPtr++) { DistanceFieldSearchSpaceStruct & searchSpaceStruct = *seachSpaceStructPtr; S32 cx = sourceX + searchSpaceStruct.xOffset; if(cx < 0 || cx >= sourceSizeX) continue; S32 cy = sourceY + searchSpaceStruct.yOffset; if(cy < 0 || cy >= sourceSizeY) continue; if((sourceData[cy * sourceSizeX + cx] < 127) != thisPixelEmpty) { closestDist = searchSpaceStruct.distance; break; } } F32 diff = thisPixelEmpty ? getMax(-0.5f,-(closestDist / radius2)) : getMin(0.5f,closestDist / radius2); F32 targetValue = 0.5f + diff; *targetData = targetValue * 255; targetData++; } } searchSpace.clear(); }
void SFXEmitter::_render3DVisualFeedback() { GFXTransformSaver saver; GFX->multWorld( getRenderTransform() ); GFXStateBlockDesc desc; desc.setZReadWrite( true, false ); desc.setBlend( true ); desc.setCullMode( GFXCullNone ); if( mRenderSB == NULL ) mRenderSB = GFX->createStateBlock( desc ); GFX->setStateBlock( mRenderSB ); // Render the max range sphere. if( smRenderColorRangeSphere.alpha > 0 ) GFX->getDrawUtil()->drawSphere( desc, mDescription.mMaxDistance, Point3F( 0.f, 0.f, 0.f ), smRenderColorRangeSphere ); //TODO: some point size support in GFX would be nice // Prepare primitive list. Make sure we stay within limits. F32 radialIncrements = smRenderRadialIncrements; F32 sweepIncrements = smRenderSweepIncrements; F32 pointDistance = smRenderPointDistance; F32 numPoints; while( 1 ) { numPoints = mCeil( 360.f / radialIncrements ) * mCeil( 360.f / sweepIncrements ) * ( mDescription.mMaxDistance / pointDistance ); if( numPoints < 65536 ) break; radialIncrements *= 1.1f; sweepIncrements *= 1.1f; pointDistance *= 1.5; } PrimBuild::begin( GFXPointList, numPoints ); // Render inner cone. _renderCone( radialIncrements, sweepIncrements, pointDistance, mDescription.mConeInsideAngle, 0.f, mDescription.mVolume, mDescription.mVolume, smRenderColorInnerCone ); // Outer Cone and Outside volume only get rendered if mConeOutsideVolume > 0 if( mDescription.mConeOutsideVolume > 0.f ) { const F32 outsideVolume = mDescription.mVolume * mDescription.mConeOutsideVolume; // Render outer cone. _renderCone( radialIncrements, sweepIncrements, pointDistance, mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, outsideVolume, mDescription.mVolume, smRenderColorOuterCone ); // Render outside volume. _renderCone( radialIncrements, sweepIncrements, pointDistance, 360.f, mDescription.mConeOutsideAngle, outsideVolume, outsideVolume, smRenderColorOutsideVolume ); } // Commit primitive list. PrimBuild::end(); }
bool NavMesh::generateMesh() { // Parse objects from level into RC-compatible format NavModelData data = NavMeshLoader::mergeModels( NavMeshLoader::parseTerrainData(getWorldBox(), 0), NavMeshLoader::parseStaticObjects(getWorldBox()), true); // Check for no geometry if(!data.getVertCount()) return false; // Free intermediate and final results freeIntermediates(true); // Create mInPolys if we don't have one already if(!mInPolys && mSaveIntermediates) mInPolys = new ConcretePolyList(); // Reconstruct input geometry from out data if(mSaveIntermediates) RCtoPolyList(&data, mInPolys); // Recast initialisation data rcContext ctx(false); rcConfig cfg; dMemset(&cfg, 0, sizeof(cfg)); cfg.cs = mCellSize; cfg.ch = mCellHeight; rcCalcBounds(data.verts, data.getVertCount(), cfg.bmin, cfg.bmax); rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height); cfg.walkableHeight = mCeil(mWalkableHeight / mCellHeight); cfg.walkableClimb = mCeil(mWalkableClimb / mCellHeight); cfg.walkableRadius = mCeil(mWalkableRadius / mCellSize); cfg.walkableSlopeAngle = mWalkableSlope; cfg.borderSize = mBorderSize; cfg.detailSampleDist = mDetailSampleDist; cfg.detailSampleMaxError = mDetailSampleMaxError; cfg.maxEdgeLen = mMaxEdgeLen; cfg.maxSimplificationError = mMaxSimplificationError; cfg.maxVertsPerPoly = 3; cfg.minRegionArea = mMinRegionArea; cfg.mergeRegionArea = mMergeRegionArea; cfg.tileSize = mTileSize; if(!createPolyMesh(cfg, data, &ctx)) return false; //Detour initialisation data dtNavMeshCreateParams params; dMemset(¶ms, 0, sizeof(params)); params.walkableHeight = cfg.walkableHeight; params.walkableRadius = cfg.walkableRadius; params.walkableClimb = cfg.walkableClimb; params.tileX = 0; params.tileY = 0; params.tileLayer = 0; rcVcopy(params.bmax, cfg.bmax); rcVcopy(params.bmin, cfg.bmin); params.buildBvTree = true; params.ch = cfg.ch; params.cs = cfg.cs; params.verts = pm->verts; params.vertCount = pm->nverts; params.polys = pm->polys; params.polyAreas = pm->areas; params.polyFlags = pm->flags; params.polyCount = pm->npolys; params.nvp = pm->nvp; params.detailMeshes = pmd->meshes; params.detailVerts = pmd->verts; params.detailVertsCount = pmd->nverts; params.detailTris = pmd->tris; params.detailTriCount = pmd->ntris; if(!createNavMesh(params)) return false; return true; }