void SceneObject::resetObjectBox() { AssertFatal( mWorldBox.isValidBox(), "SceneObject::resetObjectBox - Bad world box!" ); mObjBox = mWorldBox; mWorldToObj.mul( mObjBox ); Point3F objScale( mObjScale ); objScale.setMax( Point3F( (F32)POINT_EPSILON, (F32)POINT_EPSILON, (F32)POINT_EPSILON ) ); mObjBox.minExtents.convolveInverse( objScale ); mObjBox.maxExtents.convolveInverse( objScale ); AssertFatal( mObjBox.isValidBox(), "SceneObject::resetObjectBox - Bad object box!" ); // Update the mWorldSphere from mWorldBox mWorldBox.getCenter( &mWorldSphere.center ); mWorldSphere.radius = ( mWorldBox.maxExtents - mWorldSphere.center ).len(); // Update scene managers. for( SceneObjectLink* link = mSceneObjectLinks; link != NULL; link = link->getNextLink() ) link->update(); }
void GFXDrawUtil::drawObjectBox( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const MatrixF &objMat, const ColorI &color ) { GFXTransformSaver saver; mDevice->setStateBlockByDesc( desc ); MatrixF scaledObjMat( true ); scaledObjMat = objMat; scaledObjMat.scale( size ); scaledObjMat.setPosition( pos ); PrimBuild::color( color ); PrimBuild::begin( GFXLineList, 48 ); static const Point3F cubePoints[8] = { Point3F(-0.5, -0.5, -0.5), Point3F(-0.5, -0.5, 0.5), Point3F(-0.5, 0.5, -0.5), Point3F(-0.5, 0.5, 0.5), Point3F( 0.5, -0.5, -0.5), Point3F( 0.5, -0.5, 0.5), Point3F( 0.5, 0.5, -0.5), Point3F( 0.5, 0.5, 0.5) }; // 8 corner points of the box for ( U32 i = 0; i < 8; i++ ) { //const Point3F &start = cubePoints[i]; // 3 lines per corner point for ( U32 j = 0; j < 3; j++ ) { Point3F start = cubePoints[i]; Point3F end = start; end[j] *= 0.8f; scaledObjMat.mulP(start); PrimBuild::vertex3fv(start); scaledObjMat.mulP(end); PrimBuild::vertex3fv(end); } } PrimBuild::end(); }
bool afxEA_ZodiacPlane::ea_start() { if (!zode_data) { Con::errorf("afxEA_ZodiacPlane::ea_start() -- missing or incompatible datablock."); return false; } do_runtime_substitutions(); if (!zode_data->use_full_xfm) zode_angle_offset = calc_facing_angle(); switch (zode_data->face_dir) { case afxZodiacPlaneData::FACES_UP: aa_rot.set(Point3F(0.0f,0.0f,1.0f),0.0f); break; case afxZodiacPlaneData::FACES_DOWN: aa_rot.set(Point3F(0.0f,0.0f,-1.0f),0.0f); break; case afxZodiacPlaneData::FACES_FORWARD: aa_rot.set(Point3F(0.0f,1.0f,0.0f),0.0f); break; case afxZodiacPlaneData::FACES_BACK: aa_rot.set(Point3F(0.0f,-1.0f,0.0f),0.0f); break; case afxZodiacPlaneData::FACES_RIGHT: aa_rot.set(Point3F(1.0f,0.0f,0.0f),0.0f); break; case afxZodiacPlaneData::FACES_LEFT: aa_rot.set(Point3F(-1.0f,0.0f,0.0f),0.0f); break; } return true; }
virtual void SetUp() { // Build planes for a unit cube centered at the origin. // Note that the normals must be facing inwards. planes.push_back(PlaneF(Point3F(-0.5f, 0.f, 0.f ), Point3F( 1.f, 0.f, 0.f))); planes.push_back(PlaneF(Point3F( 0.5f, 0.f, 0.f ), Point3F(-1.f, 0.f, 0.f))); planes.push_back(PlaneF(Point3F( 0.f, -0.5f, 0.f ), Point3F( 0.f, 1.f, 0.f))); planes.push_back(PlaneF(Point3F( 0.f, 0.5f, 0.f ), Point3F( 0.f, -1.f, 0.f))); planes.push_back(PlaneF(Point3F( 0.f, 0.f, -0.5f), Point3F( 0.f, 0.f, 1.f))); planes.push_back(PlaneF(Point3F( 0.f, 0.f, 0.5f), Point3F( 0.f, 0.f, -1.f))); }
#include <stdio.h> #include "platform/platform.h" #include "T3D/Trigger.h" #include "T3D/aiFishPlayer.h" #include "T3D/InfluenceWander.h" const static F32 BOUNDARY_SIZE = 0.2f; //when fish get this near boundary, the try and turn back const static F32 BOUNCE_ANGLE = 0.5f; //when cross boundary, this is the angle they turn in by (radians) const static F32 TAN_BOUNCE_ANGLE = tanf(BOUNCE_ANGLE); const static Point3F BOUNDARY_VECTOR = Point3F(BOUNDARY_SIZE,BOUNDARY_SIZE,BOUNDARY_SIZE); bool InfluenceWander::update() { if( !Parent::update() ){ return false; } //---choose new direction ever so often---------------------- if(mTimeOut<getTimeSeconds()){ onExpired(); } return true; } void InfluenceWander::onExpired() { #ifdef DEBUG_HUD // mFish->addLog("wander expired"); #endif Point3F wanderDirection = mFish->getHeading(); reset( &wanderDirection ); }
void Frustum::setProjectionOffset(const Point2F& offsetMat) { mProjectionOffset = offsetMat; mProjectionOffsetMatrix.identity(); mProjectionOffsetMatrix.setPosition(Point3F(mProjectionOffset.x, mProjectionOffset.y, 0.0f)); }
//----------------------------------------------------------------------------- // Torque 3D // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "T3D/gameTSCtrl.h" #include "console/consoleTypes.h" #include "T3D/gameBase.h" #include "T3D/gameConnection.h" #include "T3D/shapeBase.h" #include "T3D/gameFunctions.h" //--------------------------------------------------------------------------- // Debug stuff: Point3F lineTestStart = Point3F(0, 0, 0); Point3F lineTestEnd = Point3F(0, 1000, 0); Point3F lineTestIntersect = Point3F(0, 0, 0); bool gSnapLine = false; //---------------------------------------------------------------------------- // Class: GameTSCtrl //---------------------------------------------------------------------------- IMPLEMENT_CONOBJECT(GameTSCtrl); GameTSCtrl::GameTSCtrl() { } //--------------------------------------------------------------------------- bool GameTSCtrl::onAdd() {
bool AtlasGeomChunkTracer::castLeafRay(const Point2I pos, const Point3F &start, const Point3F &end, const F32 &startT, const F32 &endT, RayInfo *info) { if(AtlasInstance::smRayCollisionDebugLevel == AtlasInstance::RayCollisionDebugToColTree) { const F32 invSize = 1.f / F32(BIT(mTreeDepth-1)); // This is a bit of a hack. But good for testing. // Do collision against the collision tree leaf bounding box and return the result... F32 t; Point3F n; Box3F box; box.minExtents.set(Point3F(F32(pos.x ) * invSize, F32(pos.y ) * invSize, getSquareMin(0, pos))); box.maxExtents.set(Point3F(F32(pos.x+1) * invSize, F32(pos.y+1) * invSize, getSquareMax(0, pos))); //Con::printf(" checking at xy = {%f, %f}->{%f, %f}, [%d, %d]", start.x, start.y, end.x, end.y, pos.x, pos.y); if(box.collideLine(start, end, &t, &n) && t >= startT && t <= endT) { info->t = t; info->normal = n; return true; } return false; } else if( AtlasInstance::smRayCollisionDebugLevel == AtlasInstance::RayCollisionDebugToMesh ) { bool haveHit = false; U32 currentIdx = 0; U32 numIdx = mChunk->mIndexCount; F32 bestT = F32_MAX; U32 bestTri = -1; Point2F bestBary; while( !haveHit && currentIdx < numIdx ) { const Point3F& a = mChunk->mVert[ mChunk->mIndex[ currentIdx ] ].point; const Point3F& b = mChunk->mVert[ mChunk->mIndex[ currentIdx + 1 ] ].point; const Point3F& c = mChunk->mVert[ mChunk->mIndex[ currentIdx + 2 ] ].point; F32 localT; Point2F localBary; // Do the cast, using our conveniently precalculated ray delta... if(castRayTriangle(mRayStart, mRayDelta, a,b,c, localT, localBary)) { if(localT < bestT) { // And it hit before anything else we've seen. bestTri = currentIdx; bestT = localT; bestBary = localBary; haveHit = true; } } currentIdx += 3; } // Fill in extra info for the hit. if(!haveHit) return false; // Calculate the normal, we skip that for the initial check. Point3F norm; // Hi norm! const Point3F &a = mChunk->mVert[mChunk->mIndex[bestTri+0]].point; const Point3F &b = mChunk->mVert[mChunk->mIndex[bestTri+1]].point; const Point3F &c = mChunk->mVert[mChunk->mIndex[bestTri+2]].point; const Point2F &aTC = mChunk->mVert[mChunk->mIndex[bestTri+0]].texCoord; const Point2F &bTC = mChunk->mVert[mChunk->mIndex[bestTri+1]].texCoord; const Point2F &cTC = mChunk->mVert[mChunk->mIndex[bestTri+2]].texCoord; // Store everything relevant into the info structure. info->t = bestT; const Point3F e0 = b-a; const Point3F e1 = c-a; info->normal = mCross(e1, e0); info->normal.normalize(); // Calculate and store the texture coords. const Point2F e0TC = bTC-aTC; const Point2F e1TC = cTC-aTC; info->texCoord = e0TC * bestBary.x + e1TC * bestBary.y + aTC; // Return true, we hit something! return true; } else { // Get the triangle list... U16 *triOffset = mChunk->mColIndicesBuffer + mChunk->mColIndicesOffsets[pos.x * BIT(mChunk->mColTreeDepth-1) + pos.y]; // Store best hit results... bool gotHit = false; F32 bestT = F32_MAX; U16 bestTri = -1, offset; Point2F bestBary; while((offset = *triOffset) != 0xFFFF) { // Advance to the next triangle.. triOffset++; // Get each triangle, and do a raycast against it. Point3F a,b,c; AssertFatal(offset < mChunk->mIndexCount, "AtlasGeomTracer2::castLeafRay - offset past end of index list."); a = mChunk->mVert[mChunk->mIndex[offset+0]].point; b = mChunk->mVert[mChunk->mIndex[offset+1]].point; c = mChunk->mVert[mChunk->mIndex[offset+2]].point; /*Con::printf(" o testing triangle %d ({%f,%f,%f},{%f,%f,%f},{%f,%f,%f})", offset, a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z); */ F32 localT; Point2F localBary; // Do the cast, using our conveniently precalculated ray delta... if(castRayTriangle(mRayStart, mRayDelta, a,b,c, localT, localBary)) { //Con::printf(" - hit triangle %d at t=%f", offset, localT); // The ray intersected, but we have to make sure we hit actually on // the line segment. (ie, a ray isn't a ray, Ray.) // BJGTODO - This should prevent some nasty edge cases, but we // seem to be calculating the wrong start and end T's. // So I've disabled this for now but it will cause // problems later! //if(localT < startT || localT > endT) // continue; // It really, really hit, wow! if(localT < bestT) { // And it hit before anything else we've seen. bestTri = offset; bestT = localT; bestBary = localBary; gotHit = true; } } else { //Con::printf(" - didn't hit triangle %d at t=%f", offset, localT); } } // Fill in extra info for the hit. if(!gotHit) return false; // Calculate the normal, we skip that for the initial check. Point3F norm; // Hi norm! const Point3F &a = mChunk->mVert[mChunk->mIndex[bestTri+0]].point; const Point3F &b = mChunk->mVert[mChunk->mIndex[bestTri+1]].point; const Point3F &c = mChunk->mVert[mChunk->mIndex[bestTri+2]].point; const Point2F &aTC = mChunk->mVert[mChunk->mIndex[bestTri+0]].texCoord; const Point2F &bTC = mChunk->mVert[mChunk->mIndex[bestTri+1]].texCoord; const Point2F &cTC = mChunk->mVert[mChunk->mIndex[bestTri+2]].texCoord; // Store everything relevant into the info structure. info->t = bestT; const Point3F e0 = b-a; const Point3F e1 = c-a; info->normal = mCross(e1, e0); info->normal.normalize(); // Calculate and store the texture coords. const Point2F e0TC = bTC-aTC; const Point2F e1TC = cTC-aTC; info->texCoord = e0TC * bestBary.x + e1TC * bestBary.y + aTC; // Return true, we hit something! return true; } }
/** * Clears the aim location and sets it to the bot's * current destination so he looks where he's going */ void AIPlayer::clearAim() { mAimObject = 0; mAimLocationSet = false; mAimOffset = Point3F(0.0f, 0.0f, 0.0f); }
/** * This method calculates the moves for the AI player * * @param movePtr Pointer to move the move list into */ bool AIPlayer::getAIMove(Move *movePtr) { *movePtr = NullMove; // Use the eye as the current position. MatrixF eye; getEyeTransform(&eye); Point3F location = eye.getPosition(); Point3F rotation = getRotation(); #ifdef TORQUE_NAVIGATION_ENABLED if(mDamageState == Enabled) { if(mMoveState != ModeStop) updateNavMesh(); if(!mFollowData.object.isNull()) { if(mPathData.path.isNull()) { if((getPosition() - mFollowData.object->getPosition()).len() > mFollowData.radius) followObject(mFollowData.object, mFollowData.radius); } else { if((mPathData.path->mTo - mFollowData.object->getPosition()).len() > mFollowData.radius) repath(); else if((getPosition() - mFollowData.object->getPosition()).len() < mFollowData.radius) { clearPath(); mMoveState = ModeStop; throwCallback("onTargetInRange"); } else if((getPosition() - mFollowData.object->getPosition()).len() < mAttackRadius) { throwCallback("onTargetInFiringRange"); } } } } #endif // TORQUE_NAVIGATION_ENABLED // Orient towards the aim point, aim object, or towards // our destination. if (mAimObject || mAimLocationSet || mMoveState != ModeStop) { // Update the aim position if we're aiming for an object if (mAimObject) mAimLocation = mAimObject->getPosition() + mAimOffset; else if (!mAimLocationSet) mAimLocation = mMoveDestination; F32 xDiff = mAimLocation.x - location.x; F32 yDiff = mAimLocation.y - location.y; if (!mIsZero(xDiff) || !mIsZero(yDiff)) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI_F) curYaw -= M_2PI_F; while (curYaw < -M_2PI_F) curYaw += M_2PI_F; // find the yaw offset F32 newYaw = mAtan2( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_PI_F ) yawDiff += M_2PI_F; movePtr->yaw = yawDiff; // Next do pitch. if (!mAimObject && !mAimLocationSet) { // Level out if were just looking at our next way point. Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } else { // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = mAimLocation.z - location.z; F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); if (mFabs(newPitch) > 0.01f) { Point3F headRotation = getHeadRotation(); movePtr->pitch = newPitch - headRotation.x; } } } } else { // Level out if we're not doing anything else Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } // Move towards the destination if (mMoveState != ModeStop) { F32 xDiff = mMoveDestination.x - location.x; F32 yDiff = mMoveDestination.y - location.y; // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) { mMoveState = ModeStop; onReachDestination(); } else { // Build move direction in world space if (mIsZero(xDiff)) movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); movePtr->y = (location.y > mMoveDestination.y) ? -value : value; movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); movePtr->x = (location.x > mMoveDestination.x) ? -value : value; movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; MatrixF moveMatrix; moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); movePtr->x = newMove.x; movePtr->y = newMove.y; // Set movement speed. We'll slow down once we get close // to try and stop on the spot... if (mMoveSlowdown) { F32 speed = mMoveSpeed; F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); F32 maxDist = mMoveTolerance*2; if (dist < maxDist) speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; mMoveState = ModeSlowing; } else { movePtr->x *= mMoveSpeed; movePtr->y *= mMoveSpeed; mMoveState = ModeMove; } if (mMoveStuckTestCountdown > 0) --mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (location - mLastLocation).len(); if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if ( mMoveState != ModeSlowing || locationDelta == 0 ) { mMoveState = ModeStuck; onStuck(); } } } } } // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. if (mAimObject) { if (checkInLos(mAimObject.getPointer())) { if (!mTargetInLOS) { throwCallback( "onTargetEnterLOS" ); mTargetInLOS = true; } } else if (mTargetInLOS) { throwCallback( "onTargetExitLOS" ); mTargetInLOS = false; } } Pose desiredPose = mPose; if ( mSwimming ) desiredPose = SwimPose; else if ( mAiPose == 1 && canCrouch() ) desiredPose = CrouchPose; else if ( mAiPose == 2 && canProne() ) desiredPose = PronePose; else if ( mAiPose == 3 && canSprint() ) desiredPose = SprintPose; else if ( canStand() ) desiredPose = StandPose; setPose( desiredPose ); // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( U32 i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[ i ] = getImageTriggerState( i ); #ifdef TORQUE_NAVIGATION_ENABLED if(mJump == Now) { movePtr->trigger[2] = true; mJump = None; } else if(mJump == Ledge) { // If we're not touching the ground, jump! RayInfo info; if(!getContainer()->castRay(getPosition(), getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) { movePtr->trigger[2] = true; mJump = None; } } #endif // TORQUE_NAVIGATION_ENABLED mLastLocation = location; return true; }
void PlanetRenderImage::render(TSRenderContext &rc) { // A simple planet culling scheme would be to dot the line of sight // with the vector from the camera to the planet. This would eliminate // the length test of v below (after m_cross((Point3F)plane, vpNormal, &v)) GFXSurface *gfxSurface = rc.getSurface(); gfxSurface->setHazeSource(GFX_HAZE_NONE); gfxSurface->setShadeSource(GFX_SHADE_CONSTANT); gfxSurface->setAlphaSource(GFX_ALPHA_NONE); gfxSurface->setFillMode(GFX_FILL_TEXTURE); gfxSurface->setTransparency(FALSE); gfxSurface->setTexturePerspective(FALSE); gfxSurface->setConstantShade(1.0f); int textureHeight; gfxSurface->setTextureMap(texture); textureHeight = texture->height; TSCamera *camera = rc.getCamera(); TS::PointArray *pointArray = rc.getPointArray(); pointArray->reset(); pointArray->useIntensities(false); pointArray->useTextures(textCoord); pointArray->useTextures(true); pointArray->setVisibility( TS::ClipMask ); // find out how high the bitmap is at 100% as projected onto the viewport, // texel:pixel will be 1:1 at 640x480 //const RectF &worldVP = camera->getWorldViewport(); //const float h = textureHeight*((worldVP.upperL.y - worldVP.lowerR.y)/480.0f); //const float sz = 0.5*distance*(h/camera->getNearDist()); // find the position of the planet Point3F displacement = camera->getTCW().p; //displacement.z *= -(distance - visibleDistance)/visibleDistance; displacement.z = -displacement.z*(distance/(visibleDistance*1.5f)); Point3F pos = position; pos += displacement; // find the normal to the view plane in world coords Point3F v0(0.0f, 1.0f, 0.0f), vpNormal; m_mul(v0, (RMat3F)camera->getTCW(), &vpNormal); vpNormal.normalize(); // construct the plane that the camera, planet pos & celestial NP all // lie on PlaneF plane(pos, camera->getTCW().p, Point3F(displacement.x, displacement.y, displacement.z + distance)); // the cross product of the VP normal and the normal to the plane just // constructed is the up vector for the planet Point3F v; m_cross((Point3F)plane, vpNormal, &v); if (IsEqual(v.len(), 0.0f)) // planet is directly to the right or left of camera return; v.normalize(); // cross the up with the normal and we get the right vector Point3F u; m_cross(vpNormal, v, &u); u *= size; v *= size; TS::VertexIndexPair V[6]; Point3F ul = pos; ul -= u; ul += v; V[0].fVertexIndex = pointArray->addPoint(ul); V[0].fTextureIndex = 0; Point3F ur = pos; ur += u; ur += v; V[1].fVertexIndex = pointArray->addPoint(ur); V[1].fTextureIndex = 1; Point3F lr = pos; lr += u; lr -= v; V[2].fVertexIndex = pointArray->addPoint(lr); V[2].fTextureIndex = 2; Point3F ll = pos; ll -= u; ll -=v; V[3].fVertexIndex = pointArray->addPoint(ll); V[3].fTextureIndex = 3; if (gfxSurface->getCaps() & GFX_DEVCAP_SUPPORTS_CONST_ALPHA) gfxSurface->setZTest(GFX_NO_ZTEST); pointArray->drawPoly(4, V, 0); if (gfxSurface->getCaps() & GFX_DEVCAP_SUPPORTS_CONST_ALPHA) gfxSurface->setZTest(GFX_ZTEST_AND_WRITE); if(lensFlare) { TS::TransformedVertex vx; camera->transformProject(pos, &vx); bool vis = vx.fStatus & TS::TransformedVertex::Projected; lensFlare->setSunPos(vis, vx.fPoint, pos); } }
void SimPlanet::load() { unload(); if ((textureTag != 0) && (!manager->isServer())) { ResourceManager &rm = *SimResource::get(manager); const char *filename = SimTagDictionary::getString(manager, textureTag); // load the texture hTexture = rm.load(filename); AssertWarn((bool)hTexture, avar("Error reading bitmap file \"%s\"", filename)); // don't want to assert fatal because we don't want to bring down // the mission editor if ((bool)hTexture) { planet.texture = (GFXBitmap *)hTexture; addToSet(SimRenderSetId); inRenderSet = true; } } else planet.texture = NULL; // calculate planet position in world coordinates // add 90 to azimuth so that zero is at up Y axis if (incidence > 89.0f) incidence = 89.0f; if (incidence < -89.0f) incidence = -89.0f; const float az = azimuth + 90.0f; const float c = planet.distance*m_cos(DEGRAD*incidence); planet.position = Point3F(c*m_cos(DEGRAD*az), c*m_sin(DEGRAD*az), planet.distance*m_sin(DEGRAD*incidence)); // initialize light if any Point3F direction = planet.position; direction.normalize(); direction *= -1.0f; if (castShadows) { // set static data items shadowDirection = direction; shadows = true; } light.setAim(direction); //light.setType(TS::Light::LightDirectional); light.setType(TS::Light::LightDirectionalWrap); light.setIntensity(intensity.red, intensity.green, intensity.blue); if (intensity.red > 0.0f || intensity.green > 0.0f || intensity.blue > 0.0f || ambient.red > 0.0f || ambient.green > 0.0f || ambient.blue > 0.0f) { addToSet(SimLightSetId); inLightSet = true; lensFlare.setColor(intensity); } planet.lensFlare = useLensFlare ? &lensFlare : NULL; // initialize static texture coordinates textCoord[0].x = 0.0f; textCoord[0].y = 0.0f; textCoord[1].x = 1.0f; textCoord[1].y = 0.0f; textCoord[2].x = 1.0f; textCoord[2].y = 1.0f; textCoord[3].x = 0.0f; textCoord[3].y = 1.0f; setMaskBits(Modified); }
void LensFlare::render(TSRenderContext &rc) { if (!renderDetail) { renderCount = 0; return; } GFXSurface *gfxSurface = rc.getSurface(); if(!visible || !flares.size() || !(gfxSurface->getCaps() & GFX_DEVCAP_SUPPORTS_CONST_ALPHA)) { renderCount = 0; return; } renderCount++; if (obscured && renderCount%15 != 1) // if something was in the way last time we checked LOS, and it isn't time // to check LOS again, bail out early return; TS::PointArray *pointArray = rc.getPointArray(); pointArray->reset(); pointArray->useIntensities(false); pointArray->useTextures(textCoord); pointArray->useTextures(true); pointArray->setVisibility( TS::ClipMask ); pointArray->useHazes(false); gfxSurface->setHazeSource(GFX_HAZE_NONE); gfxSurface->setShadeSource(GFX_SHADE_NONE); gfxSurface->setTransparency(FALSE); gfxSurface->setTexturePerspective(FALSE); gfxSurface->setFillMode(GFX_FILL_TEXTURE); gfxSurface->setTransparency(TRUE); gfxSurface->setAlphaSource(GFX_ALPHA_TEXTURE); TS::VertexIndexPair V[4]; // if it was in the screen, go ahead and draw the lens flare. TSCamera *camera = rc.getCamera(); RectI const &screenVp = camera->getScreenViewport(); const Point2F vpSize(screenVp.lowerR.x - screenVp.upperL.x, screenVp.upperL.y + screenVp.lowerR.y); // preserve relative size of the textures, they should be // 1:1 on a 640x480 viewport Point2F vpScale(vpSize.x/640.0f, vpSize.y/480.0f); float scrSize = min(vpSize.x, vpSize.y); Point2F sunP(sunPosProjected.x, sunPosProjected.y); Point2F delta((screenVp.upperL.x + screenVp.lowerR.x) >> 1, (screenVp.upperL.y + screenVp.lowerR.y) >> 1); // find vector of the flare delta -= sunP; float deltaLen = delta.len(); float deltaFrac = 2.0f*deltaLen / scrSize; float constAlpha = .5f*(1.0f - deltaFrac); if (constAlpha <= 0.0f) { if (renderCount == 1) renderCount = 0; return; } if (root && renderCount%15 == 1) { // do a LOS query, see if anything is in the way SimCollisionInfo collision; SimContainerQuery query; query.id = 0; query.type = SimContainerQuery::DefaultDetail; query.mask = -1; Vector3F v = sunPosWorld - camera->getTCW().p; v.normalizef(); v *= 2.0f; query.box.fMin = camera->getTCW().p + v; query.box.fMax = sunPosWorld; if (root->findLOS(query, &collision, SimCollisionImageQuery::High)) { obscured = true; return; } } obscured = false; gfxSurface->setConstantAlpha(constAlpha); // generate rotated box info float angle; if (delta.x == 0.0f && delta.y == 0.0f) angle = 0.0f; else angle = m_atan(delta.x, delta.y) - M_PI/2.0f; float c = m_cos(angle), s = m_sin(angle); float a = c*(-1.0f) - s*(-1.0f), b = s*(-1.0f) + c*(-1.0f); Point2F rotPoints[4]; rotPoints[0].x = a; rotPoints[0].y = b; rotPoints[1].x = -b; rotPoints[1].y = a; rotPoints[2].x = -a; rotPoints[2].y = -b; rotPoints[3].x = b; rotPoints[3].y = -a; for (int i = 0; i < flares.size(); i++) { const FlareInfo &flare = flares[i]; Point2F flarePos = delta; flarePos *= flare.dist; flarePos += sunP; const GFXBitmap *bitmap = hMaterialList->getMaterial(flare.textureIndex).getTextureMap(); gfxSurface->setTextureMap(bitmap); Point2F dimension(bitmap->width*vpScale.x, bitmap->height*vpScale.y); const float scale = 0.5*(flare.minScale + (1.0f - deltaFrac)*flare.scaleRange); for (int j = 0; j < 4; j++) { Point3F drawPoint; if (flare.rotate) drawPoint.set(rotPoints[j].x*dimension.x, rotPoints[j].y*dimension.y, 0); else drawPoint.set(boxPoints[j][0]*dimension.x, boxPoints[j][1]*dimension.y, 0); drawPoint *= scale; drawPoint += Point3F(flarePos.x, flarePos.y, 1.0); V[j].fVertexIndex = pointArray->addProjectedPoint(drawPoint); V[j].fTextureIndex = j; } pointArray->drawProjectedPoly(4, V, 0); } gfxSurface->setTransparency(FALSE); if(deltaFrac < .3) { pointArray->useTextures(false); gfxSurface->setAlphaSource(GFX_ALPHA_CONSTANT); gfxSurface->setFillMode(GFX_FILL_CONSTANT); gfxSurface->setConstantAlpha(3.3 * (.3 - deltaFrac)); gfxSurface->setFillColor(&color); V[0].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.upperL.x, screenVp.upperL.y, 1.0)); V[1].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.lowerR.x, screenVp.upperL.y, 1.0)); V[2].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.lowerR.x, screenVp.lowerR.y, 1.0)); V[3].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.upperL.x, screenVp.lowerR.y, 1.0)); pointArray->drawProjectedPoly(4, V, 0); } gfxSurface->setAlphaSource(GFX_ALPHA_NONE); }
V[0].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.upperL.x, screenVp.upperL.y, 1.0)); V[1].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.lowerR.x, screenVp.upperL.y, 1.0)); V[2].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.lowerR.x, screenVp.lowerR.y, 1.0)); V[3].fVertexIndex = pointArray->addProjectedPoint(Point3F(screenVp.upperL.x, screenVp.lowerR.y, 1.0)); pointArray->drawProjectedPoly(4, V, 0); } gfxSurface->setAlphaSource(GFX_ALPHA_NONE); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- bool SimPlanet::shadows = false; Point3F SimPlanet::shadowDirection = Point3F(0.0f, 0.0f, 1.0f); SimPlanet::SimPlanet() { planet.itype = SimRenderImage::Background; planet.sortValue = PLANET_SORTVALUE; planet.lensFlare = NULL; planet.size = 2000.0f; planet.distance = PLANET_DISTANCE; textureTag = 0; azimuth = 0.0f; incidence = 30.0f; castShadows = false; useLensFlare = false; inLightSet = false; inRenderSet = false;
Box3F ForestConvex::getBoundingBox() const { // This is probably a bad idea? -- BJG return getBoundingBox( mTransform, Point3F(mScale,mScale,mScale) ); }
void LightFlareData::prepRender( SceneRenderState *state, LightFlareState *flareState ) { PROFILE_SCOPE( LightFlareData_prepRender ); const LightInfo *lightInfo = flareState->lightInfo; if ( mIsZero( flareState->fullBrightness ) || mIsZero( lightInfo->getBrightness() ) ) return; // Figure out the element count to render. U32 elementCount = mElementCount; const bool isReflectPass = state->isReflectPass(); if ( isReflectPass ) { // Then we don't render anything this pass. if ( !mRenderReflectPass ) return; // Find the zero distance elements which make // up the corona of the light flare. elementCount = 0.0f; for ( U32 i=0; i < mElementCount; i++ ) if ( mIsZero( mElementDist[i] ) ) elementCount++; } // Better have something to render. if ( elementCount == 0 ) return; U32 visDelta = U32_MAX; F32 occlusionFade = 1.0f; Point3F lightPosSS; bool lightVisible = _testVisibility( state, flareState, &visDelta, &occlusionFade, &lightPosSS ); // We can only skip rendering if the light is not // visible, and it has elapsed the fade out time. if ( mIsZero( occlusionFade ) || !lightVisible && visDelta > FadeOutTime ) return; const RectI &viewport = GFX->getViewport(); Point3F oneOverViewportExtent( 1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f ); // Really convert it to screen space. lightPosSS.x -= viewport.point.x; lightPosSS.y -= viewport.point.y; lightPosSS *= oneOverViewportExtent; lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One; lightPosSS.y = -lightPosSS.y; lightPosSS.z = 0.0f; // Take any projection offset into account so that the point where the flare's // elements converge is at the 'eye' point rather than the center of the viewport. const Point2F& projOffset = state->getCameraFrustum().getProjectionOffset(); Point3F flareVec( -lightPosSS + Point3F(projOffset.x, projOffset.y, 0.0f) ); const F32 flareLength = flareVec.len(); if ( flareLength > 0.0f ) flareVec *= 1.0f / flareLength; // Setup the flare quad points. Point3F rotatedBasePoints[4]; dMemcpy(rotatedBasePoints, sBasePoints, sizeof( sBasePoints )); // Rotate the flare quad. F32 rot = mAcos( -1.0f * flareVec.x ); rot *= flareVec.y > 0.0f ? -1.0f : 1.0f; MathUtils::vectorRotateZAxis( rot, rotatedBasePoints, 4 ); // Here we calculate a the light source's influence on // the effect's size and brightness. // Scale based on the current light brightness compared to its normal output. F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness; const Point3F &camPos = state->getCameraPosition(); const Point3F &lightPos = flareState->lightMat.getPosition(); const bool isVectorLight = lightInfo->getType() == LightInfo::Vector; // Scale based on world space distance from camera to light source. F32 distToCamera = ( camPos - lightPos ).len(); F32 lightSourceWSDistanceScale = isVectorLight && distToCamera > 0.0f ? 1.0f : getMin( 10.0f / distToCamera, 10.0f ); // Scale based on screen space distance from screen position of light source to the screen center. F32 lightSourceSSDistanceScale = getMax( ( 1.5f - flareLength ) / 1.5f, 0.0f ); // Scale based on recent visibility changes, fading in or out. F32 fadeInOutScale = 1.0f; if ( lightVisible && visDelta < FadeInTime && flareState->occlusion > 0.0f ) fadeInOutScale = (F32)visDelta / (F32)FadeInTime; else if ( !lightVisible && visDelta < FadeOutTime ) fadeInOutScale = 1.0f - (F32)visDelta / (F32)FadeOutTime; // This combined scale influences the size of all elements this effect renders. // Note we also add in a scale that is user specified in the Light. F32 lightSourceIntensityScale = lightSourceBrightnessScale * lightSourceWSDistanceScale * lightSourceSSDistanceScale * fadeInOutScale * flareState->scale * occlusionFade; if ( mIsZero( lightSourceIntensityScale ) ) return; // The baseColor which modulates the color of all elements. // // These are the factors which affect the "alpha" of the flare effect. // Modulate more in as appropriate. ColorF baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade; // Setup the vertex buffer for the maximum flare elements. const U32 vertCount = 4 * mElementCount; if ( flareState->vertBuffer.isNull() || flareState->vertBuffer->mNumVerts != vertCount ) flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic ); GFXVertexPCT *vert = flareState->vertBuffer.lock(); const Point2F oneOverTexSize( 1.0f / (F32)mFlareTexture.getWidth(), 1.0f / (F32)mFlareTexture.getHeight() ); for ( U32 i = 0; i < mElementCount; i++ ) { // Skip non-zero elements for reflections. if ( isReflectPass && mElementDist[i] > 0.0f ) continue; Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : sBasePoints; ColorF color( baseColor * mElementTint[i] ); if ( mElementUseLightColor[i] ) color *= lightInfo->getColor(); color.clamp(); Point3F pos( lightPosSS + flareVec * mElementDist[i] * flareLength ); const RectF &rect = mElementRect[i]; Point3F size( rect.extent.x, rect.extent.y, 1.0f ); size *= mElementScale[i] * mScale * lightSourceIntensityScale; AssertFatal( size.x >= 0.0f, "LightFlareData::prepRender - Got a negative element size?" ); if ( size.x < 100.0f ) { F32 alphaScale = mPow( size.x / 100.0f, 2 ); color *= alphaScale; } Point2F texCoordMin, texCoordMax; texCoordMin = rect.point * oneOverTexSize; texCoordMax = ( rect.point + rect.extent ) * oneOverTexSize; size.x = getMax( size.x, 1.0f ); size.y = getMax( size.y, 1.0f ); size *= oneOverViewportExtent; vert->color = color; vert->point = ( basePos[0] * size ) + pos; vert->texCoord.set( texCoordMin.x, texCoordMax.y ); vert++; vert->color = color; vert->point = ( basePos[1] * size ) + pos; vert->texCoord.set( texCoordMax.x, texCoordMax.y ); vert++; vert->color = color; vert->point = ( basePos[2] * size ) + pos; vert->texCoord.set( texCoordMax.x, texCoordMin.y ); vert++; vert->color = color; vert->point = ( basePos[3] * size ) + pos; vert->texCoord.set( texCoordMin.x, texCoordMin.y ); vert++; } flareState->vertBuffer.unlock(); RenderPassManager *rpm = state->getRenderPass(); // Create and submit the render instance. ParticleRenderInst *ri = rpm->allocInst<ParticleRenderInst>(); ri->type = RenderPassManager::RIT_Particle; ri->vertBuff = &flareState->vertBuffer; ri->primBuff = &mFlarePrimBuffer; ri->translucentSort = true; ri->sortDistSq = ( lightPos - camPos ).lenSquared(); ri->modelViewProj = &MatrixF::Identity; ri->bbModelViewProj = &MatrixF::Identity; ri->count = elementCount; ri->blendStyle = ParticleRenderInst::BlendGreyscale; ri->diffuseTex = mFlareTexture; ri->softnessDistance = 1.0f; ri->defaultKey = ri->diffuseTex ? (U32)ri->diffuseTex : (U32)ri->vertBuff; // Sort by texture too. // NOTE: Offscreen partical code is currently disabled. ri->systemState = PSS_AwaitingHighResDraw; rpm->addInst( ri ); }
/** * Sets the object the bot is targeting * * @param targetObject The object to target */ void AIPlayer::setAimObject( GameBase *targetObject ) { mAimObject = targetObject; mTargetInLOS = false; mAimOffset = Point3F(0.0f, 0.0f, 0.0f); }
void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) { AssertFatal( this == gClientSceneGraph, "SceneManager::_buildSceneGraph - Only the client scenegraph can support this call!" ); PROFILE_SCOPE( SceneGraph_batchRenderImages ); // In the editor, override the type mask for diffuse passes. if( gEditingMission && state->isDiffusePass() ) objectMask = EDITOR_RENDER_TYPEMASK; // Update the zoning state and traverse zones. if( getZoneManager() ) { // Update. getZoneManager()->updateZoningState(); // If zone culling isn't disabled, traverse the // zones now. if( !state->getCullingState().disableZoneCulling() ) { // Find the start zone if we haven't already. if( !baseObject ) { getZoneManager()->findZone( state->getCameraPosition(), baseObject, baseZone ); AssertFatal( baseObject != NULL, "SceneManager::_renderScene - findZone() did not return an object" ); } // Traverse zones starting in base object. SceneTraversalState traversalState( &state->getCullingState() ); PROFILE_START( Scene_traverseZones ); baseObject->traverseZones( &traversalState, baseZone ); PROFILE_END(); // Set the scene render box to the area we have traversed. state->setRenderArea( traversalState.getTraversedArea() ); } } // Set the query box for the container query. Never // make it larger than the frustum's AABB. In the editor, // always query the full frustum as that gives objects // the opportunity to render editor visualizations even if // they are otherwise not in view. if( !state->getFrustum().getBounds().isOverlapped( state->getRenderArea() ) ) { // This handles fringe cases like flying backwards into a zone where you // end up pretty much standing on a zone border and looking directly into // its "walls". In that case the traversal area will be behind the frustum // (remember that the camera isn't where visibility starts, it's the near // distance). return; } Box3F queryBox = state->getFrustum().getBounds(); if( !gEditingMission ) { queryBox.minExtents.setMax( state->getRenderArea().minExtents ); queryBox.maxExtents.setMin( state->getRenderArea().maxExtents ); } PROFILE_START( Scene_cullObjects ); //TODO: We should split the codepaths here based on whether the outdoor zone has visible space. // If it has, we should use the container query-based path. // If it hasn't, we should fill the object list directly from the zone lists which will usually // include way fewer objects. // Gather all objects that intersect the scene render box. mBatchQueryList.clear(); getContainer()->findObjectList( queryBox, objectMask, &mBatchQueryList ); // Cull the list. U32 numRenderObjects = state->getCullingState().cullObjects( mBatchQueryList.address(), mBatchQueryList.size(), !state->isDiffusePass() ? SceneCullingState::CullEditorOverrides : 0 // Keep forced editor stuff out of non-diffuse passes. ); //HACK: If the control object is a Player and it is not in the render list, force // it into it. This really should be solved by collision bounds being separate from // object bounds; only because the Player class is using bounds not encompassing // the actual player object is it that we have this problem in the first place. // Note that we are forcing the player object into ALL passes here but such // is the power of proliferation of things done wrong. GameConnection* connection = GameConnection::getConnectionToServer(); if( connection ) { Player* player = dynamic_cast< Player* >( connection->getControlObject() ); if( player ) { mBatchQueryList.setSize( numRenderObjects ); if( !mBatchQueryList.contains( player ) ) { mBatchQueryList.push_back( player ); numRenderObjects ++; } } } PROFILE_END(); // Render the remaining objects. PROFILE_START( Scene_renderObjects ); state->renderObjects( mBatchQueryList.address(), numRenderObjects ); PROFILE_END(); // Render bounding boxes, if enabled. if( smRenderBoundingBoxes && state->isDiffusePass() ) { GFXDEBUGEVENT_SCOPE( Scene_renderBoundingBoxes, ColorI::WHITE ); GameBase* cameraObject = 0; if( connection ) cameraObject = connection->getCameraObject(); GFXStateBlockDesc desc; desc.setFillModeWireframe(); desc.setZReadWrite( true, false ); for( U32 i = 0; i < numRenderObjects; ++ i ) { SceneObject* object = mBatchQueryList[ i ]; // Skip global bounds object. if( object->isGlobalBounds() ) continue; // Skip camera object as we're viewing the scene from it. if( object == cameraObject ) continue; const Box3F& worldBox = object->getWorldBox(); GFX->getDrawUtil()->drawObjectBox( desc, Point3F( worldBox.len_x(), worldBox.len_y(), worldBox.len_z() ), worldBox.getCenter(), MatrixF::Identity, ColorI::WHITE ); } } }
/** * This method calculates the moves for the AI player * * @param movePtr Pointer to move the move list into */ bool AIPlayer::getAIMove(Move *movePtr) { *movePtr = NullMove; // Use the eye as the current position. MatrixF eye; getEyeTransform(&eye); Point3F location = eye.getPosition(); Point3F rotation = getRotation(); // Orient towards the aim point, aim object, or towards // our destination. if (mAimObject || mAimLocationSet || mMoveState != ModeStop) { // Update the aim position if we're aiming for an object if (mAimObject) mAimLocation = mAimObject->getPosition() + mAimOffset; else if (!mAimLocationSet) mAimLocation = mMoveDestination; F32 xDiff = mAimLocation.x - location.x; F32 yDiff = mAimLocation.y - location.y; if (!mIsZero(xDiff) || !mIsZero(yDiff)) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI_F) curYaw -= M_2PI_F; while (curYaw < -M_2PI_F) curYaw += M_2PI_F; // find the yaw offset F32 newYaw = mAtan2( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_PI_F ) yawDiff += M_2PI_F; movePtr->yaw = yawDiff; // Next do pitch. if (!mAimObject && !mAimLocationSet) { // Level out if were just looking at our next way point. Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } else { // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = mAimLocation.z - location.z; F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); if (mFabs(newPitch) > 0.01f) { Point3F headRotation = getHeadRotation(); movePtr->pitch = newPitch - headRotation.x; } } } } else { // Level out if we're not doing anything else Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } // Move towards the destination if (mMoveState != ModeStop) { F32 xDiff = mMoveDestination.x - location.x; F32 yDiff = mMoveDestination.y - location.y; // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) { mMoveState = ModeStop; throwCallback("onReachDestination"); } else { // Build move direction in world space if (mIsZero(xDiff)) movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); movePtr->y = (location.y > mMoveDestination.y) ? -value : value; movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); movePtr->x = (location.x > mMoveDestination.x) ? -value : value; movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; MatrixF moveMatrix; moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); movePtr->x = newMove.x; movePtr->y = newMove.y; // Set movement speed. We'll slow down once we get close // to try and stop on the spot... if (mMoveSlowdown) { F32 speed = mMoveSpeed; F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); F32 maxDist = 5.0f; if (dist < maxDist) speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; mMoveState = ModeSlowing; } else { movePtr->x *= mMoveSpeed; movePtr->y *= mMoveSpeed; mMoveState = ModeMove; } if (mMoveStuckTestCountdown > 0) --mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (location - mLastLocation).len(); if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if ( mMoveState != ModeSlowing || locationDelta == 0 ) { mMoveState = ModeStuck; throwCallback("onMoveStuck"); } } } } } // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. if (mAimObject) { MatrixF eyeMat; getEyeTransform(&eyeMat); eyeMat.getColumn(3,&location); Point3F targetLoc = mAimObject->getBoxCenter(); // This ray ignores non-static shapes. Cast Ray returns true // if it hit something. RayInfo dummy; if (getContainer()->castRay( location, targetLoc, StaticShapeObjectType | StaticObjectType | TerrainObjectType, &dummy)) { if (mTargetInLOS) { throwCallback( "onTargetExitLOS" ); mTargetInLOS = false; } } else if (!mTargetInLOS) { throwCallback( "onTargetEnterLOS" ); mTargetInLOS = true; } } // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( int i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[i] = getImageTriggerState(i); mLastLocation = location; return true; }
void TerrainBlock::_updateBaseTexture(bool writeToCache) { if ( !mBaseShader && !_initBaseShader() ) return; // This can sometimes occur outside a begin/end scene. const bool sceneBegun = GFX->canCurrentlyRender(); if ( !sceneBegun ) GFX->beginScene(); GFXDEBUGEVENT_SCOPE( TerrainBlock_UpdateBaseTexture, ColorI::GREEN ); PROFILE_SCOPE( TerrainBlock_UpdateBaseTexture ); GFXTransformSaver saver; const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 ); U32 baseTexSize = getNextPow2( mBaseTexSize ); baseTexSize = getMin( maxTextureSize, baseTexSize ); Point2I destSize( baseTexSize, baseTexSize ); // Setup geometry GFXVertexBufferHandle<GFXVertexPT> vb; { F32 copyOffsetX = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.x; F32 copyOffsetY = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.y; GFXVertexPT points[4]; points[0].point = Point3F(1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0); points[0].texCoord = Point2F(1.0, 1.0f); points[1].point = Point3F(1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0); points[1].texCoord = Point2F(1.0, 0.0f); points[2].point = Point3F(-1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0); points[2].texCoord = Point2F(0.0, 1.0f); points[3].point = Point3F(-1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0); points[3].texCoord = Point2F(0.0, 0.0f); vb.set( GFX, 4, GFXBufferTypeVolatile ); GFXVertexPT *ptr = vb.lock(); if(ptr) { dMemcpy( ptr, points, sizeof(GFXVertexPT) * 4 ); vb.unlock(); } } GFXTexHandle blendTex; // If the base texture is already a valid render target then // use it to render to else we create one. if ( mBaseTex.isValid() && mBaseTex->isRenderTarget() && mBaseTex->getFormat() == GFXFormatR8G8B8A8 && mBaseTex->getWidth() == destSize.x && mBaseTex->getHeight() == destSize.y ) blendTex = mBaseTex; else blendTex.set( destSize.x, destSize.y, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, "" ); GFX->pushActiveRenderTarget(); // Set our shader stuff GFX->setShader( mBaseShader ); GFX->setShaderConstBuffer( mBaseShaderConsts ); GFX->setStateBlock( mBaseShaderSB ); GFX->setVertexBuffer( vb ); mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex ); GFX->setActiveRenderTarget( mBaseTarget ); GFX->clear( GFXClearTarget, ColorI(0,0,0,255), 1.0f, 0 ); GFX->setTexture( 0, mLayerTex ); mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() ); for ( U32 i=0; i < mBaseTextures.size(); i++ ) { GFXTextureObject *tex = mBaseTextures[i]; if ( !tex ) continue; GFX->setTexture( 1, tex ); F32 baseSize = mFile->mMaterials[i]->getDiffuseSize(); F32 scale = 1.0f; if ( !mIsZero( baseSize ) ) scale = getWorldBlockSize() / baseSize; // A mistake early in development means that texture // coords are not flipped correctly. To compensate // we flip the y scale here. mBaseShaderConsts->setSafe( mBaseTexScaleConst, Point2F( scale, -scale ) ); mBaseShaderConsts->setSafe( mBaseTexIdConst, (F32)i ); GFX->drawPrimitive( GFXTriangleStrip, 0, 2 ); } mBaseTarget->resolve(); GFX->setShader( NULL ); //GFX->setStateBlock( NULL ); // WHY NOT? GFX->setShaderConstBuffer( NULL ); GFX->setVertexBuffer( NULL ); GFX->popActiveRenderTarget(); // End it if we begun it... Yeehaw! if ( !sceneBegun ) GFX->endScene(); /// Do we cache this sucker? if (mBaseTexFormat == NONE || !writeToCache) { // We didn't cache the result, so set the base texture // to the render target we updated. This should be good // for realtime painting cases. mBaseTex = blendTex; } else if (mBaseTexFormat == DDS) { String cachePath = _getBaseTexCacheFileName(); FileStream fs; if ( fs.open( _getBaseTexCacheFileName(), Torque::FS::File::Write ) ) { // Read back the render target, dxt compress it, and write it to disk. GBitmap blendBmp( destSize.x, destSize.y, false, GFXFormatR8G8B8A8 ); blendTex.copyToBmp( &blendBmp ); /* // Test code for dumping uncompressed bitmap to disk. { FileStream fs; if ( fs.open( "./basetex.png", Torque::FS::File::Write ) ) { blendBmp.writeBitmap( "png", fs ); fs.close(); } } */ blendBmp.extrudeMipLevels(); DDSFile *blendDDS = DDSFile::createDDSFileFromGBitmap( &blendBmp ); DDSUtil::squishDDS( blendDDS, GFXFormatDXT1 ); // Write result to file stream blendDDS->write( fs ); delete blendDDS; } fs.close(); } else { FileStream stream; if (!stream.open(_getBaseTexCacheFileName(), Torque::FS::File::Write)) { mBaseTex = blendTex; return; } GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8); blendTex->copyToBmp(&bitmap); bitmap.writeBitmap(formatToExtension(mBaseTexFormat), stream); } }
void ScatterSky::_renderMoon( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) { if ( !mMoonMatInst ) return; Point3F moonlightPosition = state->getCameraPosition() - /*mLight->getDirection()*/ mMoonLightDir * state->getFarPlane() * 0.9f; F32 dist = (moonlightPosition - state->getCameraPosition()).len(); // worldRadius = screenRadius * dist / worldToScreen // screenRadius = worldRadius / dist * worldToScreen // F32 screenRadius = GFX->getViewport().extent.y * mMoonScale * 0.5f; F32 worldRadius = screenRadius * dist / state->getWorldToScreenScale().y; // Calculate Billboard Radius (in world units) to be constant, independent of distance. // Takes into account distance, viewport size, and specified size in editor F32 BBRadius = worldRadius; mMatrixSet->restoreSceneViewProjection(); if ( state->isReflectPass() ) mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() ); mMatrixSet->setWorld( MatrixF::Identity ); // Initialize points with basic info Point3F points[4]; points[0] = Point3F(-BBRadius, 0.0, -BBRadius); points[1] = Point3F( -BBRadius, 0.0, BBRadius); points[2] = Point3F( BBRadius, 0.0, BBRadius); points[3] = Point3F( BBRadius, 0.0, -BBRadius); static const Point2F sCoords[4] = { Point2F( 0.0f, 0.0f ), Point2F( 0.0f, 1.0f ), Point2F( 1.0f, 1.0f ), Point2F( 1.0f, 0.0f ) }; // Get info we need to adjust points const MatrixF &camView = state->getCameraTransform(); // Finalize points for(int i = 0; i < 4; i++) { // align with camera camView.mulV(points[i]); // offset points[i] += moonlightPosition; } // Vertex color. ColorF moonVertColor( 1.0f, 1.0f, 1.0f, mNightInterpolant ); // Copy points to buffer. GFXVertexBufferHandle< GFXVertexPCT > vb; vb.set( GFX, 4, GFXBufferTypeVolatile ); GFXVertexPCT *pVert = vb.lock(); for ( S32 i = 0; i < 4; i++ ) { pVert->color.set( moonVertColor ); pVert->point.set( points[i] ); pVert->texCoord.set( sCoords[i].x, sCoords[i].y ); pVert++; } vb.unlock(); // Setup SceneData struct. SceneData sgData; sgData.wireframe = GFXDevice::getWireframe(); sgData.visibility = 1.0f; // Draw it while ( mMoonMatInst->setupPass( state, sgData ) ) { mMoonMatInst->setTransforms( *mMatrixSet, state ); mMoonMatInst->setSceneInfo( state, sgData ); GFX->setVertexBuffer( vb ); GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); } }
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; }
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 ForestWind::processTick() { PROFILE_SCOPE( ForestWind_ProcessTick ); const F32 deltaTime = 0.032f; const U32 simTime = Sim::getCurrentTime(); Point2F finalVec( 0, 0 ); Point2F windDir( mParent->mWindDirection.x, mParent->mWindDirection.y ); if ( mLastGustTime < simTime ) { Point2F turbVec( 0, 0 ); if ( mLastGustTime < simTime + (mParent->mWindTurbulenceFrequency * 1000.0f) ) turbVec = (mRandom.randF() * mParent->mWindTurbulenceStrength) * windDir; mLastGustTime = simTime + (mParent->mWindGustFrequency * 1000.0f); Point2F gustVec = (mRandom.randF() * mParent->mWindGustStrength + mRandom.randF() * mParent->mWindGustWobbleStrength) * windDir; finalVec += gustVec + turbVec; //finalVec.normalizeSafe(); } //bool rotationChange = false; if ( mLastYawTime < simTime ) { mLastYawTime = simTime + (mParent->mWindGustYawFrequency * 1000.0f); F32 rotateAmt = mRandom.randF() * mParent->mWindGustYawAngle + mRandom.randF() * mParent->mWindGustWobbleStrength; if ( mRandom.randF() <= 0.5f ) rotateAmt = -rotateAmt; rotateAmt = mDegToRad( rotateAmt ); if ( rotateAmt > M_2PI_F ) rotateAmt -= M_2PI_F; else if ( rotateAmt < -M_2PI_F ) rotateAmt += M_2PI_F; mTargetYawAngle = rotateAmt; //finalVec.rotate( rotateAmt ); mCurrentTarget.rotate( rotateAmt ); } //mCurrentTarget.normalizeSafe(); if ( mCurrentTarget.isZero() || mCurrentInterp >= 1.0f ) { mCurrentInterp = 0; mCurrentTarget.set( 0, 0 ); Point2F windDir( mDirection.x, mDirection.y ); windDir.normalizeSafe(); mCurrentTarget = finalVec + windDir; } else { mCurrentInterp += deltaTime; mCurrentInterp = mClampF( mCurrentInterp, 0.0f, 1.0f ); mDirection.interpolate( mDirection, Point3F( mCurrentTarget.x, mCurrentTarget.y, 0 ), mCurrentInterp ); //F32 rotateAmt = mLerp( 0, mTargetYawAngle, mCurrentInterp ); //mTargetYawAngle -= rotateAmt; //Point2F dir( mDirection.x, mDirection.y ); //if ( mTargetYawAngle > 0.0f ) // dir.rotate( rotateAmt ); //mDirection.set( dir.x, dir.y, 0 ); } }
" position = %position;\n" " dataBlock = %effectDataBlock;\n" " };\n" " }\n" "}\n\n" "// schedule an explosion\n" "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\n" "@endtsexample" ); #define MaxLightRadius 20 MRandomLCG sgRandom(0xdeadbeef); DefineEngineFunction(calcExplosionCoverage, F32, (Point3F pos, S32 id, U32 covMask),(Point3F(0.0f,0.0f,0.0f), NULL, NULL), "@brief Calculates how much an explosion effects a specific object.\n\n" "Use this to determine how much damage to apply to objects based on their " "distance from the explosion's center point, and whether the explosion is " "blocked by other objects.\n\n" "@param pos Center position of the explosion.\n" "@param id Id of the object of which to check coverage.\n" "@param covMask Mask of object types that may block the explosion.\n" "@return Coverage value from 0 (not affected by the explosion) to 1 (fully affected)\n\n" "@tsexample\n" "// Get the position of the explosion.\n" "%position = %explosion.getPosition();\n\n" "// Set a list of TypeMasks (defined in gameFunctioncs.cpp), seperated by the | character.\n" "%TypeMasks = $TypeMasks::StaticObjectType | $TypeMasks::ItemObjectType\n\n" "// Acquire the damage value from 0.0f - 1.0f.\n" "%coverage = calcExplosionCoverage( %position, %sceneObject, %TypeMasks );\n\n"
void OcclusionVolume::buildSilhouette( const SceneCameraState& cameraState, Vector< Point3F >& outPoints ) { // Extract the silhouette of the polyhedron. This works differently // depending on whether we project orthogonally or in perspective. TempAlloc< U32 > indices( mPolyhedron.getNumPoints() ); U32 numPoints; if( cameraState.getFrustum().isOrtho() ) { // Transform the view direction into object space. Point3F osViewDir; getWorldTransform().mulV( cameraState.getViewDirection(), &osViewDir ); // And extract the silhouette. SilhouetteExtractorOrtho< PolyhedronType > extractor( mPolyhedron ); numPoints = extractor.extractSilhouette( osViewDir, indices, indices.size ); } else { // Create a transform to go from view space to object space. MatrixF camView( true ); camView.scale( Point3F( 1.0f / getScale().x, 1.0f / getScale().y, 1.0f / getScale().z ) ); camView.mul( getRenderWorldTransform() ); camView.mul( cameraState.getViewWorldMatrix() ); // Do a perspective-correct silhouette extraction. numPoints = mSilhouetteExtractor.extractSilhouette( camView, indices, indices.size ); } // If we haven't yet, transform the polyhedron's points // to world space. if( mTransformDirty ) { const U32 numPoints = mPolyhedron.getNumPoints(); const PolyhedronType::PointType* points = getPolyhedron().getPoints(); mWSPoints.setSize( numPoints ); for( U32 i = 0; i < numPoints; ++ i ) { Point3F p = points[ i ]; p.convolve( getScale() ); getTransform().mulP( p, &mWSPoints[ i ] ); } mTransformDirty = false; } // Now store the points. outPoints.setSize( numPoints ); for( U32 i = 0; i < numPoints; ++ i ) outPoints[ i ] = mWSPoints[ indices[ i ] ]; }
void TransformTool::renderTool() { if (!mActive) return; // Grid { Point3F editorPos = mEditorManager->mEditorCamera.getWorldPosition(); editorPos = editorPos / 10.0f; editorPos.x = mFloor(editorPos.x); editorPos.y = mFloor(editorPos.y); editorPos = editorPos * 10.0f; Torque::Debug.ddPush(); Torque::Debug.ddSetState(true, false, true); Torque::Debug.ddSetWireframe(true); Torque::Debug.ddSetColor(BGFXCOLOR_RGBA(255, 255, 255, 255)); F32 gridNormal[3] = { 0.0f, 0.0f, 1.0f }; F32 gridPos[3] = { editorPos.x, editorPos.y, -0.01f }; Torque::Debug.ddDrawGrid(gridNormal, gridPos, 100, 10.0f); } // Draw Light Icons /*if (mLightIcon != NULL) { Vector<Lighting::LightData*> lightList = Torque::Lighting.getLightList(); for (S32 n = 0; n < lightList.size(); ++n) { Lighting::LightData* light = lightList[n]; Torque::Graphics.drawBillboard(mEditorManager->mRenderLayer4View->id, mLightIcon, light->position, 1.0f, 1.0f, ColorI(light->color[0] * 255, light->color[1] * 255, light->color[2] * 255, 255), NULL); } }*/ if (mMultiselect) { Box3F multiSelectBox; multiSelectBox.set(Point3F(0, 0, 0)); for (S32 n = 0; n < mSelectedObjects.size(); ++n) { Scene::SceneObject* obj = dynamic_cast<Scene::SceneObject*>(mSelectedObjects[n]); if (obj) { if (n == 0) multiSelectBox = obj->mBoundingBox; else multiSelectBox.intersect(obj->mBoundingBox); } Scene::BaseComponent* component = dynamic_cast<Scene::BaseComponent*>(mSelectedObjects[n]); if (component) { Box3F boundingBox = component->getBoundingBox(); boundingBox.transform(component->mTransform.matrix); if (n == 0) multiSelectBox = boundingBox; else multiSelectBox.intersect(boundingBox); } } mSelectionBounds = true; mSelectionBoundsStart = multiSelectBox.minExtents; mSelectionBoundsEnd = multiSelectBox.maxExtents; } // Object Selected if (mSelectedObject != NULL && mSelectedComponent == NULL) { mSelectionBounds = true; mSelectionBoundsStart = mSelectedObject->mBoundingBox.minExtents; mSelectionBoundsEnd = mSelectedObject->mBoundingBox.maxExtents; } // Component Selected if (mSelectedObject != NULL && mSelectedComponent != NULL) { Box3F boundingBox = mSelectedComponent->getBoundingBox(); boundingBox.transform(mSelectedObject->mTransform.matrix); mSelectionBounds = true; mSelectionBoundsStart = boundingBox.minExtents; mSelectionBoundsEnd = boundingBox.maxExtents; } // Selection Bounding Box if (mSelectionBounds) { Aabb debugBox; debugBox.m_min[0] = mSelectionBoundsStart.x; debugBox.m_min[1] = mSelectionBoundsStart.y; debugBox.m_min[2] = mSelectionBoundsStart.z; debugBox.m_max[0] = mSelectionBoundsEnd.x; debugBox.m_max[1] = mSelectionBoundsEnd.y; debugBox.m_max[2] = mSelectionBoundsEnd.z; Torque::Debug.ddSetWireframe(true); Torque::Debug.ddSetColor(BGFXCOLOR_RGBA(mSelectionBoundsColor.red, mSelectionBoundsColor.green, mSelectionBoundsColor.blue, mSelectionBoundsColor.alpha)); Torque::Debug.ddDrawAabb(debugBox); Torque::Debug.ddPop(); } // Gizmo mGizmo.render(); // Debug if (mDebugWorldRay) { Torque::Debug.ddMoveTo(mLastRayStart.x, mLastRayStart.y, mLastRayStart.z); Torque::Debug.ddLineTo(mLastRayEnd.x, mLastRayEnd.y, mLastRayEnd.z); } if (mDebugBoxSelection) { for (U32 i = 0; i < 4; ++i) { Torque::Debug.ddMoveTo(mBoxNearPoint.x, mBoxNearPoint.y, mBoxNearPoint.z); Torque::Debug.ddLineTo(mBoxFarPoint[i].x, mBoxFarPoint[i].y, mBoxFarPoint[i].z); } } }
bool SceneObject::collideBox(const Point3F &start, const Point3F &end, RayInfo *info) { const F32 * pStart = (const F32*)start; const F32 * pEnd = (const F32*)end; const F32 * pMin = (const F32*)mObjBox.minExtents; const F32 * pMax = (const F32*)mObjBox.maxExtents; F32 maxStartTime = -1; F32 minEndTime = 1; F32 startTime; F32 endTime; // used for getting normal U32 hitIndex = 0xFFFFFFFF; U32 side; // walk the axis for(U32 i = 0; i < 3; i++) { // if(pStart[i] < pEnd[i]) { if(pEnd[i] < pMin[i] || pStart[i] > pMax[i]) return(false); F32 dist = pEnd[i] - pStart[i]; startTime = (pStart[i] < pMin[i]) ? (pMin[i] - pStart[i]) / dist : -1; endTime = (pEnd[i] > pMax[i]) ? (pMax[i] - pStart[i]) / dist : 1; side = 1; } else { if(pStart[i] < pMin[i] || pEnd[i] > pMax[i]) return(false); F32 dist = pStart[i] - pEnd[i]; startTime = (pStart[i] > pMax[i]) ? (pStart[i] - pMax[i]) / dist : -1; endTime = (pEnd[i] < pMin[i]) ? (pStart[i] - pMin[i]) / dist : 1; side = 0; } // if(startTime > maxStartTime) { maxStartTime = startTime; hitIndex = i * 2 + side; } if(endTime < minEndTime) minEndTime = endTime; if(minEndTime < maxStartTime) return(false); } // fail if inside if(maxStartTime < 0.f) return(false); // static Point3F boxNormals[] = { Point3F( 1, 0, 0), Point3F(-1, 0, 0), Point3F( 0, 1, 0), Point3F( 0,-1, 0), Point3F( 0, 0, 1), Point3F( 0, 0,-1), }; // AssertFatal(hitIndex != 0xFFFFFFFF, "SceneObject::collideBox"); info->t = maxStartTime; info->object = this; mObjToWorld.mulV(boxNormals[hitIndex], &info->normal); info->material = 0; return(true); }
void WorldEditorSelection::rotate(const EulerF & rot, const Point3F & center) { // single selections will rotate around own axis, multiple about world if(size() == 1) { SceneObject* object = dynamic_cast< SceneObject* >( at( 0 ) ); if( object ) { MatrixF mat = object->getTransform(); Point3F pos; mat.getColumn(3, &pos); // get offset in obj space Point3F offset = pos - center; MatrixF wMat = object->getWorldTransform(); wMat.mulV(offset); // MatrixF transform(EulerF(0,0,0), -offset); transform.mul(MatrixF(rot)); transform.mul(MatrixF(EulerF(0,0,0), offset)); mat.mul(transform); object->setTransform(mat); } } else { for( iterator iter = begin(); iter != end(); ++ iter ) { SceneObject* object = dynamic_cast< SceneObject* >( *iter ); if( !object ) continue; MatrixF mat = object->getTransform(); Point3F pos; mat.getColumn(3, &pos); // get offset in obj space Point3F offset = pos - center; MatrixF transform(rot); Point3F wOffset; transform.mulV(offset, &wOffset); MatrixF wMat = object->getWorldTransform(); wMat.mulV(offset); // transform.set(EulerF(0,0,0), -offset); mat.setColumn(3, Point3F(0,0,0)); wMat.setColumn(3, Point3F(0,0,0)); transform.mul(wMat); transform.mul(MatrixF(rot)); transform.mul(mat); mat.mul(transform); mat.normalize(); mat.setColumn(3, wOffset + center); object->setTransform(mat); } } mCentroidValid = false; }
void LightFlareState::clear() { visChangedTime = 0; visible = false; scale = 1.0f; fullBrightness = 1.0f; lightMat = MatrixF::Identity; lightInfo = NULL; worldRadius = -1.0f; occlusion = -1.0f; } Point3F LightFlareData::sBasePoints[] = { Point3F( -0.5, 0.5, 0.0 ), Point3F( -0.5, -0.5, 0.0 ), Point3F( 0.5, -0.5, 0.0 ), Point3F( 0.5, 0.5, 0.0 ) }; IMPLEMENT_CO_DATABLOCK_V1( LightFlareData ); ConsoleDocClass( LightFlareData, "@brief Defines a light flare effect usable by scene lights.\n\n" "%LightFlareData is a datablock which defines a type of flare effect. " "This may then be referenced by other classes which support the rendering " "of a flare: Sun, ScatterSky, LightBase.\n\n"