void AdvancedLightBinManager::LightMaterialInfo::setViewParameters( const F32 _zNear, const F32 _zFar, const Point3F &_eyePos, const PlaneF &_farPlane, const PlaneF &_vsFarPlane) { MaterialParameters *matParams = matInstance->getMaterialParameters(); matParams->setSafe( farPlane, *((const Point4F *)&_farPlane) ); matParams->setSafe( vsFarPlane, *((const Point4F *)&_vsFarPlane) ); if ( negFarPlaneDotEye->isValid() ) { // -dot( farPlane, eyePos ) const F32 negFarPlaneDotEyeVal = -( mDot( *((const Point3F *)&_farPlane), _eyePos ) + _farPlane.d ); matParams->set( negFarPlaneDotEye, negFarPlaneDotEyeVal ); } matParams->setSafe( zNearFarInvNearFar, Point4F( _zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar ) ); }
F32 CubeReflector::calcFaceScore( const ReflectParams ¶ms, U32 faceidx ) { if ( Parent::calcScore( params ) <= 0.0f ) return score; VectorF vLookatPt(0.0f, 0.0f, 0.0f); switch( faceidx ) { case 0 : // D3DCUBEMAP_FACE_POSITIVE_X: vLookatPt = VectorF( 1.0f, 0.0f, 0.0f ); break; case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X: vLookatPt = VectorF( -1.0f, 0.0f, 0.0f ); break; case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y: vLookatPt = VectorF( 0.0f, 1.0f, 0.0f ); break; case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y: vLookatPt = VectorF( 0.0f, -1.0f, 0.0f ); break; case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z: vLookatPt = VectorF( 0.0f, 0.0f, 1.0f ); break; case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z: vLookatPt = VectorF( 0.0f, 0.0f, -1.0f ); break; } VectorF cameraDir; params.query->cameraMatrix.getColumn( 1, &cameraDir ); F32 dot = mDot( cameraDir, -vLookatPt ); dot = getMax( ( dot + 1.0f ) / 2.0f, 0.1f ); score *= dot; return score; }
F32 PlaneReflector::calcScore( const ReflectParams ¶ms ) { if ( Parent::calcScore( params ) <= 0.0f || score >= 1000.0f ) return score; // The planar reflection is view dependent to score it // higher if the view direction and/or position has changed. // Get the current camera info. VectorF camDir = params.query->cameraMatrix.getForwardVector(); Point3F camPos = params.query->cameraMatrix.getPosition(); // Scale up the score based on the view direction change. F32 dot = mDot( camDir, mLastDir ); dot = ( 1.0f - dot ) * 1000.0f; score += dot * mDesc->priority; // Also account for the camera movement. score += ( camPos - mLastPos ).lenSquared() * mDesc->priority; return score; }
F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) { TORQUE_UNUSED(updateMask); // Calculate a priority used to decide if this object // will be updated on the client. All the weights // are calculated 0 -> 1 Then weighted together at the // end to produce a priority. Point3F pos; getWorldBox().getCenter(&pos); pos -= camInfo->pos; F32 dist = pos.len(); if (dist == 0.0f) dist = 0.001f; pos *= 1.0f / dist; // Weight based on linear distance, the basic stuff. F32 wDistance = (dist < camInfo->visibleDistance)? 1.0f - (dist / camInfo->visibleDistance): 0.0f; // Weight by field of view, objects directly in front // will be weighted 1, objects behind will be 0 F32 dot = mDot(pos,camInfo->orientation); //Winterleaf Modification //bool inFov = dot > camInfo->cosFov; bool inFov = dot > (cos( (camInfo->fov + 40) >360?360:camInfo->fov + 40)/2); //Winterleaf Modification F32 wFov = inFov? 1.0f: 0; // Weight by linear velocity parallel to the viewing plane // (if it's the field of view, 0 if it's not). F32 wVelocity = 0.0f; if (inFov) { Point3F vec; mCross(camInfo->orientation,getVelocity(),&vec); wVelocity = (vec.len() * camInfo->fov) / (camInfo->fov * camInfo->visibleDistance); if (wVelocity > 1.0f) wVelocity = 1.0f; } // Weight by interest. F32 wInterest; if (getTypeMask() & PlayerObjectType) wInterest = 0.75f; else if (getTypeMask() & ProjectileObjectType) { // Projectiles are more interesting if they // are heading for us. wInterest = 0.30f; F32 dot = -mDot(pos,getVelocity()); if (dot > 0.0f) wInterest += 0.20 * dot; } else { if (getTypeMask() & ItemObjectType) wInterest = 0.25f; else // Everything else is less interesting. wInterest = 0.0f; } // Weight by updateSkips F32 wSkips = updateSkips * 0.5; // Calculate final priority, should total to about 1.0f // return wFov * sUpFov + wDistance * sUpDistance + wVelocity * sUpVelocity + wSkips * sUpSkips + wInterest * sUpInterest; }
void EarlyOutPolyList::end() { if (mEarlyOut == true) return; Poly& poly = mPolyList.last(); // Anything facing away from the mNormal is rejected if (mDot(poly.plane,mNormal) > 0) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } // Build intial inside/outside plane masks U32 indexStart = poly.vertexStart; U32 vertexCount = mIndexList.size() - indexStart; U32 frontMask = 0,backMask = 0; U32 i; for (i = indexStart; i < mIndexList.size(); i++) { U32 mask = mVertexList[mIndexList[i]].mask; frontMask |= mask; backMask |= ~mask; } // Trivial accept if all the vertices are on the backsides of // all the planes. if (!frontMask) { poly.vertexCount = vertexCount; mEarlyOut = true; return; } // Trivial reject if any plane not crossed has all it's points // on the front. U32 crossMask = frontMask & backMask; if (~crossMask & frontMask) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } // Need to do some clipping for (U32 p = 0; p < mPlaneList.size(); p++) { U32 pmask = 1 << p; // Only test against this plane if we have something // on both sides if (crossMask & pmask) { U32 indexEnd = mIndexList.size(); U32 i1 = indexEnd - 1; U32 mask1 = mVertexList[mIndexList[i1]].mask; for (U32 i2 = indexStart; i2 < indexEnd; i2++) { U32 mask2 = mVertexList[mIndexList[i2]].mask; if ((mask1 ^ mask2) & pmask) { // mVertexList.increment(); VectorF& v1 = mVertexList[mIndexList[i1]].point; VectorF& v2 = mVertexList[mIndexList[i2]].point; VectorF vv = v2 - v1; F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv); mIndexList.push_back(mVertexList.size() - 1); Vertex& iv = mVertexList.last(); iv.point.x = v1.x + vv.x * t; iv.point.y = v1.y + vv.y * t; iv.point.z = v1.z + vv.z * t; iv.mask = 0; // Test against the remaining planes for (U32 i = p + 1; i < mPlaneList.size(); i++) if (mPlaneList[i].distToPlane(iv.point) > 0) { iv.mask = 1 << i; break; } } if (!(mask2 & pmask)) { U32 index = mIndexList[i2]; mIndexList.push_back(index); } mask1 = mask2; i1 = i2; } // Check for degenerate indexStart = indexEnd; if (mIndexList.size() - indexStart < 3) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } } } // If we reach here, then there's a poly! mEarlyOut = true; // Emit what's left and compress the index list. poly.vertexCount = mIndexList.size() - indexStart; memcpy(&mIndexList[poly.vertexStart], &mIndexList[indexStart],poly.vertexCount); mIndexList.setSize(poly.vertexStart + poly.vertexCount); }
//-------------------------------------------------------- //-------------------------------------------------------- // JK: faster ray->convexHull test - taken from TSMesh... // // Used by lighting system... // bool castRayBrush(const Point3F &start, const Point3F &end, PlaneF *planes, U32 planeCount) { // F32 startTime = -0.01f; F32 startNum = -0.01f; F32 startDen = 1.00f; // F32 endTime = 1.01f; F32 endNum = 1.01f; F32 endDen = 1.00f; S32 curPlane = 0; U32 curMaterial = 0; bool found = false; bool tmpFound; S32 tmpPlane; F32 sgn = -1.0f; F32 * pnum = &startNum; F32 * pden = &startDen; S32 * pplane = &curPlane; bool * pfound = &found; for (S32 i=0; i<planeCount; i++) { // if start & end outside, no collision // if start & end inside, continue // if start outside, end inside, or visa versa, find intersection of line with plane // then update intersection of line with hull (using startTime and endTime) F32 dot1 = mDot(planes[i],start) + planes[i].d; F32 dot2 = mDot(planes[i],end) + planes[i].d; if (dot1*dot2>0.0f) { // same side of the plane...which side -- dot==0 considered inside if (dot1>0.0f) // start and end outside of this plane, no collision return false; // start and end inside plane, continue continue; } AssertFatal(dot1/(dot1-dot2)>=0.0f && dot1/(dot1-dot2)<=1.0f,"TSMesh::castRay (1)"); // find intersection (time) with this plane... // F32 time = dot1 / (dot1-dot2); F32 num = mFabs(dot1); F32 den = mFabs(dot1-dot2); if (sgn*dot1>=0) { sgn *= -1.0f; pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum); pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen); pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane); pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found); } bool noCollision = num*endDen*sgn<endNum*den*sgn && num*startDen*sgn<startNum*den*sgn; if (num * *pden * sgn < *pnum * den * sgn && !noCollision) { *pnum = num; *pden = den; *pplane = i; *pfound = true; } else if (noCollision) return false; } return found; }
bool SphereF::intersectsRay( const Point3F &start, const Point3F &end ) const { MatrixF worldToObj( true ); worldToObj.setPosition( center ); worldToObj.inverse(); VectorF dir = end - start; dir.normalize(); Point3F tmpStart = start; worldToObj.mulP( tmpStart ); //Compute A, B and C coefficients F32 a = mDot(dir, dir); F32 b = 2 * mDot(dir, tmpStart); F32 c = mDot(tmpStart, tmpStart) - (radius * radius); //Find discriminant F32 disc = b * b - 4 * a * c; // if discriminant is negative there are no real roots, so return // false as ray misses sphere if ( disc < 0 ) return false; // compute q as described above F32 distSqrt = mSqrt( disc ); F32 q; if ( b < 0 ) q = (-b - distSqrt)/2.0; else q = (-b + distSqrt)/2.0; // compute t0 and t1 F32 t0 = q / a; F32 t1 = c / q; // make sure t0 is smaller than t1 if ( t0 > t1 ) { // if t0 is bigger than t1 swap them around F32 temp = t0; t0 = t1; t1 = temp; } // This function doesn't use it // but t would be the interpolant // value for getting the exact // intersection point, by interpolating // start to end by t. /* F32 t = 0; TORQUE_UNUSED(t); */ // if t1 is less than zero, the object is in the ray's negative direction // and consequently the ray misses the sphere if ( t1 < 0 ) return false; // if t0 is less than zero, the intersection point is at t1 if ( t0 < 0 ) // t = t1; return true; else // else the intersection point is at t0 return true; // t = t0; }
void BtPlayer::findContact( SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects ) const { AssertFatal( mGhostObject, "BtPlayer::findContact - The controller is null!" ); VectorF normal; F32 maxDot = -1.0f; // Go thru the contact points... get the first contact. btHashedOverlappingPairCache *pairCache = mGhostObject->getOverlappingPairCache(); btBroadphasePairArray& pairArray = pairCache->getOverlappingPairArray(); U32 numPairs = pairArray.size(); btManifoldArray manifoldArray; for ( U32 i=0; i < numPairs; i++ ) { const btBroadphasePair &pair = pairArray[i]; btBroadphasePair *collisionPair = pairCache->findPair( pair.m_pProxy0, pair.m_pProxy1 ); if ( !collisionPair || !collisionPair->m_algorithm ) continue; btCollisionObject *other = (btCollisionObject*)pair.m_pProxy0->m_clientObject; if ( other == mGhostObject ) other = (btCollisionObject*)pair.m_pProxy1->m_clientObject; //.logicking >> if (outOverlapObjects->contains( PhysicsUserData::getObject( other->getUserPointer() ) )) continue; //AssertFatal( !outOverlapObjects->contains( PhysicsUserData::getObject( other->getUserPointer() ) ), // "Got multiple pairs of the same object!" ); //.logicking << outOverlapObjects->push_back( PhysicsUserData::getObject( other->getUserPointer() ) ); if ( other->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE ) continue; manifoldArray.clear(); collisionPair->m_algorithm->getAllContactManifolds( manifoldArray ); for ( U32 j=0; j < manifoldArray.size(); j++ ) { btPersistentManifold *manifold = manifoldArray[j]; btScalar directionSign = manifold->getBody0() == mGhostObject ? 1.0f : -1.0f; for ( U32 p=0; p < manifold->getNumContacts(); p++ ) { const btManifoldPoint &pt = manifold->getContactPoint(p); // Test the normal... is it the most vertical one we got? normal = btCast<Point3F>( pt.m_normalWorldOnB * directionSign ); F32 dot = mDot( normal, VectorF( 0, 0, 1 ) ); if ( dot > maxDot ) { maxDot = dot; btCollisionObject *colObject = (btCollisionObject*)collisionPair->m_pProxy0->m_clientObject; *contactObject = PhysicsUserData::getObject( colObject->getUserPointer() ); *contactNormal = normal; } } } } }
inline bool isOnPlane(Point3F p,PlaneF& plane) { F32 dist = mDot(plane,p) + plane.d; return dist < 0.1 && dist > -0.1; }
void FlyingVehicle::updateJet(F32 dt) { // Thrust Animation threads // Back if (mJetSeq[BackActivate] >=0 ) { if(!mBackMaintainOn || mThrustDirection != ThrustForward) { if(mBackMaintainOn) { mShapeInstance->setPos(mJetThread[BackActivate], 1); mShapeInstance->destroyThread(mJetThread[BackMaintain]); mBackMaintainOn = false; } mShapeInstance->setTimeScale(mJetThread[BackActivate], (mThrustDirection == ThrustForward)? 1.0f : -1.0f); mShapeInstance->advanceTime(dt,mJetThread[BackActivate]); } if(mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn && mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0) { mShapeInstance->setPos(mJetThread[BackActivate], 0); mShapeInstance->setTimeScale(mJetThread[BackActivate], 0); mJetThread[BackMaintain] = mShapeInstance->addThread(); mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0); mShapeInstance->setTimeScale(mJetThread[BackMaintain],1); mBackMaintainOn = true; } if(mBackMaintainOn) mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]); } // Thrust Animation threads // Bottom if (mJetSeq[BottomActivate] >=0 ) { if(!mBottomMaintainOn || mThrustDirection != ThrustDown || !mJetting) { if(mBottomMaintainOn) { mShapeInstance->setPos(mJetThread[BottomActivate], 1); mShapeInstance->destroyThread(mJetThread[BottomMaintain]); mBottomMaintainOn = false; } mShapeInstance->setTimeScale(mJetThread[BottomActivate], (mThrustDirection == ThrustDown && mJetting)? 1.0f : -1.0f); mShapeInstance->advanceTime(dt,mJetThread[BottomActivate]); } if(mJetSeq[BottomMaintain] >= 0 && !mBottomMaintainOn && mShapeInstance->getPos(mJetThread[BottomActivate]) >= 1.0) { mShapeInstance->setPos(mJetThread[BottomActivate], 0); mShapeInstance->setTimeScale(mJetThread[BottomActivate], 0); mJetThread[BottomMaintain] = mShapeInstance->addThread(); mShapeInstance->setSequence(mJetThread[BottomMaintain],mJetSeq[BottomMaintain],0); mShapeInstance->setTimeScale(mJetThread[BottomMaintain],1); mBottomMaintainOn = true; } if(mBottomMaintainOn) mShapeInstance->advanceTime(dt,mJetThread[BottomMaintain]); } // Jet particles for (S32 j = 0; j < NumThrustDirections; j++) { JetActivation& jet = sJetActivation[j]; updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter], jet.node,FlyingVehicleData::MaxDirectionJets); } // Trail jets Point3F yv; mObjToWorld.getColumn(1,&yv); F32 speed = mFabs(mDot(yv,mRigid.linVelocity)); F32 trail = 0; if (speed > mDataBlock->minTrailSpeed) { trail = dt; if (speed < mDataBlock->maxSpeed) trail *= (speed - mDataBlock->minTrailSpeed) / mDataBlock->maxSpeed; } updateEmitter(trail,trail,mDataBlock->jetEmitter[FlyingVehicleData::TrailEmitter], FlyingVehicleData::TrailNode,FlyingVehicleData::MaxTrails); // Allocate/Deallocate voice on demand. if ( !mJetSound ) return; if ( !mJetting ) mJetSound->stop(); else { if ( !mJetSound->isPlaying() ) mJetSound->play(); mJetSound->setTransform( getTransform() ); mJetSound->setVelocity( getVelocity() ); } }
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); } } }
bool ConvexShape::castRay( const Point3F &start, const Point3F &end, RayInfo *info ) { if ( mPlanes.empty() ) return false; const Vector< PlaneF > &planeList = mPlanes; const U32 planeCount = planeList.size(); F32 t; F32 tmin = F32_MAX; S32 hitFace = -1; Point3F hitPnt, pnt; VectorF rayDir( end - start ); rayDir.normalizeSafe(); if ( false ) { PlaneF plane( Point3F(0,0,0), Point3F(0,0,1) ); Point3F sp( 0,0,-1 ); Point3F ep( 0,0,1 ); F32 t = plane.intersect( sp, ep ); Point3F hitPnt; hitPnt.interpolate( sp, ep, t ); } for ( S32 i = 0; i < planeCount; i++ ) { // Don't hit the back-side of planes. if ( mDot( rayDir, planeList[i] ) >= 0.0f ) continue; t = planeList[i].intersect( start, end ); if ( t >= 0.0f && t <= 1.0f && t < tmin ) { pnt.interpolate( start, end, t ); S32 j = 0; for ( ; j < planeCount; j++ ) { if ( i == j ) continue; F32 dist = planeList[j].distToPlane( pnt ); if ( dist > 1.0e-004f ) break; } if ( j == planeCount ) { tmin = t; hitFace = i; } } } if ( hitFace == -1 ) return false; info->face = hitFace; info->material = mMaterialInst; info->normal = planeList[ hitFace ]; info->object = this; info->t = tmin; //mObjToWorld.mulV( info->normal ); return true; }
void Projectile::simulate( F32 dt ) { if ( isServerObject() && mCurrTick >= mDataBlock->lifetime ) { deleteObject(); return; } if ( mHasExploded ) return; // ... otherwise, we have to do some simulation work. RayInfo rInfo; Point3F oldPosition; Point3F newPosition; oldPosition = mCurrPosition; if ( mDataBlock->isBallistic ) mCurrVelocity.z -= 9.81 * mDataBlock->gravityMod * dt; newPosition = oldPosition + mCurrVelocity * dt; // disable the source objects collision reponse for a short time while we // determine if the projectile is capable of moving from the old position // to the new position, otherwise we'll hit ourself bool disableSourceObjCollision = (mSourceObject.isValid() && mCurrTick <= SourceIdTimeoutTicks); if ( disableSourceObjCollision ) mSourceObject->disableCollision(); disableCollision(); // Determine if the projectile is going to hit any object between the previous // position and the new position. This code is executed both on the server // and on the client (for prediction purposes). It is possible that the server // will have registered a collision while the client prediction has not. If this // happens the client will be corrected in the next packet update. // Raycast the abstract PhysicsWorld if a PhysicsPlugin exists. bool hit = false; if ( mPhysicsWorld ) hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce ); else hit = getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo); if ( hit ) { // make sure the client knows to bounce if ( isServerObject() && ( rInfo.object->getTypeMask() & csmStaticCollisionMask ) == 0 ) setMaskBits( BounceMask ); MatrixF xform( true ); xform.setColumn( 3, rInfo.point ); setTransform( xform ); mCurrPosition = rInfo.point; // Get the object type before the onCollision call, in case // the object is destroyed. U32 objectType = rInfo.object->getTypeMask(); // re-enable the collision response on the source object since // we need to process the onCollision and explode calls if ( disableSourceObjCollision ) mSourceObject->enableCollision(); // Ok, here is how this works: // onCollision is called to notify the server scripts that a collision has occurred, then // a call to explode is made to start the explosion process. The call to explode is made // twice, once on the server and once on the client. // The server process is responsible for two things: // 1) setting the ExplosionMask network bit to guarantee that the client calls explode // 2) initiate the explosion process on the server scripts // The client process is responsible for only one thing: // 1) drawing the appropriate explosion // It is possible that during the processTick the server may have decided that a hit // has occurred while the client prediction has decided that a hit has not occurred. // In this particular scenario the client will have failed to call onCollision and // explode during the processTick. However, the explode function will be called // during the next packet update, due to the ExplosionMask network bit being set. // onCollision will remain uncalled on the client however, therefore no client // specific code should be placed inside the function! onCollision( rInfo.point, rInfo.normal, rInfo.object ); // Next order of business: do we explode on this hit? if ( mCurrTick > mDataBlock->armingDelay || mDataBlock->armingDelay == 0 ) { mCurrVelocity = Point3F::Zero; explode( rInfo.point, rInfo.normal, objectType ); } if ( mDataBlock->isBallistic ) { // Otherwise, this represents a bounce. First, reflect our velocity // around the normal... Point3F bounceVel = mCurrVelocity - rInfo.normal * (mDot( mCurrVelocity, rInfo.normal ) * 2.0); mCurrVelocity = bounceVel; // Add in surface friction... Point3F tangent = bounceVel - rInfo.normal * mDot(bounceVel, rInfo.normal); mCurrVelocity -= tangent * mDataBlock->bounceFriction; // Now, take elasticity into account for modulating the speed of the grenade mCurrVelocity *= mDataBlock->bounceElasticity; // Set the new position to the impact and the bounce // will apply on the next frame. //F32 timeLeft = 1.0f - rInfo.t; newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f; } else { mCurrVelocity = Point3F::Zero; } } // re-enable the collision response on the source object now // that we are done processing the ballistic movement if ( disableSourceObjCollision ) mSourceObject->enableCollision(); enableCollision(); if ( isClientObject() ) { emitParticles( mCurrPosition, newPosition, mCurrVelocity, U32( dt * 1000.0f ) ); updateSound(); } mCurrDeltaBase = newPosition; mCurrBackDelta = mCurrPosition - newPosition; mCurrPosition = newPosition; MatrixF xform( true ); xform.setColumn( 3, mCurrPosition ); setTransform( xform ); }
void ConvexShape::Geometry::generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents ) { PROFILE_SCOPE( Geometry_generate ); points.clear(); faces.clear(); AssertFatal( planes.size() == tangents.size(), "ConvexShape - incorrect plane/tangent count." ); #ifdef TORQUE_ENABLE_ASSERTS for ( S32 i = 0; i < planes.size(); i++ ) { F32 dt = mDot( planes[i], tangents[i] ); AssertFatal( mIsZero( dt, 0.0001f ), "ConvexShape - non perpendicular input vectors." ); AssertFatal( planes[i].isUnitLength() && tangents[i].isUnitLength(), "ConvexShape - non unit length input vector." ); } #endif const U32 planeCount = planes.size(); Point3F linePt, lineDir; for ( S32 i = 0; i < planeCount; i++ ) { Vector< MathUtils::Line > collideLines; // Find the lines defined by the intersection of this plane with all others. for ( S32 j = 0; j < planeCount; j++ ) { if ( i == j ) continue; if ( planes[i].intersect( planes[j], linePt, lineDir ) ) { collideLines.increment(); MathUtils::Line &line = collideLines.last(); line.origin = linePt; line.direction = lineDir; } } if ( collideLines.empty() ) continue; // Find edges and points defined by the intersection of these lines. // As we find them we fill them into our working ConvexShape::Face // structure. Face newFace; for ( S32 j = 0; j < collideLines.size(); j++ ) { Vector< Point3F > collidePoints; for ( S32 k = 0; k < collideLines.size(); k++ ) { if ( j == k ) continue; MathUtils::LineSegment segment; MathUtils::mShortestSegmentBetweenLines( collideLines[j], collideLines[k], &segment ); F32 dist = ( segment.p0 - segment.p1 ).len(); if ( dist < 0.0005f ) { S32 l = 0; for ( ; l < planeCount; l++ ) { if ( planes[l].whichSide( segment.p0 ) == PlaneF::Front ) break; } if ( l == planeCount ) collidePoints.push_back( segment.p0 ); } } //AssertFatal( collidePoints.size() <= 2, "A line can't collide with more than 2 other lines in a convex shape..." ); if ( collidePoints.size() != 2 ) continue; // Push back collision points into our points vector // if they are not duplicates and determine the id // index for those points to be used by Edge(s). const Point3F &pnt0 = collidePoints[0]; const Point3F &pnt1 = collidePoints[1]; S32 idx0 = -1; S32 idx1 = -1; for ( S32 k = 0; k < points.size(); k++ ) { if ( pnt0.equal( points[k] ) ) { idx0 = k; break; } } for ( S32 k = 0; k < points.size(); k++ ) { if ( pnt1.equal( points[k] ) ) { idx1 = k; break; } } if ( idx0 == -1 ) { points.push_back( pnt0 ); idx0 = points.size() - 1; } if ( idx1 == -1 ) { points.push_back( pnt1 ); idx1 = points.size() - 1; } // Construct the Face::Edge defined by this collision. S32 localIdx0 = newFace.points.push_back_unique( idx0 ); S32 localIdx1 = newFace.points.push_back_unique( idx1 ); newFace.edges.increment(); ConvexShape::Edge &newEdge = newFace.edges.last(); newEdge.p0 = localIdx0; newEdge.p1 = localIdx1; } if ( newFace.points.size() < 3 ) continue; //AssertFatal( newFace.points.size() == newFace.edges.size(), "ConvexShape - face point count does not equal edge count." ); // Fill in some basic Face information. newFace.id = i; newFace.normal = planes[i]; newFace.tangent = tangents[i]; // Make a working array of Point3Fs on this face. U32 pntCount = newFace.points.size(); Point3F *workPoints = new Point3F[ pntCount ]; for ( S32 j = 0; j < pntCount; j++ ) workPoints[j] = points[ newFace.points[j] ]; // Calculate the average point for calculating winding order. Point3F averagePnt = Point3F::Zero; for ( S32 j = 0; j < pntCount; j++ ) averagePnt += workPoints[j]; averagePnt /= pntCount; // Sort points in correct winding order. U32 *vertMap = new U32[pntCount]; MatrixF quadMat( true ); quadMat.setPosition( averagePnt ); quadMat.setColumn( 0, newFace.tangent ); quadMat.setColumn( 1, mCross( newFace.normal, newFace.tangent ) ); quadMat.setColumn( 2, newFace.normal ); quadMat.inverse(); // Transform working points into quad space // so we can work with them as 2D points. for ( S32 j = 0; j < pntCount; j++ ) quadMat.mulP( workPoints[j] ); MathUtils::sortQuadWindingOrder( true, workPoints, vertMap, pntCount ); // Save points in winding order. for ( S32 j = 0; j < pntCount; j++ ) newFace.winding.push_back( vertMap[j] ); // Calculate the area and centroid of the face. newFace.area = 0.0f; for ( S32 j = 0; j < pntCount; j++ ) { S32 k = ( j + 1 ) % pntCount; const Point3F &p0 = workPoints[ vertMap[j] ]; const Point3F &p1 = workPoints[ vertMap[k] ]; // Note that this calculation returns positive area for clockwise winding // and negative area for counterclockwise winding. newFace.area += p0.y * p1.x; newFace.area -= p0.x * p1.y; } //AssertFatal( newFace.area > 0.0f, "ConvexShape - face area was not positive." ); if ( newFace.area > 0.0f ) newFace.area /= 2.0f; F32 factor; F32 cx = 0.0f, cy = 0.0f; for ( S32 j = 0; j < pntCount; j++ ) { S32 k = ( j + 1 ) % pntCount; const Point3F &p0 = workPoints[ vertMap[j] ]; const Point3F &p1 = workPoints[ vertMap[k] ]; factor = p0.x * p1.y - p1.x * p0.y; cx += ( p0.x + p1.x ) * factor; cy += ( p0.y + p1.y ) * factor; } factor = 1.0f / ( newFace.area * 6.0f ); newFace.centroid.set( cx * factor, cy * factor, 0.0f ); quadMat.inverse(); quadMat.mulP( newFace.centroid ); delete [] workPoints; workPoints = NULL; // Make polygons / triangles for this face. const U32 polyCount = pntCount - 2; newFace.triangles.setSize( polyCount ); for ( S32 j = 0; j < polyCount; j++ ) { ConvexShape::Triangle &poly = newFace.triangles[j]; poly.p0 = vertMap[0]; if ( j == 0 ) { poly.p1 = vertMap[ 1 ]; poly.p2 = vertMap[ 2 ]; } else { poly.p1 = vertMap[ 1 + j ]; poly.p2 = vertMap[ 2 + j ]; } } delete [] vertMap; // Calculate texture coordinates for each point in this face. const Point3F binormal = mCross( newFace.normal, newFace.tangent ); PlaneF planey( newFace.centroid - 0.5f * binormal, binormal ); PlaneF planex( newFace.centroid - 0.5f * newFace.tangent, newFace.tangent ); newFace.texcoords.setSize( newFace.points.size() ); for ( S32 j = 0; j < newFace.points.size(); j++ ) { F32 x = planex.distToPlane( points[ newFace.points[ j ] ] ); F32 y = planey.distToPlane( points[ newFace.points[ j ] ] ); newFace.texcoords[j].set( -x, -y ); } // Data verification tests. #ifdef TORQUE_ENABLE_ASSERTS //S32 triCount = newFace.triangles.size(); //S32 edgeCount = newFace.edges.size(); //AssertFatal( triCount == edgeCount - 2, "ConvexShape - triangle/edge count do not match." ); /* for ( S32 j = 0; j < triCount; j++ ) { F32 area = MathUtils::mTriangleArea( points[ newFace.points[ newFace.triangles[j][0] ] ], points[ newFace.points[ newFace.triangles[j][1] ] ], points[ newFace.points[ newFace.triangles[j][2] ] ] ); AssertFatal( area > 0.0f, "ConvexShape - triangle winding bad." ); }*/ #endif // Done with this Face. faces.push_back( newFace ); } }
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; } } } } }
void GFXDrawUtil::_drawSolidPolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm ) { GFXDEBUGEVENT_SCOPE( GFXDrawUtil_DrawSolidPolyhedron, ColorI::GREEN ); const U32 numPoints = poly.getNumPoints(); const Point3F* points = poly.getPoints(); const PlaneF* planes = poly.getPlanes(); const Point3F viewDir = GFX->getViewMatrix().getForwardVector(); // Create a temp buffer for the vertices and // put all the polyhedron's points in there. GFXVertexBufferHandle< GFXVertexPC > verts( mDevice, numPoints, GFXBufferTypeVolatile ); verts.lock(); for( U32 i = 0; i < numPoints; ++ i ) { verts[ i ].point = points[ i ]; verts[ i ].color = color; } if( xfm ) { for( U32 i = 0; i < numPoints; ++ i ) xfm->mulP( verts[ i ].point ); } verts.unlock(); // Allocate a temp buffer for the face indices. const U32 numIndices = poly.getNumEdges() * 2; const U32 numPlanes = poly.getNumPlanes(); GFXPrimitiveBufferHandle prims( mDevice, numIndices, 0, GFXBufferTypeVolatile ); // Unfortunately, since polygons may have varying numbers of // vertices, we also need to retain that information. FrameTemp< U32 > numIndicesForPoly( numPlanes ); U32 numPolys = 0; // Create all the polygon indices. U16* indices; prims.lock( &indices ); U32 idx = 0; for( U32 i = 0; i < numPlanes; ++ i ) { // Since face extraction is somewhat costly, don't bother doing it for // backfacing polygons if culling is enabled. if( !desc.cullDefined || desc.cullMode != GFXCullNone ) { F32 dot = mDot( planes[ i ], viewDir ); // See if it faces *the same way* as the view direction. This would // normally mean that the face is *not* backfacing but since we expect // planes on the polyhedron to be facing *inwards*, we need to reverse // the logic here. if( dot > 0.f ) continue; } U32 numPoints = poly.extractFace( i, &indices[ idx ], numIndices - idx ); numIndicesForPoly[ numPolys ] = numPoints; idx += numPoints; numPolys ++; } prims.unlock(); // Set up state. mDevice->setStateBlockByDesc( desc ); mDevice->setupGenericShaders(); mDevice->setVertexBuffer( verts ); mDevice->setPrimitiveBuffer( prims ); // Render one triangle fan for each polygon. U32 startIndex = 0; for( U32 i = 0; i < numPolys; ++ i ) { U32 numVerts = numIndicesForPoly[ i ]; mDevice->drawIndexedPrimitive( GFXTriangleFan, 0, 0, numPoints, startIndex, numVerts - 2 ); startIndex += numVerts; } }
void ScatterSky::_getColor( const Point3F &pos, ColorF *outColor ) { PROFILE_SCOPE( ScatterSky_GetColor ); F32 scaleOverScaleDepth = mScale / mRayleighScaleDepth; F32 rayleighBrightness = mRayleighScattering * mSkyBrightness; F32 mieBrightness = mMieScattering * mSkyBrightness; Point3F invWaveLength( 1.0f / mWavelength4[0], 1.0f / mWavelength4[1], 1.0f / mWavelength4[2] ); Point3F v3Pos = pos / 6378000.0f; v3Pos.z += mSphereInnerRadius; Point3F newCamPos( 0, 0, smViewerHeight ); VectorF v3Ray = v3Pos - newCamPos; F32 fFar = v3Ray.len(); v3Ray / fFar; v3Ray.normalizeSafe(); Point3F v3Start = newCamPos; F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight ) ); F32 fStartAngle = mDot( v3Ray, v3Start ); F32 fStartOffset = fDepth * _vernierScale( fStartAngle ); F32 fSampleLength = fFar / 2.0f; F32 fScaledLength = fSampleLength * mScale; VectorF v3SampleRay = v3Ray * fSampleLength; Point3F v3SamplePoint = v3Start + v3SampleRay * 0.5f; Point3F v3FrontColor( 0, 0, 0 ); for ( U32 i = 0; i < 2; i++ ) { F32 fHeight = v3SamplePoint.len(); F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight) ); F32 fLightAngle = mDot( mLightDir, v3SamplePoint ) / fHeight; F32 fCameraAngle = mDot( v3Ray, v3SamplePoint ) / fHeight; F32 fScatter = (fStartOffset + fDepth * ( _vernierScale( fLightAngle ) - _vernierScale( fCameraAngle ) )); Point3F v3Attenuate( 0, 0, 0 ); F32 tmp = mExp( -fScatter * (invWaveLength[0] * mRayleighScattering4PI + mMieScattering4PI) ); v3Attenuate.x = tmp; tmp = mExp( -fScatter * (invWaveLength[1] * mRayleighScattering4PI + mMieScattering4PI) ); v3Attenuate.y = tmp; tmp = mExp( -fScatter * (invWaveLength[2] * mRayleighScattering4PI + mMieScattering4PI) ); v3Attenuate.z = tmp; v3FrontColor += v3Attenuate * (fDepth * fScaledLength); v3SamplePoint += v3SampleRay; } Point3F mieColor = v3FrontColor * mieBrightness; Point3F rayleighColor = v3FrontColor * (invWaveLength * rayleighBrightness); Point3F v3Direction = newCamPos - v3Pos; v3Direction.normalize(); F32 fCos = mDot( mLightDir, v3Direction ) / v3Direction.len(); F32 fCos2 = fCos * fCos; F32 g = -0.991f; F32 g2 = g * g; F32 miePhase = _getMiePhase( fCos, fCos2, g, g2 ); Point3F color = rayleighColor + (miePhase * mieColor); ColorF tmp( color.x, color.y, color.z, color.y ); Point3F expColor( 0, 0, 0 ); expColor.x = 1.0f - exp(-mExposure * color.x); expColor.y = 1.0f - exp(-mExposure * color.y); expColor.z = 1.0f - exp(-mExposure * color.z); tmp.set( expColor.x, expColor.y, expColor.z, 1.0f ); if ( !tmp.isValidColor() ) { F32 len = expColor.len(); if ( len > 0 ) expColor /= len; } outColor->set( expColor.x, expColor.y, expColor.z, 1.0f ); }
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 FlyingVehicle::updateForces(F32 /*dt*/) { PROFILE_SCOPE( FlyingVehicle_UpdateForces ); MatrixF currPosMat; mRigid.getTransform(&currPosMat); mRigid.atRest = false; Point3F massCenter; currPosMat.mulP(mDataBlock->massCenter,&massCenter); Point3F xv,yv,zv; currPosMat.getColumn(0,&xv); currPosMat.getColumn(1,&yv); currPosMat.getColumn(2,&zv); F32 speed = mRigid.linVelocity.len(); Point3F force = Point3F(0, 0, sFlyingVehicleGravity * mRigid.mass * mGravityMod); Point3F torque = Point3F(0, 0, 0); // Drag at any speed force -= mRigid.linVelocity * mDataBlock->minDrag; torque -= mRigid.angMomentum * mDataBlock->rotationalDrag; // Auto-stop at low speeds if (speed < mDataBlock->maxAutoSpeed) { F32 autoScale = 1 - speed / mDataBlock->maxAutoSpeed; // Gyroscope F32 gf = mDataBlock->autoAngularForce * autoScale; torque -= xv * gf * mDot(yv,Point3F(0,0,1)); // Manuevering jets F32 sf = mDataBlock->autoLinearForce * autoScale; force -= yv * sf * mDot(yv, mRigid.linVelocity); force -= xv * sf * mDot(xv, mRigid.linVelocity); } // Hovering Jet F32 vf = -sFlyingVehicleGravity * mRigid.mass * mGravityMod; F32 h = getHeight(); if (h <= 1) { if (h > 0) { vf -= vf * h * 0.1; } else { vf += mDataBlock->jetForce * -h; } } force += zv * vf; // Damping "surfaces" force -= xv * mDot(xv,mRigid.linVelocity) * mDataBlock->horizontalSurfaceForce; force -= zv * mDot(zv,mRigid.linVelocity) * mDataBlock->verticalSurfaceForce; // Turbo Jet if (mJetting) { if (mThrustDirection == ThrustForward) force += yv * mDataBlock->jetForce * mCeilingFactor; else if (mThrustDirection == ThrustBackward) force -= yv * mDataBlock->jetForce * mCeilingFactor; else force += zv * mDataBlock->jetForce * mDataBlock->vertThrustMultiple * mCeilingFactor; } // Maneuvering jets force += yv * (mThrust.y * mDataBlock->maneuveringForce * mCeilingFactor); force += xv * (mThrust.x * mDataBlock->maneuveringForce * mCeilingFactor); // Steering Point2F steering; steering.x = mSteering.x / mDataBlock->maxSteeringAngle; steering.x *= mFabs(steering.x); steering.y = mSteering.y / mDataBlock->maxSteeringAngle; steering.y *= mFabs(steering.y); torque -= xv * steering.y * mDataBlock->steeringForce; torque -= zv * steering.x * mDataBlock->steeringForce; // Roll torque += yv * steering.x * mDataBlock->steeringRollForce; F32 ar = mDataBlock->autoAngularForce * mDot(xv,Point3F(0,0,1)); ar -= mDataBlock->rollForce * mDot(xv, mRigid.linVelocity); torque += yv * ar; // Add in force from physical zones... force += mAppliedForce; // Container buoyancy & drag force -= Point3F(0, 0, 1) * (mBuoyancy * sFlyingVehicleGravity * mRigid.mass * mGravityMod); force -= mRigid.linVelocity * mDrag; // mRigid.force = force; mRigid.torque = torque; }
void ClippedPolyList::end() { PROFILE_SCOPE( ClippedPolyList_Clip ); Poly& poly = mPolyList.last(); // Reject polygons facing away from our normal. if ( mDot( poly.plane, mNormal ) < mNormalTolCosineRadians ) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } // Build initial inside/outside plane masks U32 indexStart = poly.vertexStart; U32 vertexCount = mIndexList.size() - indexStart; U32 frontMask = 0,backMask = 0; U32 i; for (i = indexStart; i < mIndexList.size(); i++) { U32 mask = mVertexList[mIndexList[i]].mask; frontMask |= mask; backMask |= ~mask; } // Trivial accept if all the vertices are on the backsides of // all the planes. if (!frontMask) { poly.vertexCount = vertexCount; return; } // Trivial reject if any plane not crossed has all it's points // on the front. U32 crossMask = frontMask & backMask; if (~crossMask & frontMask) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } // Potentially, this will add up to mPlaneList.size() * (indexStart - indexEnd) // elements to mIndexList, so ensure that it has enough space to store that // so we can use push_back_noresize. If you find this code block getting hit // frequently, changing the value of 'IndexListReserveSize' or doing some selective // allocation is suggested // // TODO: Re-visit this, since it obviously does not work correctly, and than // re-enable the push_back_noresize //while(mIndexList.size() + mPlaneList.size() * (mIndexList.size() - indexStart) > mIndexList.capacity() ) // mIndexList.reserve(mIndexList.capacity() * 2); // Need to do some clipping for (U32 p = 0; p < mPlaneList.size(); p++) { U32 pmask = 1 << p; // Only test against this plane if we have something // on both sides if (!(crossMask & pmask)) continue; U32 indexEnd = mIndexList.size(); U32 i1 = indexEnd - 1; U32 mask1 = mVertexList[mIndexList[i1]].mask; for (U32 i2 = indexStart; i2 < indexEnd; i2++) { U32 mask2 = mVertexList[mIndexList[i2]].mask; if ((mask1 ^ mask2) & pmask) { // mVertexList.increment(); VectorF& v1 = mVertexList[mIndexList[i1]].point; VectorF& v2 = mVertexList[mIndexList[i2]].point; VectorF vv = v2 - v1; F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv); mNormalList.increment(); VectorF& n1 = mNormalList[mIndexList[i1]]; VectorF& n2 = mNormalList[mIndexList[i1]]; VectorF nn = mLerp( n1, n2, t ); nn.normalizeSafe(); mNormalList.last() = nn; mIndexList.push_back/*_noresize*/(mVertexList.size() - 1); Vertex& iv = mVertexList.last(); iv.point.x = v1.x + vv.x * t; iv.point.y = v1.y + vv.y * t; iv.point.z = v1.z + vv.z * t; iv.mask = 0; // Test against the remaining planes for (U32 i = p + 1; i < mPlaneList.size(); i++) if (mPlaneList[i].distToPlane(iv.point) > 0) { iv.mask = 1 << i; break; } } if (!(mask2 & pmask)) { U32 index = mIndexList[i2]; mIndexList.push_back/*_noresize*/(index); } mask1 = mask2; i1 = i2; } // Check for degenerate indexStart = indexEnd; if (mIndexList.size() - indexStart < 3) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } } // Emit what's left and compress the index list. poly.vertexCount = mIndexList.size() - indexStart; memcpy(&mIndexList[poly.vertexStart], &mIndexList[indexStart],poly.vertexCount); mIndexList.setSize(poly.vertexStart + poly.vertexCount); }
//-------------------------------------- static void m_matF_x_scale_x_planeF_C(const F32* m, const F32* s, const F32* p, F32* presult) { // We take in a matrix, a scale factor, and a plane equation. We want to output // the resultant normal // We have T = m*s // To multiply the normal, we want Inv(Tr(m*s)) // Inv(Tr(ms)) = Inv(Tr(s) * Tr(m)) // = Inv(Tr(m)) * Inv(Tr(s)) // // Inv(Tr(s)) = Inv(s) = [ 1/x 0 0 0] // [ 0 1/y 0 0] // [ 0 0 1/z 0] // [ 0 0 0 1] // // Since m is an affine matrix, // Tr(m) = [ [ ] 0 ] // [ [ R ] 0 ] // [ [ ] 0 ] // [ [ x y z ] 1 ] // // Inv(Tr(m)) = [ [ -1 ] 0 ] // [ [ R ] 0 ] // [ [ ] 0 ] // [ [ A B C ] 1 ] // Where: // // P = (x, y, z) // A = -(Row(0, r) * P); // B = -(Row(1, r) * P); // C = -(Row(2, r) * P); MatrixF invScale(true); F32* pScaleElems = invScale; pScaleElems[MatrixF::idx(0, 0)] = 1.0f / s[0]; pScaleElems[MatrixF::idx(1, 1)] = 1.0f / s[1]; pScaleElems[MatrixF::idx(2, 2)] = 1.0f / s[2]; const Point3F shear( m[MatrixF::idx(3, 0)], m[MatrixF::idx(3, 1)], m[MatrixF::idx(3, 2)] ); const Point3F row0(m[MatrixF::idx(0, 0)], m[MatrixF::idx(0, 1)], m[MatrixF::idx(0, 2)]); const Point3F row1(m[MatrixF::idx(1, 0)], m[MatrixF::idx(1, 1)], m[MatrixF::idx(1, 2)]); const Point3F row2(m[MatrixF::idx(2, 0)], m[MatrixF::idx(2, 1)], m[MatrixF::idx(2, 2)]); const F32 A = -mDot(row0, shear); const F32 B = -mDot(row1, shear); const F32 C = -mDot(row2, shear); MatrixF invTrMatrix(true); F32* destMat = invTrMatrix; destMat[MatrixF::idx(0, 0)] = m[MatrixF::idx(0, 0)]; destMat[MatrixF::idx(1, 0)] = m[MatrixF::idx(1, 0)]; destMat[MatrixF::idx(2, 0)] = m[MatrixF::idx(2, 0)]; destMat[MatrixF::idx(0, 1)] = m[MatrixF::idx(0, 1)]; destMat[MatrixF::idx(1, 1)] = m[MatrixF::idx(1, 1)]; destMat[MatrixF::idx(2, 1)] = m[MatrixF::idx(2, 1)]; destMat[MatrixF::idx(0, 2)] = m[MatrixF::idx(0, 2)]; destMat[MatrixF::idx(1, 2)] = m[MatrixF::idx(1, 2)]; destMat[MatrixF::idx(2, 2)] = m[MatrixF::idx(2, 2)]; destMat[MatrixF::idx(0, 3)] = A; destMat[MatrixF::idx(1, 3)] = B; destMat[MatrixF::idx(2, 3)] = C; invTrMatrix.mul(invScale); Point3F norm(p[0], p[1], p[2]); Point3F point = norm * -p[3]; invTrMatrix.mulP(norm); norm.normalize(); MatrixF temp; memcpy(temp, m, sizeof(F32) * 16); point.x *= s[0]; point.y *= s[1]; point.z *= s[2]; temp.mulP(point); PlaneF resultPlane(point, norm); presult[0] = resultPlane.x; presult[1] = resultPlane.y; presult[2] = resultPlane.z; presult[3] = resultPlane.d; }
void Item::updatePos(const U32 /*mask*/, const F32 dt) { // Try and move Point3F pos; mObjToWorld.getColumn(3,&pos); delta.posVec = pos; bool contact = false; bool nonStatic = false; bool stickyNotify = false; CollisionList collisionList; F32 time = dt; static Polyhedron sBoxPolyhedron; static ExtrudedPolyList sExtrudedPolyList; static EarlyOutPolyList sEarlyOutPolyList; MatrixF collisionMatrix(true); Point3F end = pos + mVelocity * time; U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask; // Part of our speed problem here is that we don't track contact surfaces, like we do // with the player. In order to handle the most common and performance impacting // instance of this problem, we'll use a ray cast to detect any contact surfaces below // us. This won't be perfect, but it only needs to catch a few of these to make a // big difference. We'll cast from the top center of the bounding box at the tick's // beginning to the bottom center of the box at the end. Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5, mObjBox.maxExtents.z); Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5, mObjBox.minExtents.z); collisionMatrix.setColumn(3, pos); collisionMatrix.mulP(startCast); collisionMatrix.setColumn(3, end); collisionMatrix.mulP(endCast); RayInfo rinfo; bool doToughCollision = true; disableCollision(); if (mCollisionObject) mCollisionObject->disableCollision(); if (getContainer()->castRay(startCast, endCast, mask, &rinfo)) { F32 bd = -mDot(mVelocity, rinfo.normal); if (bd >= 0.0) { // Contact! if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) { mVelocity.set(0, 0, 0); mAtRest = true; mAtRestCounter = 0; stickyNotify = true; mStickyCollisionPos = rinfo.point; mStickyCollisionNormal = rinfo.normal; doToughCollision = false;; } else { // Subtract out velocity into surface and friction VectorF fv = mVelocity + rinfo.normal * bd; F32 fvl = fv.len(); if (fvl) { F32 ff = bd * mDataBlock->friction; if (ff < fvl) { fv *= ff / fvl; fvl = ff; } } bd *= 1 + mDataBlock->elasticity; VectorF dv = rinfo.normal * (bd + 0.002); mVelocity += dv; mVelocity -= fv; // Keep track of what we hit contact = true; U32 typeMask = rinfo.object->getTypeMask(); if (!(typeMask & StaticObjectType)) nonStatic = true; if (isServerObject() && (typeMask & ShapeBaseObjectType)) { ShapeBase* col = static_cast<ShapeBase*>(rinfo.object); queueCollision(col,mVelocity - col->getVelocity()); } } } } enableCollision(); if (mCollisionObject) mCollisionObject->enableCollision(); if (doToughCollision) { U32 count; for (count = 0; count < 3; count++) { // Build list from convex states here... end = pos + mVelocity * time; collisionMatrix.setColumn(3, end); Box3F wBox = getObjBox(); collisionMatrix.mul(wBox); Box3F testBox = wBox; Point3F oldMin = testBox.minExtents; Point3F oldMax = testBox.maxExtents; testBox.minExtents.setMin(oldMin + (mVelocity * time)); testBox.maxExtents.setMin(oldMax + (mVelocity * time)); sEarlyOutPolyList.clear(); sEarlyOutPolyList.mNormal.set(0,0,0); sEarlyOutPolyList.mPlaneList.setSize(6); sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0)); sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0)); sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0)); sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0)); sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1)); sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1)); CollisionWorkingList& eorList = mConvex.getWorkingList(); CollisionWorkingList* eopList = eorList.wLink.mNext; while (eopList != &eorList) { if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0) { Box3F convexBox = eopList->mConvex->getBoundingBox(); if (testBox.isOverlapped(convexBox)) { eopList->mConvex->getPolyList(&sEarlyOutPolyList); if (sEarlyOutPolyList.isEmpty() == false) break; } } eopList = eopList->wLink.mNext; } if (sEarlyOutPolyList.isEmpty()) { pos = end; break; } collisionMatrix.setColumn(3, pos); sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true); // Build extruded polyList... VectorF vector = end - pos; sExtrudedPolyList.extrude(sBoxPolyhedron, vector); sExtrudedPolyList.setVelocity(mVelocity); sExtrudedPolyList.setCollisionList(&collisionList); CollisionWorkingList& rList = mConvex.getWorkingList(); CollisionWorkingList* pList = rList.wLink.mNext; while (pList != &rList) { if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0) { Box3F convexBox = pList->mConvex->getBoundingBox(); if (testBox.isOverlapped(convexBox)) { pList->mConvex->getPolyList(&sExtrudedPolyList); } } pList = pList->wLink.mNext; } if (collisionList.getTime() < 1.0) { // Set to collision point F32 dt = time * collisionList.getTime(); pos += mVelocity * dt; time -= dt; // Pick the most resistant surface F32 bd = 0; const Collision* collision = 0; for (int c = 0; c < collisionList.getCount(); c++) { const Collision &cp = collisionList[c]; F32 dot = -mDot(mVelocity,cp.normal); if (dot > bd) { bd = dot; collision = &cp; } } if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) { mVelocity.set(0, 0, 0); mAtRest = true; mAtRestCounter = 0; stickyNotify = true; mStickyCollisionPos = collision->point; mStickyCollisionNormal = collision->normal; break; } else { // Subtract out velocity into surface and friction if (collision) { VectorF fv = mVelocity + collision->normal * bd; F32 fvl = fv.len(); if (fvl) { F32 ff = bd * mDataBlock->friction; if (ff < fvl) { fv *= ff / fvl; fvl = ff; } } bd *= 1 + mDataBlock->elasticity; VectorF dv = collision->normal * (bd + 0.002); mVelocity += dv; mVelocity -= fv; // Keep track of what we hit contact = true; U32 typeMask = collision->object->getTypeMask(); if (!(typeMask & StaticObjectType)) nonStatic = true; if (isServerObject() && (typeMask & ShapeBaseObjectType)) { ShapeBase* col = static_cast<ShapeBase*>(collision->object); queueCollision(col,mVelocity - col->getVelocity()); } } } } else { pos = end; break; } } if (count == 3) { // Couldn't move... mVelocity.set(0, 0, 0); } } // If on the client, calculate delta for backstepping if (isGhost()) { delta.pos = pos; delta.posVec -= pos; delta.dt = 1; } // Update transform MatrixF mat = mObjToWorld; mat.setColumn(3,pos); Parent::setTransform(mat); enableCollision(); if (mCollisionObject) mCollisionObject->enableCollision(); updateContainer(); if ( mPhysicsRep ) mPhysicsRep->setTransform( mat ); // if (contact) { // Check for rest condition if (!nonStatic && mVelocity.len() < sAtRestVelocity) { mVelocity.x = mVelocity.y = mVelocity.z = 0; mAtRest = true; mAtRestCounter = 0; } // Only update the client if we hit a non-static shape or // if this is our final rest pos. if (nonStatic || mAtRest) setMaskBits(PositionMask); } // Collision callbacks. These need to be processed whether we hit // anything or not. if (!isGhost()) { SimObjectPtr<Item> safePtr(this); if (stickyNotify) { notifyCollision(); if(bool(safePtr)) onStickyCollision_callback( getIdString() ); } else notifyCollision(); // water if(bool(safePtr)) { if(!mInLiquid && mWaterCoverage != 0.0f) { mInLiquid = true; if ( !isGhost() ) mDataBlock->onEnterLiquid_callback( this, mWaterCoverage, mLiquidType.c_str() ); } else if(mInLiquid && mWaterCoverage == 0.0f) { mInLiquid = false; if ( !isGhost() ) mDataBlock->onLeaveLiquid_callback( this, mLiquidType.c_str() ); } } } }
void DecalRoad::_captureVerts() { PROFILE_SCOPE( DecalRoad_captureVerts ); //Con::warnf( "%s - captureVerts", isServerObject() ? "server" : "client" ); if ( isServerObject() ) { //Con::errorf( "DecalRoad::_captureVerts - called on the server side!" ); return; } if ( mEdges.size() == 0 ) return; // // Construct ClippedPolyList objects for each pair // of roadEdges. // Use them to capture Terrain verts. // SphereF sphere; RoadEdge *edge = NULL; RoadEdge *nextEdge = NULL; mTriangleCount = 0; mVertCount = 0; Vector<ClippedPolyList> clipperList; for ( U32 i = 0; i < mEdges.size() - 1; i++ ) { Box3F box; edge = &mEdges[i]; nextEdge = &mEdges[i+1]; box.minExtents = edge->p1; box.maxExtents = edge->p1; box.extend( edge->p0 ); box.extend( edge->p2 ); box.extend( nextEdge->p0 ); box.extend( nextEdge->p1 ); box.extend( nextEdge->p2 ); box.minExtents.z -= 5.0f; box.maxExtents.z += 5.0f; sphere.center = ( nextEdge->p1 + edge->p1 ) * 0.5f; sphere.radius = 100.0f; // NOTE: no idea how to calculate this ClippedPolyList clipper; clipper.mNormal.set(0.0f, 0.0f, 0.0f); VectorF n; PlaneF plane0, plane1; // Construct Back Plane n = edge->p2 - edge->p0; n.normalize(); n = mCross( n, edge->uvec ); plane0.set( edge->p0, n ); clipper.mPlaneList.push_back( plane0 ); // Construct Front Plane n = nextEdge->p2 - nextEdge->p0; n.normalize(); n = -mCross( edge->uvec, n ); plane1.set( nextEdge->p0, -n ); //clipper.mPlaneList.push_back( plane1 ); // Test if / where the planes intersect. bool discardLeft = false; bool discardRight = false; Point3F iPos; VectorF iDir; if ( plane0.intersect( plane1, iPos, iDir ) ) { Point2F iPos2F( iPos.x, iPos.y ); Point2F cPos2F( edge->p1.x, edge->p1.y ); Point2F rVec2F( edge->rvec.x, edge->rvec.y ); Point2F iVec2F = iPos2F - cPos2F; F32 iLen = iVec2F.len(); iVec2F.normalize(); if ( iLen < edge->width * 0.5f ) { F32 dot = mDot( rVec2F, iVec2F ); // The clipping planes intersected on the right side, // discard the right side clipping plane. if ( dot > 0.0f ) discardRight = true; // The clipping planes intersected on the left side, // discard the left side clipping plane. else discardLeft = true; } } // Left Plane if ( !discardLeft ) { n = ( nextEdge->p0 - edge->p0 ); n.normalize(); n = mCross( edge->uvec, n ); clipper.mPlaneList.push_back( PlaneF(edge->p0, n) ); } else { nextEdge->p0 = edge->p0; } // Right Plane if ( !discardRight ) { n = ( nextEdge->p2 - edge->p2 ); n.normalize(); n = -mCross( n, edge->uvec ); clipper.mPlaneList.push_back( PlaneF(edge->p2, -n) ); } else { nextEdge->p2 = edge->p2; } n = nextEdge->p2 - nextEdge->p0; n.normalize(); n = -mCross( edge->uvec, n ); plane1.set( nextEdge->p0, -n ); clipper.mPlaneList.push_back( plane1 ); // We have constructed the clipping planes, // now grab/clip the terrain geometry getContainer()->buildPolyList( PLC_Decal, box, TerrainObjectType, &clipper ); clipper.cullUnusedVerts(); clipper.triangulate(); clipper.generateNormals(); // If we got something, add it to the ClippedPolyList Vector if ( !clipper.isEmpty() && !( smDiscardAll && ( discardRight || discardLeft ) ) ) { clipperList.push_back( clipper ); mVertCount += clipper.mVertexList.size(); mTriangleCount += clipper.mPolyList.size(); } } // // Set the roadEdge height to be flush with terrain // This is not really necessary but makes the debug spline rendering better. // for ( U32 i = 0; i < mEdges.size() - 1; i++ ) { edge = &mEdges[i]; _getTerrainHeight( edge->p0.x, edge->p0.y, edge->p0.z ); _getTerrainHeight( edge->p2.x, edge->p2.y, edge->p2.z ); } // // Allocate the RoadBatch(s) // // If we captured no verts, then we can return here without // allocating any RoadBatches or the Vert/Index Buffers. // PreprenderImage will not allocate a render instance while // mBatches.size() is zero. U32 numClippers = clipperList.size(); if ( numClippers == 0 ) return; mBatches.clear(); // Allocate the VertexBuffer and PrimitiveBuffer mVB.set( GFX, mVertCount, GFXBufferTypeStatic ); mPB.set( GFX, mTriangleCount * 3, 0, GFXBufferTypeStatic ); // Lock the VertexBuffer GFXVertexPNTBT *vertPtr = mVB.lock(); if(!vertPtr) return; U32 vertIdx = 0; // // Fill the VertexBuffer and vertex data for the RoadBatches // Loop through the ClippedPolyList Vector // RoadBatch *batch = NULL; F32 texStart = 0.0f; F32 texEnd; for ( U32 i = 0; i < clipperList.size(); i++ ) { ClippedPolyList *clipper = &clipperList[i]; RoadEdge &edge = mEdges[i]; RoadEdge &nextEdge = mEdges[i+1]; VectorF segFvec = nextEdge.p1 - edge.p1; F32 segLen = segFvec.len(); segFvec.normalize(); F32 texLen = segLen / mTextureLength; texEnd = texStart + texLen; BiQuadToSqr quadToSquare( Point2F( edge.p0.x, edge.p0.y ), Point2F( edge.p2.x, edge.p2.y ), Point2F( nextEdge.p2.x, nextEdge.p2.y ), Point2F( nextEdge.p0.x, nextEdge.p0.y ) ); // if ( i % mSegmentsPerBatch == 0 ) { mBatches.increment(); batch = &mBatches.last(); batch->bounds.minExtents = clipper->mVertexList[0].point; batch->bounds.maxExtents = clipper->mVertexList[0].point; batch->startVert = vertIdx; } // Loop through each ClippedPolyList for ( U32 j = 0; j < clipper->mVertexList.size(); j++ ) { // Add each vert to the VertexBuffer Point3F pos = clipper->mVertexList[j].point; vertPtr[vertIdx].point = pos; vertPtr[vertIdx].normal = clipper->mNormalList[j]; Point2F uv = quadToSquare.transform( Point2F(pos.x,pos.y) ); vertPtr[vertIdx].texCoord.x = uv.x; vertPtr[vertIdx].texCoord.y = -(( texEnd - texStart ) * uv.y + texStart); vertPtr[vertIdx].tangent = mCross( segFvec, clipper->mNormalList[j] ); vertPtr[vertIdx].binormal = segFvec; vertIdx++; // Expand the RoadBatch bounds to contain this vertex batch->bounds.extend( pos ); } batch->endVert = vertIdx - 1; texStart = texEnd; } // Unlock the VertexBuffer, we are done filling it. mVB.unlock(); // Lock the PrimitiveBuffer U16 *idxBuff; mPB.lock(&idxBuff); U32 curIdx = 0; U16 vertOffset = 0; batch = NULL; S32 batchIdx = -1; // Fill the PrimitiveBuffer // Loop through each ClippedPolyList in the Vector for ( U32 i = 0; i < clipperList.size(); i++ ) { ClippedPolyList *clipper = &clipperList[i]; if ( i % mSegmentsPerBatch == 0 ) { batchIdx++; batch = &mBatches[batchIdx]; batch->startIndex = curIdx; } for ( U32 j = 0; j < clipper->mPolyList.size(); j++ ) { // Write indices for each Poly ClippedPolyList::Poly *poly = &clipper->mPolyList[j]; AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" ); idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart] + vertOffset; curIdx++; idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 1] + vertOffset; curIdx++; idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 2] + vertOffset; curIdx++; } batch->endIndex = curIdx - 1; vertOffset += clipper->mVertexList.size(); } // Unlock the PrimitiveBuffer, we are done filling it. mPB.unlock(); // Generate the object/world bounds // Is the union of all batch bounding boxes. Box3F box; for ( U32 i = 0; i < mBatches.size(); i++ ) { const RoadBatch &batch = mBatches[i]; if ( i == 0 ) box = batch.bounds; else box.intersect( batch.bounds ); } Point3F pos = getPosition(); mWorldBox = box; resetObjectBox(); // Make sure we are in the correct bins given our world box. if( getSceneManager() != NULL ) getSceneManager()->notifyObjectDirty( this ); }
F32 GjkCollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist, const MatrixF* _w2a, const MatrixF* _w2b) { num_iterations = 0; MatrixF w2a,w2b; if (_w2a == NULL || _w2b == NULL) { w2a = a2w; w2b = b2w; w2a.inverse(); w2b.inverse(); } else { w2a = *_w2a; w2b = *_w2b; } reset(a2w,b2w); bits = 0; all_bits = 0; F32 mu = 0; do { nextBit(); VectorF va,sa; w2a.mulV(-v,&va); p[last] = a->support(va); a2w.mulP(p[last],&sa); VectorF vb,sb; w2b.mulV(v,&vb); q[last] = b->support(vb); b2w.mulP(q[last],&sb); VectorF w = sa - sb; F32 nm = mDot(v, w) / dist; if (nm > mu) mu = nm; if (mu > dontCareDist) return mu; if (mFabs(dist - mu) <= dist * rel_error) return dist; ++num_iterations; if (degenerate(w) || num_iterations > sIteration) { ++num_irregularities; return dist; } y[last] = w; all_bits = bits | last_bit; if (!closest(v)) { ++num_irregularities; return dist; } dist = v.len(); } while (bits < 15 && dist > sTolerance) ; if (bits == 15 && mu <= 0) dist = 0; return dist; }
void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const LightInfo *lightInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly ) { MaterialParameters *matParams = matInstance->getMaterialParameters(); // Set color in the right format, set alpha to the luminance value for the color. ColorF col = lightInfo->getColor(); // TODO: The specularity control of the light // is being scaled by the overall lumiance. // // Not sure if this may be the source of our // bad specularity results maybe? // const Point3F colorToLumiance( 0.3576f, 0.7152f, 0.1192f ); F32 lumiance = mDot(*((const Point3F *)&lightInfo->getColor()), colorToLumiance ); col.alpha *= lumiance; matParams->setSafe( lightColor, col ); matParams->setSafe( lightBrightness, lightInfo->getBrightness() ); switch( lightInfo->getType() ) { case LightInfo::Vector: { VectorF lightDir = lightInfo->getDirection(); worldViewOnly.mulV(lightDir); lightDir.normalize(); matParams->setSafe( lightDirection, lightDir ); // Set small number for alpha since it represents existing specular in // the vector light. This prevents a divide by zero. ColorF ambientColor = renderState->getAmbientLightColor(); ambientColor.alpha = 0.00001f; matParams->setSafe( lightAmbient, ambientColor ); // If no alt color is specified, set it to the average of // the ambient and main color to avoid artifacts. // // TODO: Trilight disabled until we properly implement it // in the light info! // //ColorF lightAlt = lightInfo->getAltColor(); ColorF lightAlt( ColorF::BLACK ); // = lightInfo->getAltColor(); if ( lightAlt.red == 0.0f && lightAlt.green == 0.0f && lightAlt.blue == 0.0f ) lightAlt = (lightInfo->getColor() + renderState->getAmbientLightColor()) / 2.0f; ColorF trilightColor = lightAlt; matParams->setSafe(lightTrilight, trilightColor); } break; case LightInfo::Spot: { const F32 outerCone = lightInfo->getOuterConeAngle(); const F32 innerCone = getMin( lightInfo->getInnerConeAngle(), outerCone ); const F32 outerCos = mCos( mDegToRad( outerCone / 2.0f ) ); const F32 innerCos = mCos( mDegToRad( innerCone / 2.0f ) ); Point4F spotParams( outerCos, innerCos - outerCos, mCos( mDegToRad( outerCone ) ), 0.0f ); matParams->setSafe( lightSpotParams, spotParams ); VectorF lightDir = lightInfo->getDirection(); worldViewOnly.mulV(lightDir); lightDir.normalize(); matParams->setSafe( lightDirection, lightDir ); } // Fall through case LightInfo::Point: { const F32 radius = lightInfo->getRange().x; matParams->setSafe( lightRange, radius ); Point3F lightPos; worldViewOnly.mulP(lightInfo->getPosition(), &lightPos); matParams->setSafe( lightPosition, lightPos ); // Get the attenuation falloff ratio and normalize it. Point3F attenRatio = lightInfo->getExtended<ShadowMapParams>()->attenuationRatio; F32 total = attenRatio.x + attenRatio.y + attenRatio.z; if ( total > 0.0f ) attenRatio /= total; Point2F attenParams( ( 1.0f / radius ) * attenRatio.y, ( 1.0f / ( radius * radius ) ) * attenRatio.z ); matParams->setSafe( lightAttenuation, attenParams ); break; } default: AssertFatal( false, "Bad light type!" ); break; } }
bool SceneCullingState::createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume ) { const Point3F& viewPos = getCameraState().getViewPosition(); const Point3F& viewDir = getCameraState().getViewDirection(); const bool isOrtho = getCullingFrustum().isOrtho(); //TODO: check if we need to handle penetration of the near plane for occluders specially // Allocate space for the clipping planes we generate. Assume the worst case // of every edge generating a plane and, for includers, all edges meeting at // steep angles so we need to insert extra planes (the latter is not possible, // of course, but it makes things less complicated here). For occluders, add // an extra plane for the near cap. const U32 maxPlanes = ( type == SceneCullingVolume::Occluder ? numVertices + 1 : numVertices * 2 ); PlaneF* planes = allocateData< PlaneF >( maxPlanes ); // Keep track of the world-space bounds of the polygon. We use this later // to derive some metrics. Box3F wsPolyBounds; wsPolyBounds.minExtents = Point3F( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX ); wsPolyBounds.maxExtents = Point3F( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN ); // For occluders, also keep track of the nearest, and two farthest silhouette points. We use // this later to construct a near capping plane. F32 minVertexDistanceSquared = TypeTraits< F32 >::MAX; U32 leastDistantVert = 0; F32 maxVertexDistancesSquared[ 2 ] = { TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN }; U32 mostDistantVertices[ 2 ] = { 0, 0 }; // Generate the extrusion volume. For orthographic projections, extrude // parallel to the view direction whereas for parallel projections, extrude // from the viewpoint. U32 numPlanes = 0; U32 lastVertex = numVertices - 1; bool invert = false; for( U32 i = 0; i < numVertices; lastVertex = i, ++ i ) { AssertFatal( numPlanes < maxPlanes, "SceneCullingState::createCullingVolume - Did not allocate enough planes!" ); const Point3F& v1 = vertices[ i ]; const Point3F& v2 = vertices[ lastVertex ]; // Keep track of bounds. wsPolyBounds.minExtents.setMin( v1 ); wsPolyBounds.maxExtents.setMax( v1 ); // Skip the edge if it's length is really short. const Point3F edgeVector = v2 - v1; const F32 edgeVectorLenSquared = edgeVector.lenSquared(); if( edgeVectorLenSquared < 0.025f ) continue; //TODO: might need to do additional checks here for non-planar polygons used by occluders //TODO: test for colinearity of edge vector with view vector (occluders only) // Create a plane for the edge. if( isOrtho ) { // Compute a plane through the two edge vertices and one // of the vertices extended along the view direction. if( !invert ) planes[ numPlanes ] = PlaneF( v1, v1 + viewDir, v2 ); else planes[ numPlanes ] = PlaneF( v2, v1 + viewDir, v1 ); } else { // Compute a plane going through the viewpoint and the two // edge vertices. if( !invert ) planes[ numPlanes ] = PlaneF( v1, viewPos, v2 ); else planes[ numPlanes ] = PlaneF( v2, viewPos, v1 ); } numPlanes ++; // If this is the first plane that we have created, find out whether // the vertex ordering is giving us the plane orientations that we want // (facing inside). If not, invert vertex order from now on. if( numPlanes == 1 ) { Point3F center( 0, 0, 0 ); for( U32 n = 0; n < numVertices; ++ n ) center += vertices[n]; center /= numVertices; if( planes[numPlanes - 1].whichSide( center ) == PlaneF::Back ) { invert = true; planes[ numPlanes - 1 ].invert(); } } // For occluders, keep tabs of the nearest, and two farthest vertices. if( type == SceneCullingVolume::Occluder ) { const F32 distSquared = ( v1 - viewPos ).lenSquared(); if( distSquared < minVertexDistanceSquared ) { minVertexDistanceSquared = distSquared; leastDistantVert = i; } if( distSquared > maxVertexDistancesSquared[ 0 ] ) { // Move 0 to 1. maxVertexDistancesSquared[ 1 ] = maxVertexDistancesSquared[ 0 ]; mostDistantVertices[ 1 ] = mostDistantVertices[ 0 ]; // Replace 0. maxVertexDistancesSquared[ 0 ] = distSquared; mostDistantVertices[ 0 ] = i; } else if( distSquared > maxVertexDistancesSquared[ 1 ] ) { // Replace 1. maxVertexDistancesSquared[ 1 ] = distSquared; mostDistantVertices[ 1 ] = i; } } } // If the extrusion produced no useful result, abort. if( numPlanes < 3 ) return false; // For includers, test the angle of the edges at the current vertex. // If too steep, add an extra plane to improve culling efficiency. if( false )//type == SceneCullingVolume::Includer ) { const U32 numOriginalPlanes = numPlanes; U32 lastPlaneIndex = numPlanes - 1; for( U32 i = 0; i < numOriginalPlanes; lastPlaneIndex = i, ++ i ) { const PlaneF& currentPlane = planes[ i ]; const PlaneF& lastPlane = planes[ lastPlaneIndex ]; // Compute the cosine of the angle between the two plane normals. const F32 cosAngle = mFabs( mDot( currentPlane, lastPlane ) ); // The planes meet at increasingly steep angles the more they point // in opposite directions, i.e the closer the angle of their normals // is to 180 degrees. Skip any two planes that don't get near that. if( cosAngle > 0.1f ) continue; //TODO const Point3F addNormals = currentPlane + lastPlane; const Point3F crossNormals = mCross( currentPlane, lastPlane ); Point3F newNormal = currentPlane + lastPlane;//addNormals - mDot( addNormals, crossNormals ) * crossNormals; // planes[ numPlanes ] = PlaneF( currentPlane.getPosition(), newNormal ); numPlanes ++; } } // Compute the metrics of the culling volume in relation to the view frustum. // // For this, we are short-circuiting things slightly. The correct way (other than doing // full screen projections) would be to transform all the polygon points into camera // space, lay an AABB around those points, and then find the X and Z extents on the near plane. // // However, while not as accurate, a faster way is to just project the axial vectors // of the bounding box onto both the camera right and up vector. This gives us a rough // estimate of the camera-space size of the polygon we're looking at. const MatrixF& cameraTransform = getCameraState().getViewWorldMatrix(); const Point3F cameraRight = cameraTransform.getRightVector(); const Point3F cameraUp = cameraTransform.getUpVector(); const Point3F wsPolyBoundsExtents = wsPolyBounds.getExtents(); F32 widthEstimate = getMax( mFabs( wsPolyBoundsExtents.x * cameraRight.x ), getMax( mFabs( wsPolyBoundsExtents.y * cameraRight.y ), mFabs( wsPolyBoundsExtents.z * cameraRight.z ) ) ); F32 heightEstimate = getMax( mFabs( wsPolyBoundsExtents.x * cameraUp.x ), getMax( mFabs( wsPolyBoundsExtents.y * cameraUp.y ), mFabs( wsPolyBoundsExtents.z * cameraUp.z ) ) ); // If the current camera is a perspective one, divide the two estimates // by the distance of the nearest bounding box vertex to the camera // to account for perspective distortion. if( !isOrtho ) { const Point3F nearestVertex = wsPolyBounds.computeVertex( Box3F::getPointIndexFromOctant( - viewDir ) ); const F32 distance = ( nearestVertex - viewPos ).len(); widthEstimate /= distance; heightEstimate /= distance; } // If we are creating an occluder, check to see if the estimates fit // our minimum requirements. if( type == SceneCullingVolume::Occluder ) { const F32 widthEstimatePercentage = widthEstimate / getCullingFrustum().getWidth(); const F32 heightEstimatePercentage = heightEstimate / getCullingFrustum().getHeight(); if( widthEstimatePercentage < smOccluderMinWidthPercentage || heightEstimatePercentage < smOccluderMinHeightPercentage ) return false; // Reject. } // Use the area estimate as the volume's sort point. const F32 sortPoint = widthEstimate * heightEstimate; // Finally, if it's an occluder, compute a near cap. The near cap prevents objects // in front of the occluder from testing positive. The same could be achieved by // manually comparing distances before testing objects but since that would amount // to the same checks the plane/AABB tests do, it's easier to just add another plane. // Additionally, it gives the benefit of being able to create more precise culling // results by angling the plane. //NOTE: Could consider adding a near cap for includers too when generating a volume // for the outdoor zone as that may prevent quite a bit of space from being included. // However, given that this space will most likely just be filled with interior // stuff anyway, it's probably not worth it. if( type == SceneCullingVolume::Occluder ) { const U32 nearCapIndex = numPlanes; planes[ nearCapIndex ] = PlaneF( vertices[ mostDistantVertices[ 0 ] ], vertices[ mostDistantVertices[ 1 ] ], vertices[ leastDistantVert ] ); // Invert the plane, if necessary. if( planes[ nearCapIndex ].whichSide( viewPos ) == PlaneF::Front ) planes[ nearCapIndex ].invert(); numPlanes ++; } // Create the volume from the planes. outVolume = SceneCullingVolume( type, PlaneSetF( planes, numPlanes ) ); outVolume.setSortPoint( sortPoint ); // Done. return true; }
bool castRayTriangle(const Point3D &orig, const Point3D &dir, const Point3D &vert0, const Point3D &vert1, const Point3D &vert2) { F64 t; Point2D bary; Point3D tvec, qvec; // Find vectors for two edges sharing vert0 const Point3D edge1 = vert1 - vert0; const Point3D edge2 = vert2 - vert0; // Begin calculating determinant - also used to calculate U parameter. Point3D pvec; mCross(dir, edge2, &pvec); // If determinant is near zero, ray lies in plane of triangle. const F64 det = mDot(edge1, pvec); if (det > 0.00001) { // calculate distance from vert0 to ray origin tvec = orig - vert0; // calculate U parameter and test bounds bary.x = mDot(tvec, pvec); // bary.x is really bary.u... if (bary.x < 0.0 || bary.x > det) return false; // prepare to test V parameter mCross(tvec, edge1, &qvec); // calculate V parameter and test bounds bary.y = mDot(dir, qvec); // bary.y is really bary.v if (bary.y < 0.0 || (bary.x + bary.y) > det) return false; } else if(det < -0.00001) { // calculate distance from vert0 to ray origin tvec = orig - vert0; // calculate U parameter and test bounds bary.x = mDot(tvec, pvec); if (bary.x > 0.0 || bary.x < det) return false; // prepare to test V parameter mCross(tvec, edge1, &qvec); // calculate V parameter and test bounds bary.y = mDot(dir, qvec); if (bary.y > 0.0 || (bary.x + bary.y) < det) return false; } else return false; // ray is parallel to the plane of the triangle. const F32 inv_det = 1.0 / det; // calculate t, ray intersects triangle t = mDot(edge2, qvec) * inv_det; bary *= inv_det; //AssertFatal((t >= 0.f && t <=1.f), "AtlasGeomTracer::castRayTriangle - invalid t!"); // Hack, check the math here! return (t >= 0.f && t <=1.f); }
void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type) { if(type == Process) return; TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain(); if ( !terrBlock ) return; if(type == Begin) { mTerrainEditor->lockSelection(true); mTerrainEditor->getRoot()->mouseLock(mTerrainEditor); // the way this works is: // construct a plane that goes through the collision point // with one axis up the terrain Z, and horizontally parallel to the // plane of projection // the cross of the camera ffdv and the terrain up vector produces // the cross plane vector. // all subsequent mouse actions are collided against the plane and the deltaZ // from the previous position is used to delta the selection up and down. Point3F cameraDir; EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir); terrBlock->getTransform().getColumn(2, &mTerrainUpVector); // ok, get the cross vector for the plane: Point3F planeCross; mCross(cameraDir, mTerrainUpVector, &planeCross); planeCross.normalize(); Point3F planeNormal; Point3F intersectPoint; mTerrainEditor->collide(event, intersectPoint); mCross(mTerrainUpVector, planeCross, &planeNormal); mIntersectionPlane.set(intersectPoint, planeNormal); // ok, we have the intersection point... // project the collision point onto the up vector of the terrain mPreviousZ = mDot(mTerrainUpVector, intersectPoint); // add to undo // and record the starting heights for(U32 i = 0; i < sel->size(); i++) { mTerrainEditor->getUndoSel()->add((*sel)[i]); (*sel)[i].mStartHeight = (*sel)[i].mHeight; } } else if(type == Update) { // ok, collide the ray from the event with the intersection plane: Point3F intersectPoint; Point3F start = event.pos; Point3F end = start + event.vec * 1000; F32 t = mIntersectionPlane.intersect(start, end); m_point3F_interpolate( start, end, t, intersectPoint); F32 currentZ = mDot(mTerrainUpVector, intersectPoint); F32 diff = currentZ - mPreviousZ; for(U32 i = 0; i < sel->size(); i++) { (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight; // clamp it if((*sel)[i].mHeight < 0.f) (*sel)[i].mHeight = 0.f; if((*sel)[i].mHeight > 2047.f) (*sel)[i].mHeight = 2047.f; mTerrainEditor->setGridInfoHeight((*sel)[i]); } mTerrainEditor->scheduleGridUpdate(); } else if(type == End) { mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor); } }
bool intersect_triangle(Point3F orig, Point3F dir, Point3F vert0, Point3F vert1, Point3F vert2, F32& t, F32& u, F32& v) { Point3F edge1, edge2, tvec, pvec, qvec; F32 det,inv_det; /* find vectors for two edges sharing vert0 */ edge1.x = vert1.x - vert0.x; edge1.y = vert1.y - vert0.y; edge1.z = vert1.z - vert0.z; edge2.x = vert2.x - vert0.x; edge2.y = vert2.y - vert0.y; edge2.z = vert2.z - vert0.z; /* begin calculating determinant - also used to calculate U parameter */ //CROSS(pvec, dir, edge2); mCross(dir, edge2, &pvec); /* if determinant is near zero, ray lies in plane of triangle */ //det = DOT(edge1, pvec); det = mDot(edge1, pvec); #ifdef TEST_CULL /* define TEST_CULL if culling is desired */ if (det < EPSILON) return 0; /* calculate distance from vert0 to ray origin */ SUB(tvec, orig, vert0); /* calculate U parameter and test bounds */ *u = DOT(tvec, pvec); if (*u < 0.0 || *u > det) return 0; /* prepare to test V parameter */ CROSS(qvec, tvec, edge1); /* calculate V parameter and test bounds */ *v = DOT(dir, qvec); if (*v < 0.0 || *u + *v > det) return 0; /* calculate t, scale parameters, ray intersects triangle */ *t = DOT(edge2, qvec); inv_det = 1.0 / det; *t *= inv_det; *u *= inv_det; *v *= inv_det; #else /* the non-culling branch */ if (det > -EPSILON && det < EPSILON) return false; inv_det = 1.0 / det; /* calculate distance from vert0 to ray origin */ //SUB(tvec, orig, vert0); tvec.x = orig.x - vert0.x; tvec.y = orig.y - vert0.y; tvec.z = orig.z - vert0.z; /* calculate U parameter and test bounds */ // *u = DOT(tvec, pvec) * inv_det; u = mDot(tvec, pvec) * inv_det; if (u < 0.0 || u > 1.0) return false; /* prepare to test V parameter */ //CROSS(qvec, tvec, edge1); mCross(tvec, edge1, &qvec); /* calculate V parameter and test bounds */ // *v = DOT(dir, qvec) * inv_det; v = mDot(dir, qvec) * inv_det; if (v < 0.0 || u + v > 1.0) return false; /* calculate t, ray intersects triangle */ // *t = DOT(edge2, qvec) * inv_det; t = mDot(edge2, qvec) * inv_det; #endif return true; }
void ForestWindMgr::updateWind( const Point3F &camPos, const TreePlacementInfo &info, F32 timeDelta ) { PROFILE_SCOPE(ForestWindMgr_updateWind); // See if we have the blended source available. ForestWindAccumulator *blendDest = NULL; { IdToWindMap::Iterator iter = mPrevSources->find( info.itemKey ); if ( iter != mPrevSources->end() ) { blendDest = iter->value; mPrevSources->erase( iter ); } } // Get some stuff we'll need for finding the emitters. F32 treeHeight = info.scale * info.dataBlock->getObjBox().len_z(); Point3F top = info.pos; top.z += treeHeight; if ( blendDest ) top += ( 1.0f / info.scale ) * blendDest->getDirection(); // Go thru the emitters to accumulate the total wind force. VectorF windForce( 0, 0, 0 ); F32 time = Sim::getCurrentTime() / 1000.0f; ForestWindEmitterList::iterator iter = mEmitters.begin(); for ( ; iter != mEmitters.end(); iter++ ) { ForestWindEmitter *emitter = (*iter); // If disabled or no wind object... skip it. if ( !emitter->isEnabled() || !emitter->getWind() ) continue; ForestWind *wind = emitter->getWind(); F32 strength = wind->getStrength(); if ( emitter->isRadialEmitter() ) { Point3F closest = MathUtils::mClosestPointOnSegment( info.pos, top, emitter->getPosition() ); Point3F dir = closest - emitter->getPosition(); F32 lenSquared = dir.lenSquared(); if ( lenSquared > emitter->getWindRadiusSquared() ) continue; dir *= 1.0f / mSqrt( lenSquared ); F32 att = lenSquared / emitter->getWindRadiusSquared(); strength *= 1.0f - att; windForce += dir * strength; } else { F32 d = mDot( info.pos, Point3F::One ); //PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) ); //F32 d = PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) ); F32 scale = 1.0f + ( mSin( d + ( time / 10.0 ) ) * 0.5f ); windForce += wind->getDirection() * strength * scale; } } // If we need a accumulator then we also need to presimulate. if ( !blendDest ) { blendDest = new ForestWindAccumulator( info ); blendDest->presimulate( windForce, 4.0f / TickSec ); } else blendDest->updateWind( windForce, timeDelta ); mSources->insertUnique( info.itemKey, blendDest ); }