void Projectile::interpolateTick(F32 delta) { Parent::interpolateTick(delta); if( mHasExploded ) return; Point3F interpPos = mCurrDeltaBase + mCurrBackDelta * delta; Point3F dir = mCurrVelocity; if(dir.isZero()) dir.set(0,0,1); else dir.normalize(); MatrixF xform(true); xform = MathUtils::createOrientFromDir(dir); xform.setPosition(interpPos); setRenderTransform(xform); // fade out the projectile image S32 time = (S32)(mCurrTick - delta); if(time > mDataBlock->fadeDelay) { F32 fade = F32(time - mDataBlock->fadeDelay); mFadeValue = 1.0 - (fade / F32(mDataBlock->lifetime)); } else mFadeValue = 1.0; updateSound(); }
void Etherform::updateCameraPos(F32 delta) { // // Update 3rd person camera position. // F32 min,max; Point3F offset; MatrixF rot; this->getCameraParameters(&min,&max,&offset,&rot); Point3F vec = mCameraTargetPos - mCameraPos; F32 dist = vec.len(); if(dist == 0) { // Catch camera position up to its target position. mCameraPos = mCameraTargetPos; } else if(dist > max) { // Catch camera up to max allowed dist from target position. vec.normalize(); vec.neg(); mCameraPos = mCameraTargetPos + vec * max; } else { // Move camera pos towards its target pos. #if 0 F32 speed = mDataBlock->accelerationForce; speed *= 1 - (1/vec.lenSquared()); vec.normalize(); mCameraPos += vec * speed * delta; #else //F32 speedScale = this->getVelocity().len() / mDataBlock->accelerationForce; F32 speedScale = 4; //mDataBlock->accelerationForce / 2; F32 distScale = 1 - (1/vec.lenSquared()); vec *= speedScale * distScale * delta; if(vec.len() > dist) mCameraPos = mCameraTargetPos; else mCameraPos += vec; #endif } }
void PathCamera::calculateLookDirMat(Point3F dir) { mLookDirMat.identity(); if(dir == Point3F(0.f, 0.f, 0.f)) { mUseLookDirMatrix = false; return; } Point3F left = mCross(dir, Point3F(0.f, 0.f, 1.f)); left.normalize(); Point3F up = mCross(left, dir); up.normalize(); mLookDirMat.setColumn(0, Point4F(left.x, left.y, left.z, 0.f)); mLookDirMat.setColumn(1, Point4F(dir.x, dir.y, dir.z, 0.f)); mLookDirMat.setColumn(2, Point4F(up.x, up.y, up.z, 0.f)); mUseLookDirMatrix = true; }
MatrixF PlaneReflector::getFrustumClipProj( MatrixF &modelview ) { static MatrixF rotMat(EulerF( static_cast<F32>(M_PI / 2.f), 0.0, 0.0)); static MatrixF invRotMat(EulerF( -static_cast<F32>(M_PI / 2.f), 0.0, 0.0)); MatrixF revModelview = modelview; revModelview = rotMat * revModelview; // add rotation to modelview because it needs to be removed from projection // rotate clip plane into modelview space Point4F clipPlane; Point3F pnt = refplane * -(refplane.d + 0.0 ); Point3F norm = refplane; revModelview.mulP( pnt ); revModelview.mulV( norm ); norm.normalize(); clipPlane.set( norm.x, norm.y, norm.z, -mDot( pnt, norm ) ); // Manipulate projection matrix //------------------------------------------------------------------------ MatrixF proj = GFX->getProjectionMatrix(); proj.mul( invRotMat ); // reverse rotation imposed by Torque proj.transpose(); // switch to row-major order // Calculate the clip-space corner point opposite the clipping plane // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and // transform it into camera space by multiplying it // by the inverse of the projection matrix Vector4F q; q.x = sgn(clipPlane.x) / proj(0,0); q.y = sgn(clipPlane.y) / proj(1,1); q.z = -1.0F; q.w = ( 1.0F - proj(2,2) ) / proj(3,2); F32 a = 1.0 / (clipPlane.x * q.x + clipPlane.y * q.y + clipPlane.z * q.z + clipPlane.w * q.w); Vector4F c = clipPlane * a; // CodeReview [ags 1/23/08] Come up with a better way to deal with this. if(GFX->getAdapterType() == OpenGL) c.z += 1.0f; // Replace the third column of the projection matrix proj.setColumn( 2, c ); proj.transpose(); // convert back to column major order proj.mul( rotMat ); // restore Torque rotation return proj; }
//.logicking >> void PathCamera::setLookDir(Point3F dir) { if(dir == Point3F(0.f, 0.f, 0.f)) { mLookDirVector = dir; setMaskBits(LookDirVectorMask); } else { dir.normalize(); mLookDirVector = dir; calculateLookDirMat(mLookDirVector); setMaskBits(LookDirVectorMask); } }
//---------------------------------------------------------------------------- // Update ring //---------------------------------------------------------------------------- void Splash::updateRing( SplashRing& ring, F32 dt ) { for( U32 i=0; i<ring.points.size(); i++ ) { if( mDead ) { Point3F vel = ring.points[i].velocity; vel.normalize(); vel *= mDataBlock->acceleration; ring.points[i].velocity += vel * dt; } ring.points[i].velocity += Point3F( 0.0f, 0.0f, -9.8f ) * dt; ring.points[i].position += ring.points[i].velocity * dt; } }
//---------------------------------------------------------------------------- // Create ring //---------------------------------------------------------------------------- SplashRing Splash::createRing() { SplashRing ring; U32 numPoints = mDataBlock->numSegments + 1; Point3F ejectionAxis( 0.0, 0.0, 1.0 ); Point3F axisx; if (mFabs(ejectionAxis.z) < 0.999f) mCross(ejectionAxis, Point3F(0, 0, 1), &axisx); else mCross(ejectionAxis, Point3F(0, 1, 0), &axisx); axisx.normalize(); for( U32 i=0; i<numPoints; i++ ) { F32 t = F32(i) / F32(numPoints); AngAxisF thetaRot( axisx, mDataBlock->ejectionAngle * (M_PI / 180.0)); AngAxisF phiRot( ejectionAxis, t * (M_PI * 2.0)); Point3F pointAxis = ejectionAxis; MatrixF temp; thetaRot.setMatrix(&temp); temp.mulP(pointAxis); phiRot.setMatrix(&temp); temp.mulP(pointAxis); Point3F startOffset = axisx; temp.mulV( startOffset ); startOffset *= mDataBlock->startRadius; SplashRingPoint point; point.position = getPosition() + startOffset; point.velocity = pointAxis * mDataBlock->velocity; ring.points.push_back( point ); } ring.color = mDataBlock->colors[0]; ring.lifetime = mDataBlock->ringLifetime; ring.elapsedTime = 0.0; ring.v = mDataBlock->texFactor * mFmod( mElapsedTime, 1.0 ); return ring; }
void PathCamera::calculateAim(Point3F currentPosition) { if(mAimTarget == -1 && mAimOffset == Point3F(0.f, 0.f, 0.f)) { calculateLookDirMat(mLookDirVector); return; } Point3F dir = mAimOffset; if(mAimTarget > 0) { SceneObject* obj = NULL; Sim::findObject(mAimTarget, obj); if(obj) dir += obj->getPosition(); } dir = dir - currentPosition; dir.normalize(); calculateLookDirMat(dir); }
void AppMesh::computeNormals() { // Clear normals normals.setSize( points.size() ); for (S32 iNorm = 0; iNorm < normals.size(); iNorm++) normals[iNorm] = Point3F::Zero; // Sum triangle normals for each vertex for (S32 iPrim = 0; iPrim < primitives.size(); iPrim++) { const TSDrawPrimitive& prim = primitives[iPrim]; for (S32 iInd = 0; iInd < prim.numElements; iInd += 3) { // Compute the normal for this triangle S32 idx0 = indices[prim.start + iInd + 0]; S32 idx1 = indices[prim.start + iInd + 1]; S32 idx2 = indices[prim.start + iInd + 2]; const Point3F& v0 = points[idx0]; const Point3F& v1 = points[idx1]; const Point3F& v2 = points[idx2]; Point3F n; mCross(v2 - v0, v1 - v0, &n); n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect) normals[idx0] += n; normals[idx1] += n; normals[idx2] += n; } } // Normalize the vertex normals (this takes care of averaging the triangle normals) for (S32 iNorm = 0; iNorm < normals.size(); iNorm++) normals[iNorm].normalize(); }
void ScatterSky::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) { GFXStateBlockDesc desc; desc.fillMode = GFXFillSolid; desc.setBlend( false, GFXBlendOne, GFXBlendZero ); desc.setZReadWrite( false, false ); GFXStateBlockRef sb = GFX->GFX->createStateBlock( desc ); GFX->setStateBlock( sb ); PrimBuild::begin( GFXLineStrip, mSkyPoints.size() ); PrimBuild::color3i( 255, 0, 255 ); for ( U32 i = 0; i < mSkyPoints.size(); i++ ) { Point3F pnt = mSkyPoints[i]; pnt.normalize(); pnt *= 500; pnt += state->getCameraPosition(); PrimBuild::vertex3fv( pnt ); } PrimBuild::end(); }
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 BaseShadowRenderImage::preRenderShadow(TSRenderContext & rc, float curTime) { if (!castShadow) return; // do visibility check for shadow Point3F center; m_mul(shape->getShape().fCenter,transform,¢er); SphereF shadowSphere = SphereF(center,shape->getShape().fRadius); int shadowVis = rc.getCamera()->testVisibility(shadowSphere); if ( shadowVis==TS::ClipNoneVis ) { shadowSettings.shadowDetail = -1; return; } // shape detail to use for shadows if (!shadowOwnDetail) { projSize = rc.getCamera()->transformProjectRadius( center, shape->getShape().fRadius ); float adjustedSize = shadowDetailScale * projSize; shadowSettings.shadowDetail = shape->getShape().selectDetail( adjustedSize ); if (shadowSettings.shadowDetail==-1) return; } // first pass at shadow details setShadowDetailsA(rc); // set shadow details may veto shadow if (shadowSettings.shadowDetail==-1) return; // set light direction, but only if different than before Point3F * pLight = &lightDirection; Point3F tempDirection; if (swingDown != 0.0f) { tempDirection = (lightDirection * (1.0f - swingDown)) + (Point3F(0, 0, -1) * swingDown); tempDirection.normalize(); pLight = &tempDirection; } if (m_dot(*pLight,lastLight) < 0.999) { shadow.setLight( *pLight, shape); lastLight = *pLight; nextShadowUpdateTime = -1; } // set position of shadow shadow.setPosition(transform.p); if (shadowSettings.cacheProjection) { if (shadowSettings.recacheProjection) { shadow.calcSourceWindow(shape,transform); getPolys(); shadow.cachePolys(); shadowSettings.recacheProjection = false; } } else { shadow.calcSourceWindow(shape,transform); getPolys(); Point3F cc = rc.getCamera()->getTCW().p; Point3F camY = transform.p-cc; camY.normalize(); shadow.getPlanes(cc,camY); } setShadowDetails(rc); // set shadow details may veto shadow if (shadowSettings.shadowDetail==-1) return; if (shadowSettings.shadowDetail < shadowSettings.hiShadowDetail) shadowSettings.shadowDetail = shadowSettings.hiShadowDetail; // adjust next update time... nextShadowUpdateTime += shadowSettings.updateDelta - prevShadowUpdateDelta; prevShadowUpdateDelta = shadowSettings.updateDelta; // adjust bmp dim... int newBmpDim = shadowSettings.bmpDim; if (newBmpDim != prevBmpDim) { prevBmpDim = newBmpDim; shadow.setBitmapSize(deviceManager.getGFXDeviceManager(),newBmpDim,rc.getSurface()); nextShadowUpdateTime = -1; } shadow.setAlphaLevel(alphaLevel); // create the shadow bitmap if needed if (curTime > nextShadowUpdateTime) { if (shadowSettings.useFloor) { SimContainerQuery query; Box3F & box = query.box; box.fMin = box.fMax = center; box.fMin.z += shape->getShape().fRadius; box.fMax.z -= shape->getShape().fRadius * 1.5f; query.id = -1; query.type = -1; query.mask = shadowSettings.projectTerrainOnly ? SimTerrainObjectType : projectionMask; SimCollisionInfo collision; if (root->findLOS(query,&collision)) { Point3F n,p; m_mul(collision.surfaces[0].position,collision.surfaces.tWorld,&p); m_mul(collision.surfaces[0].normal,(RMat3F&)collision.surfaces.tWorld,&n); p -= transform.p; Point3F lift = n; lift *= shadowSettings.liftFloor; p += lift; shadow.setFloor(p,n); } else shadow.clearFloor(); } else shadow.clearFloor(); shape->setDetailLevel(shadowSettings.shadowDetail); shape->animate(); GFXPalette * pal = SimGame::get()->getWorld(SimGame::CLIENT)->getPalette(); AssertFatal(pal, "invalid palette"); // getShadowBitmap assumes calcSourceWindow already called... // ...but that's ok because we called it above shadow.getShadowBitmap(shape,pal,transform,shadowSettings.blurMethod); nextShadowUpdateTime = curTime + shadowSettings.updateDelta; } AssertFatal(root, "shadowRenderImage::preRenderShadow: cannot cast shadow before \'root\' container set"); // this'll keep track of how many shadows are out there if (shadowNum < 0) shadowNum=1; // first preRender this render cycle else shadowNum++; }
void CloudLayer::_initBuffers() { // Vertex Buffer... Point3F vertScale( 16.0f, 16.0f, mHeight ); F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f ); mVB.set( GFX, smVertCount, GFXBufferTypeStatic ); GFXCloudVertex *pVert = mVB.lock(); if(!pVert) return; for ( U32 y = 0; y < smVertStride; y++ ) { F32 v = ( (F32)y / (F32)smStrideMinusOne - 0.5f ) * 2.0f; for ( U32 x = 0; x < smVertStride; x++ ) { F32 u = ( (F32)x / (F32)smStrideMinusOne - 0.5f ) * 2.0f; F32 sx = u; F32 sy = v; F32 sz = mCos( mSqrt( sx*sx + sy*sy ) ) + zOffset; //F32 sz = 1.0f; pVert->point.set( sx, sy, sz ); pVert->point *= vertScale; // The vert to our right. Point3F rpnt; F32 ru = ( (F32)( x + 1 ) / (F32)smStrideMinusOne - 0.5f ) * 2.0f; F32 rv = v; rpnt.x = ru; rpnt.y = rv; rpnt.z = mCos( mSqrt( rpnt.x*rpnt.x + rpnt.y*rpnt.y ) ) + zOffset; rpnt *= vertScale; // The vert to our front. Point3F fpnt; F32 fu = u; F32 fv = ( (F32)( y + 1 ) / (F32)smStrideMinusOne - 0.5f ) * 2.0f; fpnt.x = fu; fpnt.y = fv; fpnt.z = mCos( mSqrt( fpnt.x*fpnt.x + fpnt.y*fpnt.y ) ) + zOffset; fpnt *= vertScale; Point3F fvec = fpnt - pVert->point; fvec.normalize(); Point3F rvec = rpnt - pVert->point; rvec.normalize(); pVert->normal = mCross( fvec, rvec ); pVert->normal.normalize(); pVert->binormal = fvec; pVert->tangent = rvec; pVert->texCoord.set( u, v ); pVert++; } } mVB.unlock(); // Primitive Buffer... mPB.set( GFX, smTriangleCount * 3, smTriangleCount, GFXBufferTypeStatic ); U16 *pIdx = NULL; mPB.lock(&pIdx); U32 curIdx = 0; for ( U32 y = 0; y < smStrideMinusOne; y++ ) { for ( U32 x = 0; x < smStrideMinusOne; x++ ) { U32 offset = x + y * smVertStride; pIdx[curIdx] = offset; curIdx++; pIdx[curIdx] = offset + 1; curIdx++; pIdx[curIdx] = offset + smVertStride + 1; curIdx++; pIdx[curIdx] = offset; curIdx++; pIdx[curIdx] = offset + smVertStride + 1; curIdx++; pIdx[curIdx] = offset + smVertStride; curIdx++; } } mPB.unlock(); }
void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) { PROFILE_SCOPE( TSMesh_InnerRender ); if( vertsPerFrame <= 0 ) return; F32 meshVisibility = rdata.getFadeOverride() * mVisibility; if ( meshVisibility < VISIBILITY_EPSILON ) return; const SceneRenderState *state = rdata.getSceneState(); RenderPassManager *renderPass = state->getRenderPass(); MeshRenderInst *coreRI = renderPass->allocInst<MeshRenderInst>(); coreRI->type = RenderPassManager::RIT_Mesh; const MatrixF &objToWorld = GFX->getWorldMatrix(); // Sort by the center point or the bounds. if ( rdata.useOriginSort() ) coreRI->sortDistSq = ( objToWorld.getPosition() - state->getCameraPosition() ).lenSquared(); else { Box3F rBox = mBounds; objToWorld.mul( rBox ); coreRI->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() ); } if (getFlags(Billboard)) { Point3F camPos = state->getDiffuseCameraPosition(); Point3F objPos; objToWorld.getColumn(3, &objPos); Point3F targetVector = camPos - objPos; if(getFlags(BillboardZAxis)) targetVector.z = 0.0f; targetVector.normalize(); MatrixF orient = MathUtils::createOrientFromDir(targetVector); orient.setPosition(objPos); orient.scale(objToWorld.getScale()); coreRI->objectToWorld = renderPass->allocUniqueXform( orient ); } else coreRI->objectToWorld = renderPass->allocUniqueXform( objToWorld ); coreRI->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); coreRI->projection = renderPass->allocSharedXform(RenderPassManager::Projection); AssertFatal( vb.isValid(), "TSMesh::innerRender() - Got invalid vertex buffer!" ); AssertFatal( pb.isValid(), "TSMesh::innerRender() - Got invalid primitive buffer!" ); coreRI->vertBuff = &vb; coreRI->primBuff = &pb; coreRI->defaultKey2 = (U32) coreRI->vertBuff; coreRI->materialHint = rdata.getMaterialHint(); coreRI->visibility = meshVisibility; coreRI->cubemap = rdata.getCubemap(); // NOTICE: SFXBB is removed and refraction is disabled! //coreRI->backBuffTex = GFX->getSfxBackBuffer(); for ( S32 i = 0; i < primitives.size(); i++ ) { const TSDrawPrimitive &draw = primitives[i]; // We need to have a material. if ( draw.matIndex & TSDrawPrimitive::NoMaterial ) continue; #ifdef TORQUE_DEBUG // for inspection if you happen to be running in a debugger and can't do bit // operations in your head. S32 triangles = draw.matIndex & TSDrawPrimitive::Triangles; S32 strip = draw.matIndex & TSDrawPrimitive::Strip; S32 fan = draw.matIndex & TSDrawPrimitive::Fan; S32 indexed = draw.matIndex & TSDrawPrimitive::Indexed; S32 type = draw.matIndex & TSDrawPrimitive::TypeMask; TORQUE_UNUSED(triangles); TORQUE_UNUSED(strip); TORQUE_UNUSED(fan); TORQUE_UNUSED(indexed); TORQUE_UNUSED(type); #endif const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask; BaseMatInstance *matInst = materials->getMaterialInst( matIndex ); #ifndef TORQUE_OS_MAC // Get the instancing material if this mesh qualifies. if ( meshType != SkinMeshType && pb->mPrimitiveArray[i].numVertices < smMaxInstancingVerts ) matInst = InstancingMaterialHook::getInstancingMat( matInst ); #endif // If we don't have a material instance after the overload then // there is nothing to render... skip this primitive. matInst = state->getOverrideMaterial( matInst ); if ( !matInst || !matInst->isValid()) continue; // If the material needs lights then gather them // here once and set them on the core render inst. if ( matInst->isForwardLit() && !coreRI->lights[0] && rdata.getLightQuery() ) rdata.getLightQuery()->getLights( coreRI->lights, 8 ); MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>(); *ri = *coreRI; ri->matInst = matInst; ri->defaultKey = matInst->getStateHint(); ri->primBuffIndex = i; // Translucent materials need the translucent type. if ( matInst->getMaterial()->isTranslucent() ) { ri->type = RenderPassManager::RIT_Translucent; ri->translucentSort = true; } renderPass->addInst( ri ); } }
void PSSMLightShadowMap::_calcPlanesCullForShadowCasters(Vector< Vector<PlaneF> > &out, const Frustum &viewFrustum, const Point3F &_ligthDir) { #define ENABLE_CULL_ASSERT PROFILE_SCOPE(PSSMLightShadowMap_render_getCullFrustrum); Point3F ligthDir = _ligthDir; PlaneF lightFarPlane, lightNearPlane; MatrixF lightFarPlaneMat(true); MatrixF invLightFarPlaneMat(true); // init data { ligthDir.normalize(); Point3F viewDir = viewFrustum.getTransform().getForwardVector(); viewDir.normalize(); const Point3F viewPosition = viewFrustum.getPosition(); const F32 viewDistance = viewFrustum.getBounds().len(); lightNearPlane = PlaneF(viewPosition + (viewDistance * -ligthDir), ligthDir); const Point3F lightFarPlanePos = viewPosition + (viewDistance * ligthDir); lightFarPlane = PlaneF(lightFarPlanePos, -ligthDir); lightFarPlaneMat = MathUtils::createOrientFromDir(-ligthDir); lightFarPlaneMat.setPosition(lightFarPlanePos); lightFarPlaneMat.invertTo(&invLightFarPlaneMat); } Vector<Point2F> projVertices; //project all frustum vertices into plane // all vertices are 2d and local to far plane projVertices.setSize(8); for (int i = 0; i < 8; ++i) // { const Point3F &point = viewFrustum.getPoints()[i]; #ifdef ENABLE_CULL_ASSERT AssertFatal( PlaneF::Front == lightNearPlane.whichSide(point), "" ); AssertFatal( PlaneF::Front == lightFarPlane.whichSide(point), "" ); #endif Point3F localPoint(lightFarPlane.project(point)); invLightFarPlaneMat.mulP(localPoint); projVertices[i] = Point2F(localPoint.x, localPoint.z); } //create hull arround projected proints Vector<Point2F> hullVerts; MathUtils::mBuildHull2D(projVertices, hullVerts); Vector<PlaneF> planes; planes.push_back(lightNearPlane); planes.push_back(lightFarPlane); //build planes for (int i = 0; i < (hullVerts.size() - 1); ++i) { Point2F pos2D = (hullVerts[i] + hullVerts[i + 1]) / 2; Point3F pos3D(pos2D.x, 0, pos2D.y); Point3F pos3DA(hullVerts[i].x, 0, hullVerts[i].y); Point3F pos3DB(hullVerts[i + 1].x, 0, hullVerts[i + 1].y); // move hull points to 3d space lightFarPlaneMat.mulP(pos3D); lightFarPlaneMat.mulP(pos3DA); lightFarPlaneMat.mulP(pos3DB); PlaneF plane(pos3D, MathUtils::mTriangleNormal(pos3DB, pos3DA, (pos3DA - ligthDir))); planes.push_back(plane); } //recalculate planes for each splits for (int split = 0; split < mNumSplits; ++split) { Frustum subFrustum(viewFrustum); subFrustum.cropNearFar(mSplitDist[split], mSplitDist[split + 1]); subFrustum.setFarDist(getMin(subFrustum.getFarDist()*2.5f, viewFrustum.getFarDist())); subFrustum.update(); Vector<PlaneF> subPlanes = planes; for (int planeIdx = 0; planeIdx < subPlanes.size(); ++planeIdx) { PlaneF &plane = subPlanes[planeIdx]; F32 minDist = 0; //calculate near vertex distance for (int vertexIdx = 0; vertexIdx < 8; ++vertexIdx) { Point3F point = subFrustum.getPoints()[vertexIdx]; minDist = getMin(plane.distToPlane(point), minDist); } // move plane to near vertex Point3F newPos = plane.getPosition() + (plane.getNormal() * minDist); plane = PlaneF(newPos, plane.getNormal()); #ifdef ENABLE_CULL_ASSERT for(int x = 0; x < 8; ++x) { AssertFatal( PlaneF::Back != plane.whichSide( subFrustum.getPoints()[x] ), ""); } #endif } out.push_back(subPlanes); } #undef ENABLE_CULL_ASSERT }
Move HoverPodController::getMove(ShapeBase* obj) { Move retMove = NullMove; GameConnection* client = NULL; if(!Sim::findObject(mClientId, client)) return retMove; ShapeBase* control = client->getControlObject(); if(control) { Move cMove = client->lastReceivedMove(); Point3F v1, v2; control->getTransform().getColumn(0, &v1); control->getTransform().getColumn(1, &v2); Point3F pv = v1*cMove.x + v2*cMove.y; //pv = mDirection * mDot(pv, mDirection); obj->getTransform().getColumn(0, &v1); obj->getTransform().getColumn(1, &v2); retMove.x = mDot(v1, pv); retMove.y = mDot(v2, pv); } return retMove; #if 0 if(this->isServerObject() && this->isMounted()) { Move mMove = NullMove; ShapeBase* mount = this->getObjectMount(); if(mount->getType() & VehicleObjectType) { Vehicle* vehicle = (Vehicle*)mount; if(move && move->x != 0) { mMove.yaw = move->x; } else { Point3F zv; vehicle->getTransform().getColumn(2, &zv); zv.normalize(); Point3F m = vehicle->getRigid().angMomentum; //Point3F v = vehicle->getRigid().angVelocity; F32 dot = mDot(zv, m); //Con::printf("%f / %f %f %f / %f %f %f", dot, // m.x, m.y, m.z, v.x, v.y, v.z); mMove.yaw = dot / 50; } if(move) mMove.y = move->y; } else if(move) { Point3F v1, v2; this->getTransform().getColumn(0, &v1); this->getTransform().getColumn(1, &v2); Point3F pv = v1*move->x + v2*move->y; mount->getTransform().getColumn(0, &v1); mount->getTransform().getColumn(1, &v2); mMove.x = mDot(v1, pv); mMove.y = mDot(v2, pv); } mount->processTick(&mMove); } #endif
void GuiRiverEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) { mGizmo->on3DMouseDown( event ); if ( !isFirstResponder() ) setFirstResponder(); // Get the raycast collision position Point3F tPos; if ( !getStaticPos( event, tPos ) ) return; // Construct a LineSegment from the camera position to 1000 meters away in // the direction clicked. // If that segment hits the terrain, truncate the ray to only be that length. // We will use a LineSegment/Sphere intersection test to determine if a RiverNode // was clicked. Point3F startPnt = event.pos; Point3F endPnt = event.pos + event.vec * 1000.0f; RayInfo ri; if ( gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri) ) endPnt = ri.point; River *riverPtr = NULL; River *clickedRiverPtr = NULL; // Did we click on a river? check current selection first U32 insertNodeIdx = -1; Point3F collisionPnt; if ( mSelRiver != NULL && mSelRiver->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) { clickedRiverPtr = mSelRiver; } else { for ( SimSetIterator iter(mRiverSet); *iter; ++iter ) { riverPtr = static_cast<River*>( *iter ); if ( riverPtr->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) { clickedRiverPtr = riverPtr; break; } } } // Did we click on a riverNode? bool nodeClicked = false; S32 clickedNodeIdx = -1; F32 clickedNodeDist = mNodeSphereRadius; // If we clicked on the currently selected river, only scan its nodes if ( mSelRiver != NULL && clickedRiverPtr == mSelRiver ) { for ( U32 i = 0; i < mSelRiver->mNodes.size(); i++ ) { const Point3F &nodePos = mSelRiver->mNodes[i].point; Point3F screenPos; project( nodePos, &screenPos ); F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len(); if ( dist < clickedNodeDist ) { clickedNodeDist = dist; clickedNodeIdx = i; nodeClicked = true; } } } else { for ( SimSetIterator iter(mRiverSet); *iter; ++iter ) { riverPtr = static_cast<River*>( *iter ); for ( U32 i = 0; i < riverPtr->mNodes.size(); i++ ) { const Point3F &nodePos = riverPtr->mNodes[i].point; Point3F screenPos; project( nodePos, &screenPos ); F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len(); if ( dist < clickedNodeDist ) { // we found a hit! clickedNodeDist = dist; clickedNodeIdx = i; nodeClicked = true; clickedRiverPtr = riverPtr; } } } } // shortcuts bool dblClick = ( event.mouseClickCount > 1 ); if( dblClick ) { if( mMode == mSelectRiverMode ) { setMode( mAddRiverMode, true ); return; } if( mMode == mAddNodeMode ) { // Delete the node attached to the cursor. deleteSelectedNode(); mMode = mAddRiverMode; return; } } //this check is here in order to bounce back from deleting a whole road with ctrl+z //this check places the editor back into addrivermode if ( mMode == mAddNodeMode ) { if ( !mSelRiver ) mMode = mAddRiverMode; } if ( mMode == mSelectRiverMode ) { // Did not click on a River or a node. if ( !clickedRiverPtr ) { setSelectedRiver( NULL ); setSelectedNode( -1 ); return; } // Clicked on a River that wasn't the currently selected River. if ( clickedRiverPtr != mSelRiver ) { setSelectedRiver( clickedRiverPtr ); setSelectedNode( clickedNodeIdx ); return; } // Clicked on a node in the currently selected River that wasn't // the currently selected node. if ( nodeClicked ) { setSelectedNode( clickedNodeIdx ); return; } } else if ( mMode == mAddRiverMode ) { if ( nodeClicked ) { // A double-click on a node in Normal mode means set AddNode mode. if ( clickedNodeIdx == 0 ) { setSelectedRiver( clickedRiverPtr ); setSelectedNode( clickedNodeIdx ); mAddNodeIdx = clickedNodeIdx; mMode = mAddNodeMode; mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx ); mIsDirty = true; return; } else if ( clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 ) { setSelectedRiver( clickedRiverPtr ); setSelectedNode( clickedNodeIdx ); mAddNodeIdx = U32_MAX; mMode = mAddNodeMode; mSelNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal); mIsDirty = true; setSelectedNode( mSelNode ); return; } } if ( !isMethod( "createRiver" ) ) { Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method does not exist." ); return; } const char *res = Con::executef( this, "createRiver" ); River *newRiver; if ( !Sim::findObject( res, newRiver ) ) { Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method did not return a river object." ); return; } // Add to MissionGroup SimGroup *missionGroup; if ( !Sim::findObject( "MissionGroup", missionGroup ) ) Con::errorf( "GuiRiverEditorCtrl - could not find MissionGroup to add new River" ); else missionGroup->addObject( newRiver ); Point3F pos( endPnt ); pos.z += mDefaultDepth * 0.5f; newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 ); U32 newNode = newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 ); // Always add to the end of the road, the first node is the start. mAddNodeIdx = U32_MAX; setSelectedRiver( newRiver ); setSelectedNode( newNode ); mMode = mAddNodeMode; // Disable the hover node while in addNodeMode, we // don't want some random node enlarged. mHoverNode = -1; // Grab the mission editor undo manager. UndoManager *undoMan = NULL; if ( !Sim::findObject( "EUndoManager", undoMan ) ) { Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); return; } // Create the UndoAction. MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad"); action->addObject( newRiver ); // Submit it. undoMan->addAction( action ); return; } else if ( mMode == mAddNodeMode ) { // Oops the road got deleted, maybe from an undo action? // Back to NormalMode. if ( mSelRiver ) { // A double-click on a node in Normal mode means set AddNode mode. if ( clickedNodeIdx == 0 ) { submitUndo( "Add Node" ); mAddNodeIdx = clickedNodeIdx; mMode = mAddNodeMode; mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx ); mIsDirty = true; setSelectedNode( mSelNode ); return; } else { if( clickedRiverPtr && clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 ) { submitUndo( "Add Node" ); mAddNodeIdx = U32_MAX; mMode = mAddNodeMode; U32 newNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal); mIsDirty = true; setSelectedNode( newNode ); return; } else { submitUndo( "Insert Node" ); // A single-click on empty space while in // AddNode mode means insert / add a node. //submitUndo( "Add Node" ); //F32 width = mSelRiver->getNodeWidth( mSelNode ); U32 newNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx); mIsDirty = true; setSelectedNode( newNode ); return; } } } } else if ( mMode == mInsertPointMode && mSelRiver != NULL ) { if ( clickedRiverPtr == mSelRiver ) { // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node... // in order to handle inserting nodes in the same way as for DecalRoad U32 prevNodeIdx = insertNodeIdx; U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRiver->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1; const RiverNode &prevNode = mSelRiver->mNodes[prevNodeIdx]; const RiverNode &nextNode = mSelRiver->mNodes[nextNodeIdx]; F32 width = ( prevNode.width + nextNode.width ) * 0.5f; F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f; Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f; normal.normalize(); submitUndo( "Insert Node" ); U32 newNode = mSelRiver->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 ); mIsDirty = true; setSelectedNode( newNode ); return; } } else if ( mMode == mRemovePointMode && mSelRiver != NULL ) { if ( nodeClicked && clickedRiverPtr == mSelRiver ) { setSelectedNode( clickedNodeIdx ); deleteSelectedNode(); return; } } else if ( mMode == mMovePointMode ) { if ( nodeClicked && clickedRiverPtr == mSelRiver ) { setSelectedNode( clickedNodeIdx ); return; } } else if ( mMode == mScalePointMode ) { if ( nodeClicked && clickedRiverPtr == mSelRiver ) { setSelectedNode( clickedNodeIdx ); return; } } else if ( mMode == mRotatePointMode ) { if ( nodeClicked && clickedRiverPtr == mSelRiver ) { setSelectedNode( clickedNodeIdx ); return; } } }
void blTerrainProxy::lightVector(LightInfo * light) { // Grab our terrain object TerrainBlock* terrain = getObject(); if (!terrain) return; // Get the direction to the light (the inverse of the direction // the light is pointing) Point3F lightDir = -light->getDirection(); lightDir.normalize(); // Get the ratio between the light map pixel and world space (used below) F32 lmTerrRatio = (F32)mTerrainBlockSize / (F32) mLightMapSize; lmTerrRatio *= terrain->getSquareSize(); // Get the terrain position Point3F terrPos( terrain->getTransform().getPosition() ); U32 i = 0; for (U32 y = 0; y < mLightMapSize; y++) { for (U32 x = 0; x < mLightMapSize; x++) { // Get the relative pixel position and scale it // by the ratio between lightmap and world space Point2F pixelPos(x, y); pixelPos *= lmTerrRatio; // Start with a default normal of straight up Point3F normal(0.0f, 0.0f, 1.0f); // Try to get the actual normal from the terrain. // Note: this won't change the default normal if // it can't find a normal. terrain->getNormal(pixelPos, &normal); // The terrain lightmap only contains shadows. F32 shadowed = 0.0f; // Get the height at the lightmap pixel's position F32 height = 0.0f; terrain->getHeight(pixelPos, &height); // Calculate the 3D position of the pixel Point3F pixelPos3F(pixelPos.x, pixelPos.y, height); // Translate that position by the terrain's transform terrain->getTransform().mulP(pixelPos3F); // Offset slighting along the normal so that we don't // raycast into ourself pixelPos3F += (normal * 0.1f); // Calculate the light's position. // If it is a vector light like the sun (no position // just direction) then translate along that direction // a reasonable distance to get a point sufficiently // far away Point3F lightPos = light->getPosition(); if(light->getType() == LightInfo::Vector) { lightPos = 1000.f * lightDir; lightPos = pixelPos3F + lightPos; } // Cast a ray from the world space position of the lightmap pixel to the light source. // If we hit something then we are in shadow. This allows us to be shadowed by anything // that supports a castRay operation. RayInfo info; if(terrain->getContainer()->castRay(pixelPos3F, lightPos, STATIC_COLLISION_TYPEMASK, &info)) { // Shadow the pixel. shadowed = 1.0f; } // Set the final lightmap color. mLightmap[i++] += ColorF::WHITE * mClampF( 1.0f - shadowed, 0.0f, 1.0f ); } } }
void Turret::shoot (bool playerControlled, Player* targetPlayer) { if (data && data->isSustained == false) { if (data && data->projectile.type == -1) { if (!isGhost()) if (const char* script = scriptName("onFire")) Console->executef(2, script, scriptThis()); } else { float energy = getEnergy(); if (waitTime <= manager->getCurrentTime() && data && energy >= data->minGunEnergy && data->projectile.type != -1) { TMat3F muzzleTransform; getMuzzleTransform(0, &muzzleTransform); Projectile* bullet = createProjectile(data->projectile); if (!playerControlled && data->deflection) { static Random random; EulerF angles; muzzleTransform.angles (&angles); angles.x += (random.getFloat() - 0.5) * M_2PI * data->deflection; angles.z += (random.getFloat() - 0.5) * M_2PI * data->deflection; muzzleTransform.set (angles, muzzleTransform.p); } else if (playerControlled) { Point3F start = muzzleTransform.p; muzzleTransform = getEyeTransform (); aimedTransform (&muzzleTransform, start); muzzleTransform.p = start; } bullet->initProjectile (muzzleTransform, Point3F (0, 0, 0), getId()); if (bullet->isTargetable() == true) { if (targetPlayer != NULL) { if (GameBase* mo = targetPlayer->getMountObject()) bullet->setTarget(static_cast<ShapeBase*>(mo)); else bullet->setTarget(targetPlayer); } else if (playerControlled) { ShapeBase* pClosest = NULL; Point3F closeHisPos; float closestVal = -2.0f; SimSet::iterator itr; Point3F lookDir; getEyeTransform().getRow(1, &lookDir); lookDir.normalize(); SimContainerQuery collisionQuery; SimCollisionInfo info; collisionQuery.id = getId(); collisionQuery.type = -1; collisionQuery.mask = Projectile::csm_collisionMask; collisionQuery.detail = SimContainerQuery::DefaultDetail; collisionQuery.box.fMin = getEyeTransform().p; SimContainer* pRoot = (SimContainer*)manager->findObject(SimRootContainerId); SimSet* pSet = dynamic_cast<SimSet*>(manager->findObject(PlayerSetId)); AssertFatal(pSet != NULL, "No player set?"); for (itr = pSet->begin(); itr != pSet->end(); itr++) { Player* pPlayer = dynamic_cast<Player*>(*itr); if (!pPlayer || pPlayer->getVisibleToTeam(getTeam()) == false) continue; collisionQuery.box.fMax = pPlayer->getBoxCenter(); if (pRoot->findLOS(collisionQuery, &info, SimCollisionImageQuery::High) == true) { if (info.object != (SimObject*)pPlayer) continue; } Point3F hisPos = pPlayer->getBoxCenter(); hisPos -= getLinearPosition(); hisPos.normalize(); float prod = m_dot(hisPos, lookDir); if (prod > 0.0f && prod > closestVal) { closestVal = prod; pClosest = pPlayer; closeHisPos = hisPos; } } pSet = dynamic_cast<SimSet*>(manager->findObject(MoveableSetId)); AssertFatal(pSet != NULL, "No moveable set?"); for (itr = pSet->begin(); itr != pSet->end(); itr++) { if (((*itr)->getType() & VehicleObjectType) == 0) continue; ShapeBase* pObject = dynamic_cast<ShapeBase*>(*itr); if (pObject->getVisibleToTeam(getTeam()) == false) continue; collisionQuery.box.fMax = pObject->getBoxCenter(); if (pRoot->findLOS(collisionQuery, &info, SimCollisionImageQuery::High) == true) { if (info.object != (SimObject*)pObject) continue; } Point3F hisPos = pObject->getBoxCenter(); hisPos -= getLinearPosition(); hisPos.normalize(); float prod = m_dot(hisPos, lookDir); if (prod > 0.0f && prod > closestVal) { closestVal = prod; closeHisPos = hisPos; pClosest = pObject; } } // We need to find the current FOV, and take the percentage of // it specified in the .dat file for this turret. Only if the // do product is greater than this, do we allow the target to // be set... // float myFov = (fov / 2.0) * data->targetableFovRatio; float compCos = cos(myFov); if (compCos > 0.996f) // hack for single precision math. It's very compCos = 0.996; // hard to get more precise answers from the dot prod. if (pClosest != NULL && closestVal > compCos) bullet->setTarget(pClosest); } } if (data->maxGunEnergy) { float e; e = energy > data->maxGunEnergy ? data->maxGunEnergy : energy; float pofm = e / float(data->maxGunEnergy); bullet->setEnergy (e, pofm); energy -= e; setEnergy (energy); } SimGroup *grp = NULL; if(SimObject *obj = manager->findObject("MissionCleanup")) grp = dynamic_cast<SimGroup*>(obj); if(!manager->registerObject(bullet)) delete bullet; else { if(grp) grp->addObject(bullet); else manager->addObject(bullet); } waitTime = manager->getCurrentTime() + data->reloadDelay; if (animThread) { setFireThread (); animThread->SetPosition (0.0); } fireCount++; setMaskBits (ShootingMask); } } } else { if (data && data->projectile.type == -1) { if (!isGhost()) if (const char* script = scriptName("onFire")) Console->executef(2, script, scriptThis()); } else { float energy = getEnergy(); if (waitTime <= manager->getCurrentTime() && data && energy >= data->minGunEnergy && data->projectile.type != -1) { TMat3F muzzleTransform; getMuzzleTransform(0, &muzzleTransform); Projectile* bullet = createProjectile(data->projectile); if (!playerControlled && data->deflection) { static Random random; EulerF angles; muzzleTransform.angles (&angles); angles.x += (random.getFloat() - 0.5) * M_2PI * data->deflection; angles.z += (random.getFloat() - 0.5) * M_2PI * data->deflection; muzzleTransform.set (angles, muzzleTransform.p); } else if (playerControlled) { Point3F start = muzzleTransform.p; muzzleTransform = getEyeTransform (); aimedTransform (&muzzleTransform, start); muzzleTransform.p = start; } bullet->initProjectile (muzzleTransform, Point3F (0, 0, 0), getId()); AssertFatal(bullet->isSustained() == true, "Error, must be sustained bullet"); SimGroup *grp = NULL; if(SimObject *obj = manager->findObject("MissionCleanup")) grp = dynamic_cast<SimGroup*>(obj); if(!manager->registerObject(bullet)) delete bullet; else { if(grp) grp->addObject(bullet); else manager->addObject(bullet); } if (animThread) { setFireThread (); animThread->SetPosition (0.0); } fireCount++; setMaskBits (ShootingMask); m_fireState = Firing; m_beganState = wg->currentTime; m_pProjectile = bullet; m_pTarget = targetPlayer; if (m_pTarget) deleteNotify(m_pTarget); } } } }
void HoverVehicle::updateForces(F32 /*dt*/) { PROFILE_SCOPE( HoverVehicle_UpdateForces ); Point3F gravForce(0, 0, sHoverVehicleGravity * mRigid.mass * mGravityMod); MatrixF currTransform; mRigid.getTransform(&currTransform); mRigid.atRest = false; mThrustLevel = (mForwardThrust * mDataBlock->mainThrustForce + mReverseThrust * mDataBlock->reverseThrustForce + mLeftThrust * mDataBlock->strafeThrustForce + mRightThrust * mDataBlock->strafeThrustForce); Point3F thrustForce = ((Point3F( 0, 1, 0) * (mForwardThrust * mDataBlock->mainThrustForce)) + (Point3F( 0, -1, 0) * (mReverseThrust * mDataBlock->reverseThrustForce)) + (Point3F(-1, 0, 0) * (mLeftThrust * mDataBlock->strafeThrustForce)) + (Point3F( 1, 0, 0) * (mRightThrust * mDataBlock->strafeThrustForce))); currTransform.mulV(thrustForce); if (mJetting) thrustForce *= mDataBlock->turboFactor; Point3F torque(0, 0, 0); Point3F force(0, 0, 0); Point3F vel = mRigid.linVelocity; F32 baseStabLen = getBaseStabilizerLength(); Point3F stabExtend(0, 0, -baseStabLen); currTransform.mulV(stabExtend); StabPoint stabPoints[2]; stabPoints[0].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, mObjBox.maxExtents.y, (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5); stabPoints[1].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, mObjBox.minExtents.y, (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5); U32 j, i; for (i = 0; i < 2; i++) { currTransform.mulP(stabPoints[i].osPoint, &stabPoints[i].wsPoint); stabPoints[i].wsExtension = stabExtend; stabPoints[i].extension = baseStabLen; stabPoints[i].wsVelocity = mRigid.linVelocity; } RayInfo rinfo; mFloating = true; bool reallyFloating = true; F32 compression[2] = { 0.0f, 0.0f }; F32 normalMod[2] = { 0.0f, 0.0f }; bool normalSet[2] = { false, false }; Point3F normal[2]; for (j = 0; j < 2; j++) { if (getContainer()->castRay(stabPoints[j].wsPoint, stabPoints[j].wsPoint + stabPoints[j].wsExtension * 2.0, TerrainObjectType | WaterObjectType, &rinfo)) { reallyFloating = false; if (rinfo.t <= 0.5) { // Ok, stab is in contact with the ground, let's calc the forces... compression[j] = (1.0 - (rinfo.t * 2.0)) * baseStabLen; } normalSet[j] = true; normalMod[j] = rinfo.t < 0.5 ? 1.0 : (1.0 - ((rinfo.t - 0.5) * 2.0)); normal[j] = rinfo.normal; } if ( pointInWater( stabPoints[j].wsPoint ) ) compression[j] = baseStabLen; } for (j = 0; j < 2; j++) { if (compression[j] != 0.0) { mFloating = false; // Spring force and damping Point3F springForce = -stabPoints[j].wsExtension; springForce.normalize(); springForce *= compression[j] * mDataBlock->stabSpringConstant; Point3F springDamping = -stabPoints[j].wsExtension; springDamping.normalize(); springDamping *= -getMin(mDot(springDamping, stabPoints[j].wsVelocity), 0.7f) * mDataBlock->stabDampingConstant; force += springForce + springDamping; } } // Gravity if (reallyFloating == false) force += gravForce; else force += gravForce * mDataBlock->floatingGravMag; // Braking F32 vellen = mRigid.linVelocity.len(); if (mThrottle == 0.0f && mLeftThrust == 0.0f && mRightThrust == 0.0f && vellen != 0.0f && vellen < mDataBlock->brakingActivationSpeed) { Point3F dir = mRigid.linVelocity; dir.normalize(); dir.neg(); force += dir * mDataBlock->brakingForce; } // Gyro Drag torque = -mRigid.angMomentum * mDataBlock->gyroDrag; // Move to proper normal Point3F sn, r; currTransform.getColumn(2, &sn); if (normalSet[0] || normalSet[1]) { if (normalSet[0] && normalSet[1]) { F32 dot = mDot(normal[0], normal[1]); if (dot > 0.999) { // Just pick the first normal. They're too close to call if ((sn - normal[0]).lenSquared() > 0.00001) { mCross(sn, normal[0], &r); torque += r * mDataBlock->normalForce * normalMod[0]; } } else { Point3F rotAxis; mCross(normal[0], normal[1], &rotAxis); rotAxis.normalize(); F32 angle = mAcos(dot) * (normalMod[0] / (normalMod[0] + normalMod[1])); AngAxisF aa(rotAxis, angle); QuatF q(aa); MatrixF tempMat(true); q.setMatrix(&tempMat); Point3F newNormal; tempMat.mulV(normal[1], &newNormal); if ((sn - newNormal).lenSquared() > 0.00001) { mCross(sn, newNormal, &r); torque += r * (mDataBlock->normalForce * ((normalMod[0] + normalMod[1]) * 0.5)); } } } else { Point3F useNormal; F32 useMod; if (normalSet[0]) { useNormal = normal[0]; useMod = normalMod[0]; } else { useNormal = normal[1]; useMod = normalMod[1]; } if ((sn - useNormal).lenSquared() > 0.00001) { mCross(sn, useNormal, &r); torque += r * mDataBlock->normalForce * useMod; } } } else { if ((sn - Point3F(0, 0, 1)).lenSquared() > 0.00001) { mCross(sn, Point3F(0, 0, 1), &r); torque += r * mDataBlock->restorativeForce; } } Point3F sn2; currTransform.getColumn(0, &sn); currTransform.getColumn(1, &sn2); mCross(sn, sn2, &r); r.normalize(); torque -= r * (mSteering.x * mDataBlock->steeringForce); currTransform.getColumn(0, &sn); currTransform.getColumn(2, &sn2); mCross(sn, sn2, &r); r.normalize(); torque -= r * (mSteering.x * mDataBlock->rollForce); currTransform.getColumn(1, &sn); currTransform.getColumn(2, &sn2); mCross(sn, sn2, &r); r.normalize(); torque -= r * (mSteering.y * mDataBlock->pitchForce); // Apply drag Point3F vDrag = mRigid.linVelocity; if (!mFloating) { vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor)); } else { vDrag.convolve(Point3F(0.25, 0.25, mDataBlock->vertFactor)); } force -= vDrag * mDataBlock->dragForce; force += mFloating ? thrustForce * mDataBlock->floatingThrustFactor : thrustForce; // Add in physical zone force force += mAppliedForce; // Container buoyancy & drag force += Point3F(0, 0,-mBuoyancy * sHoverVehicleGravity * mRigid.mass * mGravityMod); force -= mRigid.linVelocity * mDrag; torque -= mRigid.angMomentum * mDrag; mRigid.force = force; mRigid.torque = torque; }
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 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); } }
void DebugDrawer::drawPolyhedronDebugInfo( const AnyPolyhedron& polyhedron, const MatrixF& transform, const Point3F& scale ) { Point3F center = polyhedron.getCenterPoint(); center.convolve( scale ); transform.mulP( center ); // Render plane indices and normals. const U32 numPlanes = polyhedron.getNumPlanes(); for( U32 i = 0; i < numPlanes; ++ i ) { const AnyPolyhedron::PlaneType& plane = polyhedron.getPlanes()[ i ]; Point3F planePos = plane.getPosition(); planePos.convolve( scale ); transform.mulP( planePos ); Point3F normal = plane.getNormal(); transform.mulV( normal ); drawText( planePos, String::ToString( i ), ColorI::BLACK ); drawLine( planePos, planePos + normal, ColorI::GREEN ); } // Render edge indices and direction indicators. const U32 numEdges = polyhedron.getNumEdges(); for( U32 i = 0; i < numEdges; ++ i ) { const AnyPolyhedron::EdgeType& edge = polyhedron.getEdges()[ i ]; Point3F v1 = polyhedron.getPoints()[ edge.vertex[ 0 ] ]; Point3F v2 = polyhedron.getPoints()[ edge.vertex[ 1 ] ]; v1.convolve( scale ); v2.convolve( scale ); transform.mulP( v1 ); transform.mulP( v2 ); const Point3F midPoint = v1 + ( v2 - v1 ) / 2.f; drawText( midPoint, String::ToString( "%i (%i, %i)", i, edge.face[ 0 ], edge.face[ 1 ] ), ColorI::WHITE ); // Push out the midpoint away from the center to place the direction indicator. Point3F pushDir = midPoint - center; pushDir.normalize(); const Point3F dirPoint = midPoint + pushDir; const Point3F lineDir = ( v2 - v1 ) / 2.f; drawLine( dirPoint, dirPoint + lineDir, ColorI::RED ); } // Render point indices and coordinates. const U32 numPoints = polyhedron.getNumPoints(); for( U32 i = 0; i < numPoints; ++ i ) { Point3F p = polyhedron.getPoints()[ i ]; p.convolve( scale ); transform.mulP( p ); drawText( p, String::ToString( "%i: (%.2f, %.2f, %.2f)", i, p.x, p.y, p.z ), ColorF::WHITE ); } }
bool Frustum::bakeProjectionOffset() { // Nothing to bake if ortho if( mIsOrtho ) return false; // Nothing to bake if no offset if( mProjectionOffset.isZero() ) return false; // Near plane points in camera space Point3F np[4]; np[0].set( mNearLeft, mNearDist, mNearTop ); // NearTopLeft np[1].set( mNearRight, mNearDist, mNearTop ); // NearTopRight np[2].set( mNearLeft, mNearDist, mNearBottom ); // NearBottomLeft np[3].set( mNearRight, mNearDist, mNearBottom ); // NearBottomRight // Generate the near plane PlaneF nearPlane( np[0], np[1], np[3] ); // Far plane points in camera space const F32 farOverNear = mFarDist / mNearDist; Point3F fp0( mNearLeft * farOverNear, mFarDist, mNearTop * farOverNear ); // FarTopLeft Point3F fp1( mNearRight * farOverNear, mFarDist, mNearTop * farOverNear ); // FarTopRight Point3F fp2( mNearLeft * farOverNear, mFarDist, mNearBottom * farOverNear ); // FarBottomLeft Point3F fp3( mNearRight * farOverNear, mFarDist, mNearBottom * farOverNear ); // FarBottomRight // Generate the far plane PlaneF farPlane( fp0, fp1, fp3 ); // The offset camera point Point3F offsetCamera( mProjectionOffset.x, 0.0f, mProjectionOffset.y ); // The near plane point we'll be using for our calculations below U32 nIndex = 0; if( mProjectionOffset.x < 0.0 ) { // Offset to the left so we'll need to use the near plane point on the right nIndex = 1; } if( mProjectionOffset.y > 0.0 ) { // Offset to the top so we'll need to use the near plane point at the bottom nIndex += 2; } // Begin by calculating the offset point on the far plane as it goes // from the offset camera to the edge of the near plane. Point3F farPoint; Point3F fdir = np[nIndex] - offsetCamera; fdir.normalize(); if( farPlane.intersect(offsetCamera, fdir, &farPoint) ) { // Calculate the new near plane edge from the non-offset camera position // to the far plane point from above. Point3F nearPoint; Point3F ndir = farPoint; ndir.normalize(); if( nearPlane.intersect( Point3F::Zero, ndir, &nearPoint) ) { // Handle a x offset if( mProjectionOffset.x < 0.0 ) { // The new near plane right side mNearRight = nearPoint.x; } else if( mProjectionOffset.x > 0.0 ) { // The new near plane left side mNearLeft = nearPoint.x; } // Handle a y offset if( mProjectionOffset.y < 0.0 ) { // The new near plane top side mNearTop = nearPoint.y; } else if( mProjectionOffset.y > 0.0 ) { // The new near plane bottom side mNearBottom = nearPoint.y; } } } mDirty = true; // Indicate that we've modified the frustum return true; }
void Projectile::emitParticles(const Point3F& from, const Point3F& to, const Point3F& vel, const U32 ms) { if ( mHasExploded ) return; Point3F axis = -vel; if( axis.isZero() ) axis.set( 0.0, 0.0, 1.0 ); else axis.normalize(); bool fromWater = pointInWater(from); bool toWater = pointInWater(to); if (!fromWater && !toWater && bool(mParticleEmitter)) // not in water mParticleEmitter->emitParticles(from, to, axis, vel, ms); else if (fromWater && toWater && bool(mParticleWaterEmitter)) // in water mParticleWaterEmitter->emitParticles(from, to, axis, vel, ms); else if (!fromWater && toWater && mDataBlock->splash) // entering water { // cast the ray to get the surface point of the water RayInfo rInfo; if (gClientContainer.castRay(from, to, WaterObjectType, &rInfo)) { MatrixF trans = getTransform(); trans.setPosition(rInfo.point); Splash *splash = new Splash(); splash->onNewDataBlock(mDataBlock->splash, false); splash->setTransform(trans); splash->setInitialState(trans.getPosition(), Point3F(0.0, 0.0, 1.0)); if (!splash->registerObject()) { delete splash; splash = NULL; } // create an emitter for the particles out of water and the particles in water if (mParticleEmitter) mParticleEmitter->emitParticles(from, rInfo.point, axis, vel, ms); if (mParticleWaterEmitter) mParticleWaterEmitter->emitParticles(rInfo.point, to, axis, vel, ms); } } else if (fromWater && !toWater && mDataBlock->splash) // leaving water { // cast the ray in the opposite direction since that point is out of the water, otherwise // we hit water immediately and wont get the appropriate surface point RayInfo rInfo; if (gClientContainer.castRay(to, from, WaterObjectType, &rInfo)) { MatrixF trans = getTransform(); trans.setPosition(rInfo.point); Splash *splash = new Splash(); splash->onNewDataBlock(mDataBlock->splash,false); splash->setTransform(trans); splash->setInitialState(trans.getPosition(), Point3F(0.0, 0.0, 1.0)); if (!splash->registerObject()) { delete splash; splash = NULL; } // create an emitter for the particles out of water and the particles in water if (mParticleEmitter) mParticleEmitter->emitParticles(rInfo.point, to, axis, vel, ms); if (mParticleWaterEmitter) mParticleWaterEmitter->emitParticles(from, rInfo.point, axis, vel, ms); } } }