void TerrainConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf) { U32 i; cf->material = 0; cf->object = mObject; // Plane is normal n + support point PlaneF plane; plane.set(support(n),n); S32 vertexCount = cf->mVertexList.size(); // Emit vertices on the plane S32* vertexListPointer; if (halfA) vertexListPointer = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4]; else vertexListPointer = square ? sVertexList[(split45 << 1)] : sVertexList[4]; S32 pm = 0; S32 numVerts = *vertexListPointer; vertexListPointer += 1; for (i = 0; i < numVerts; i++) { Point3F& cp = point[vertexListPointer[i]]; cf->mVertexList.increment(); mat.mulP(cp,&cf->mVertexList.last()); pm |= 1 << vertexListPointer[i]; } // Emit Edges S32* ep = (square && halfA)? (split45 ? sEdgeList45A[pm]: sEdgeList135A[pm]): (split45 ? sEdgeList45[pm]: sEdgeList135[pm]); S32 numEdges = *ep; S32 edgeListStart = cf->mEdgeList.size(); cf->mEdgeList.increment(numEdges); ep += 1; for (i = 0; i < numEdges; i++) { cf->mEdgeList[edgeListStart + i].vertex[0] = vertexCount + ep[i * 2 + 0]; cf->mEdgeList[edgeListStart + i].vertex[1] = vertexCount + ep[i * 2 + 1]; } // Emit faces S32* fp = split45 ? sFaceList45[pm]: sFaceList135[pm]; S32 numFaces = *fp; fp += 1; S32 faceListStart = cf->mFaceList.size(); cf->mFaceList.increment(numFaces); for (i = 0; i < numFaces; i++) { cf->mFaceList[faceListStart + i].normal = normal[fp[i * 4 + 0]]; cf->mFaceList[faceListStart + i].vertex[0] = vertexCount + fp[i * 4 + 1]; cf->mFaceList[faceListStart + i].vertex[1] = vertexCount + fp[i * 4 + 2]; cf->mFaceList[faceListStart + i].vertex[2] = vertexCount + fp[i * 4 + 3]; } }
inline SceneZoneCullingState::CullingTestResult SceneCullingState::_test( const T& bounds, Iter zoneIter, const PlaneF& nearPlane, const PlaneF& farPlane ) const { // Defer test of near and far plane until we've hit a zone // which actually has visible space. This prevents us from // doing near/far tests on objects that were included in the // potential render list but aren't actually in any visible // zone. bool haveTestedNearAndFar = false; // Test the culling states of all zones that the object // is assigned to. for( ; zoneIter.isValid(); ++ zoneIter ) { const SceneZoneCullingState& zoneState = getZoneState( *zoneIter ); // Skip zone if there are no positive culling volumes. if( !zoneState.hasIncluders() ) continue; // If we haven't tested the near and far plane yet, do so // now. if( !haveTestedNearAndFar ) { // Test near plane. PlaneF::Side nearSide = nearPlane.whichSide( bounds ); if( nearSide == PlaneF::Back ) return SceneZoneCullingState::CullingTestNegative; // Test far plane. PlaneF::Side farSide = farPlane.whichSide( bounds ); if( farSide == PlaneF::Back ) return SceneZoneCullingState::CullingTestNegative; haveTestedNearAndFar = true; } // If the object's world bounds overlaps any of the volumes // for this zone, it's rendered. SceneZoneCullingState::CullingTestResult result = zoneState.testVolumes( bounds ); if( result == SceneZoneCullingState::CullingTestPositiveByInclusion ) return result; else if( result == SceneZoneCullingState::CullingTestPositiveByOcclusion ) return result; } return SceneZoneCullingState::CullingTestNegative; }
ShadowVolumeBSP::SVNode::Side ShadowVolumeBSP::whichSide(SVPoly * poly, const PlaneF & plane) const { bool front = false; bool back = false; for(U32 i = 0; i < poly->mWindingCount; i++) { switch(plane.whichSide(poly->mWinding[i])) { case PlaneF::Front: if(back) return(SVNode::Split); front = true; break; case PlaneF::Back: if(front) return(SVNode::Split); back = true; break; default: break; } } AssertFatal(!(front && back), "ShadowVolumeBSP::whichSide - failed to classify poly"); if(!front && !back) return(SVNode::On); return(front ? SVNode::Front : SVNode::Back); }
void BoxConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf) { cf->material = 0; cf->object = mObject; S32 v = 0; v += (n.x >= 0)? 1: 0; v += (n.y >= 0)? 2: 0; v += (n.z >= 0)? 4: 0; PlaneF plane; plane.set(getVertex(v),n); // Emit vertex and edge S32 mask = 0; Corner& corner = sCorner[v]; mask |= isOnPlane(getVertex(corner.a),plane)? 1: 0; mask |= isOnPlane(getVertex(corner.b),plane)? 2: 0; mask |= isOnPlane(getVertex(corner.c),plane)? 4: 0; switch(mask) { case 0: { cf->mVertexList.increment(); mat.mulP(getVertex(v),&cf->mVertexList.last()); break; } case 1: emitEdge(v,corner.a,mat,cf); break; case 2: emitEdge(v,corner.b,mat,cf); break; case 4: emitEdge(v,corner.c,mat,cf); break; case 1 | 2: emitFace(corner.ab,mat,cf); break; case 2 | 4: emitFace(corner.bc,mat,cf); break; case 1 | 4: emitFace(corner.ac,mat,cf); break; } }
bool TerrainBlock::castRayBlock(const Point3F &pStart, const Point3F &pEnd, const Point2I &aBlockPos, U32 aLevel, F32 invDeltaX, F32 invDeltaY, F32 aStartT, F32 aEndT, RayInfo *info, bool collideEmpty) { F32 invBlockSize = 1 / F32(BlockSquareWidth); static TerrLOSStackNode stack[BlockShift * 3 + 1]; U32 stackSize = 1; stack[0].startT = aStartT; stack[0].endT = aEndT; stack[0].blockPos = aBlockPos; stack[0].level = aLevel; if(!mTile && !aBlockPos.isZero()) return false; while(stackSize--) { TerrLOSStackNode *sn = stack + stackSize; U32 level = sn->level; F32 startT = sn->startT; F32 endT = sn->endT; Point2I blockPos = sn->blockPos; GridSquare *sq = findSquare(level, Point2I(blockPos.x & BlockMask, blockPos.y & BlockMask)); F32 startZ = startT * (pEnd.z - pStart.z) + pStart.z; F32 endZ = endT * (pEnd.z - pStart.z) + pStart.z; F32 minHeight = fixedToFloat(sq->minHeight); if(startZ <= minHeight && endZ <= minHeight) { //drawLineTest(startT, sn->endT, false); continue; } F32 maxHeight = fixedToFloat(sq->maxHeight); if(startZ >= maxHeight && endZ >= maxHeight) { //drawLineTest(startT, endT, false); continue; } if (!collideEmpty && (sq->flags & GridSquare::Empty) && blockPos.x == (blockPos.x & BlockMask) && blockPos.y == (blockPos.y & BlockMask)) { //drawLineTest(startT, endT, false); continue; } if(level == 0) { F32 xs = blockPos.x * invBlockSize; F32 ys = blockPos.y * invBlockSize; F32 zBottomLeft = fixedToFloat(getHeight(blockPos.x, blockPos.y)); F32 zBottomRight= fixedToFloat(getHeight(blockPos.x + 1, blockPos.y)); F32 zTopLeft = fixedToFloat(getHeight(blockPos.x, blockPos.y + 1)); F32 zTopRight = fixedToFloat(getHeight(blockPos.x + 1, blockPos.y + 1)); PlaneF p1, p2; PlaneF divider; Point3F planePoint; if(sq->flags & GridSquare::Split45) { p1.set(zBottomLeft - zBottomRight, zBottomRight - zTopRight, invBlockSize); p2.set(zTopLeft - zTopRight, zBottomLeft - zTopLeft, invBlockSize); planePoint.set(xs, ys, zBottomLeft); divider.x = 1; divider.y = -1; divider.z = 0; } else { p1.set(zTopLeft - zTopRight, zBottomRight - zTopRight, invBlockSize); p2.set(zBottomLeft - zBottomRight, zBottomLeft - zTopLeft, invBlockSize); planePoint.set(xs + invBlockSize, ys, zBottomRight); divider.x = 1; divider.y = 1; divider.z = 0; } p1.setPoint(planePoint); p2.setPoint(planePoint); divider.setPoint(planePoint); F32 t1 = p1.intersect(pStart, pEnd); F32 t2 = p2.intersect(pStart, pEnd); F32 td = divider.intersect(pStart, pEnd); F32 dStart = divider.distToPlane(pStart); F32 dEnd = divider.distToPlane(pEnd); // see if the line crosses the divider if((dStart >= 0 && dEnd < 0) || (dStart < 0 && dEnd >= 0)) { if(dStart < 0) { F32 temp = t1; t1 = t2; t2 = temp; } if(t1 >= startT && t1 && t1 <= td && t1 <= endT) { info->t = t1; info->normal = p1; return true; } if(t2 >= td && t2 >= startT && t2 <= endT) { info->t = t2; info->normal = p2; return true; } } else { F32 t; if(dStart >= 0) { t = t1; info->normal = p1; } else { t = t2; info->normal = p2; } if(t >= startT && t <= endT) { info->t = t; return true; } } continue; } int subSqWidth = 1 << (level - 1); F32 xIntercept = (blockPos.x + subSqWidth) * invBlockSize; F32 xInt = calcInterceptX(pStart.x, invDeltaX, xIntercept); F32 yIntercept = (blockPos.y + subSqWidth) * invBlockSize; F32 yInt = calcInterceptY(pStart.y, invDeltaY, yIntercept); F32 startX = startT * (pEnd.x - pStart.x) + pStart.x; F32 startY = startT * (pEnd.y - pStart.y) + pStart.y; if(xInt < startT) xInt = MAX_FLOAT; if(yInt < startT) yInt = MAX_FLOAT; U32 x0 = (startX > xIntercept) * subSqWidth; U32 y0 = (startY > yIntercept) * subSqWidth; U32 x1 = subSqWidth - x0; U32 y1 = subSqWidth - y0; U32 nextLevel = level - 1; // push the items on the stack in reverse order of processing if(xInt > endT && yInt > endT) { // only test the square the point started in: stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y0); stack[stackSize].level = nextLevel; stackSize++; } else if(xInt < yInt) { F32 nextIntersect = endT; if(yInt <= endT) { stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = yInt; stackSize++; } stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y0); stack[stackSize].startT = xInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else if(yInt < xInt) { F32 nextIntersect = endT; if(xInt <= endT) { stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = xInt; stackSize++; } stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = yInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else { stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } } return false; }
void ShadowVolumeBSP::splitPoly(SVPoly * poly, const PlaneF & plane, SVPoly ** front, SVPoly ** back) { PlaneF::Side sides[SVPoly::MaxWinding]; U32 i; for(i = 0; i < poly->mWindingCount; i++) sides[i] = plane.whichSide(poly->mWinding[i]); // create the polys (*front) = createPoly(); (*back) = createPoly(); // copy the info (*front)->mWindingCount = (*back)->mWindingCount = 0; (*front)->mPlane = (*back)->mPlane = poly->mPlane; (*front)->mTarget = (*back)->mTarget = poly->mTarget; (*front)->mSurfaceInfo = (*back)->mSurfaceInfo = poly->mSurfaceInfo; (*front)->mShadowVolume = (*back)->mShadowVolume = poly->mShadowVolume; // for(i = 0; i < poly->mWindingCount; i++) { U32 j = (i+1) % poly->mWindingCount; if(sides[i] == PlaneF::On) { (*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i]; (*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i]; } else if(sides[i] == PlaneF::Front) { (*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i]; if(sides[j] == PlaneF::Back) { const Point3F & a = poly->mWinding[i]; const Point3F & b = poly->mWinding[j]; F32 t = plane.intersect(a, b); AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection"); Point3F pos; pos.interpolate(a, b, t); // (*front)->mWinding[(*front)->mWindingCount++] = (*back)->mWinding[(*back)->mWindingCount++] = pos; } } else if(sides[i] == PlaneF::Back) { (*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i]; if(sides[j] == PlaneF::Front) { const Point3F & a = poly->mWinding[i]; const Point3F & b = poly->mWinding[j]; F32 t = plane.intersect(a, b); AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection"); Point3F pos; pos.interpolate(a, b, t); (*front)->mWinding[(*front)->mWindingCount++] = (*back)->mWinding[(*back)->mWindingCount++] = pos; } } } AssertFatal((*front)->mWindingCount && (*back)->mWindingCount, "ShadowVolume::split - invalid split"); }
void blInteriorProxy::addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level) { if(light->getType() != LightInfo::Vector) return; ColorF ambient = light->getAmbient(); bool shadowedTree = true; InteriorInstance* interior = dynamic_cast<InteriorInstance*>(getObject()); if (!interior) return; Resource<InteriorResource> mInteriorRes = interior->getResource(); // check if just getting shadow detail if(level == SceneLighting::SHADOW_DETAIL) { shadowedTree = false; level = mInteriorRes->getNumDetailLevels() - 1; } Interior * detail = mInteriorRes->getDetailLevel(level); bool hasAlarm = detail->hasAlarmState(); // make sure surfaces do not get processed more than once BitVector surfaceProcessed; surfaceProcessed.setSize(detail->mSurfaces.size()); surfaceProcessed.clear(); ColorI color = light->getAmbient(); // go through the zones of the interior and grab outside visible surfaces for(U32 i = 0; i < detail->getNumZones(); i++) { Interior::Zone & zone = detail->mZones[i]; for(U32 j = 0; j < zone.surfaceCount; j++) { U32 surfaceIndex = detail->mZoneSurfaces[zone.surfaceStart + j]; // dont reprocess a surface if(surfaceProcessed.test(surfaceIndex)) continue; surfaceProcessed.set(surfaceIndex); Interior::Surface & surface = detail->mSurfaces[surfaceIndex]; // outside visible? if(!(surface.surfaceFlags & Interior::SurfaceOutsideVisible)) continue; // good surface? PlaneF plane = detail->getPlane(surface.planeIndex); if(Interior::planeIsFlipped(surface.planeIndex)) plane.neg(); // project the plane PlaneF projPlane; mTransformPlane(interior->getTransform(), interior->getScale(), plane, &projPlane); // fill with ambient? (need to do here, because surface will not be // added to the SVBSP tree) F32 dot = mDot(projPlane, light->getDirection()); if(dot > -gParellelVectorThresh)// && !(GFX->getPixelShaderVersion() > 0.0) ) { if(shadowedTree) { // alarm lighting GFXTexHandle normHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getNormalLMapIndex(surfaceIndex)); GFXTexHandle alarmHandle; GBitmap * normLightmap = normHandle->getBitmap(); GBitmap * alarmLightmap = 0; // check if they share the lightmap if(hasAlarm) { if(detail->getNormalLMapIndex(surfaceIndex) != detail->getAlarmLMapIndex(surfaceIndex)) { alarmHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getAlarmLMapIndex(surfaceIndex)); alarmLightmap = alarmHandle->getBitmap(); } } // // Support for interior light map border sizes. // U32 xlen, ylen, xoff, yoff; U32 lmborder = detail->getLightMapBorderSize(); xlen = surface.mapSizeX + (lmborder * 2); ylen = surface.mapSizeY + (lmborder * 2); xoff = surface.mapOffsetX - lmborder; yoff = surface.mapOffsetY - lmborder; // attemp to light normal and alarm lighting for(U32 c = 0; c < 2; c++) { GBitmap * lightmap = (c == 0) ? normLightmap : alarmLightmap; if(!lightmap) continue; // fill it for(U32 y = 0; y < ylen; y++) { for(U32 x = 0; x < xlen; x++) { ColorI outColor(255, 0, 0, 255); #ifndef SET_COLORS ColorI lmColor(0, 0, 0, 255); lightmap->getColor(xoff + x, yoff + y, lmColor); U32 _r = static_cast<U32>( color.red ) + static_cast<U32>( lmColor.red ); U32 _g = static_cast<U32>( color.green ) + static_cast<U32>( lmColor.green ); U32 _b = static_cast<U32>( color.blue ) + static_cast<U32>( lmColor.blue ); outColor.red = mClamp(_r, 0, 255); outColor.green = mClamp(_g, 0, 255); outColor.blue = mClamp(_b, 0, 255); #endif lightmap->setColor(xoff + x, yoff + y, outColor); } } } } continue; } ShadowVolumeBSP::SVPoly * poly = buildInteriorPoly(shadowVolume, detail, surfaceIndex, light, shadowedTree); // insert it into the SVBSP tree shadowVolume->insertPoly(poly); } } }
ShadowVolumeBSP::SVPoly * blInteriorProxy::buildInteriorPoly(ShadowVolumeBSP * shadowVolumeBSP, Interior * detail, U32 surfaceIndex, LightInfo * light, bool createSurfaceInfo) { InteriorInstance* interior = dynamic_cast<InteriorInstance*>(getObject()); if (!interior) return NULL; // transform and add the points... const MatrixF & transform = interior->getTransform(); const VectorF & scale = interior->getScale(); const Interior::Surface & surface = detail->mSurfaces[surfaceIndex]; ShadowVolumeBSP::SVPoly * poly = shadowVolumeBSP->createPoly(); poly->mWindingCount = surface.windingCount; // project these points for(U32 j = 0; j < poly->mWindingCount; j++) { Point3F iPnt = detail->mPoints[detail->mWindings[surface.windingStart + j]].point; Point3F tPnt; iPnt.convolve(scale); transform.mulP(iPnt, &tPnt); poly->mWinding[j] = tPnt; } // convert from fan U32 tmpIndices[ShadowVolumeBSP::SVPoly::MaxWinding]; Point3F fanIndices[ShadowVolumeBSP::SVPoly::MaxWinding]; tmpIndices[0] = 0; U32 idx = 1; U32 i; for(i = 1; i < poly->mWindingCount; i += 2) tmpIndices[idx++] = i; for(i = ((poly->mWindingCount - 1) & (~0x1)); i > 0; i -= 2) tmpIndices[idx++] = i; idx = 0; for(i = 0; i < poly->mWindingCount; i++) if(surface.fanMask & (1 << i)) fanIndices[idx++] = poly->mWinding[tmpIndices[i]]; // set the data poly->mWindingCount = idx; for(i = 0; i < poly->mWindingCount; i++) poly->mWinding[i] = fanIndices[i]; // flip the plane - shadow volumes face inwards PlaneF plane = detail->getPlane(surface.planeIndex); if(!Interior::planeIsFlipped(surface.planeIndex)) plane.neg(); // transform the plane mTransformPlane(transform, scale, plane, &poly->mPlane); shadowVolumeBSP->buildPolyVolume(poly, light); // do surface info? if(createSurfaceInfo) { ShadowVolumeBSP::SurfaceInfo * surfaceInfo = new ShadowVolumeBSP::SurfaceInfo; shadowVolumeBSP->mSurfaces.push_back(surfaceInfo); // fill it surfaceInfo->mSurfaceIndex = surfaceIndex; surfaceInfo->mShadowVolume = shadowVolumeBSP->getShadowVolume(poly->mShadowVolume); // POLY and POLY node gets it too ShadowVolumeBSP::SVNode * traverse = shadowVolumeBSP->getShadowVolume(poly->mShadowVolume); while(traverse->mFront) { traverse->mSurfaceInfo = surfaceInfo; traverse = traverse->mFront; } // get some info from the poly node poly->mSurfaceInfo = traverse->mSurfaceInfo = surfaceInfo; surfaceInfo->mPlaneIndex = traverse->mPlaneIndex; } return(poly); }
void WaterPlane::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) { outPos = getPosition(); outPlane.set( outPos, Point3F(0,0,1) ); }