void TerrCell::_updatePrimitiveBuffer() { PROFILE_SCOPE( TerrCell_UpdatePrimitiveBuffer ); if ( !mHasEmpty ) { if ( mPrimBuffer.isValid() ) { // There are no more empty squares for this cell, so // get rid of the primitive buffer to use the standard one. mPrimBuffer = NULL; } return; } // Build our custom primitive buffer. We're setting it to the maximum allowed // size, but should be just shy of it depending on the number of empty squares // in this cell. We could calculate it, but note that it would be different // from mEmptyVertexList.size() as that can include vertices on the edges that // are really considered part of another cell's squares. So we take a slightly // larger buffer over running through the calculation. mPrimBuffer.set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" ); GFXPrimitive *prim = mPrimBuffer.getPointer()->mPrimitiveArray; prim->type = GFXTriangleList; prim->numVertices = smVBSize; mTriCount = 0; // Lock and fill it up! U16 *idxBuff; mPrimBuffer.lock( &idxBuff ); U32 counter = 0; U32 maxIndex = 0; for ( U32 y = 0; y < smMinCellSize; y++ ) { const U32 yTess = y % 2; for ( U32 x = 0; x < smMinCellSize; x++ ) { U32 index = ( y * smVBStride ) + x; // Should this square be skipped? if ( _isVertIndexEmpty(index) ) continue; const U32 xTess = x % 2; if ( ( xTess == 0 && yTess == 0 ) || ( xTess != 0 && yTess != 0 ) ) { idxBuff[0] = index + 0; idxBuff[1] = index + smVBStride; idxBuff[2] = index + smVBStride + 1; idxBuff[3] = index + 0; idxBuff[4] = index + smVBStride + 1; idxBuff[5] = index + 1; } else { idxBuff[0] = index + 1; idxBuff[1] = index; idxBuff[2] = index + smVBStride; idxBuff[3] = index + 1; idxBuff[4] = index + smVBStride; idxBuff[5] = index + smVBStride + 1; } idxBuff += 6; maxIndex = index + 1 + smVBStride; counter += 6; mTriCount += 2; } } // Now add indices for the 'skirts'. // These could probably be reduced to a loop. // Temporaries that hold triangle indices. // Top/Bottom - 0,1 U32 t0, t1, b0, b1; // Top edge skirt... // Index to the first vert of the top row. U32 startIndex = 0; // Index to the first vert of the skirt under the top row. U32 skirtStartIdx = smVBStride * smVBStride; // Step to go one vert to the right. U32 step = 1; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + i * step; // Should this square be skipped? if ( _isVertIndexEmpty(t0) ) continue; t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = b0; idxBuff[1] = t0; idxBuff[2] = t1; idxBuff[3] = b1; idxBuff[4] = b0; idxBuff[5] = t1; idxBuff += 6; maxIndex = b1; counter += 6; mTriCount += 2; } // Bottom edge skirt... // Index to the first vert of the bottom row. startIndex = smVBStride * smVBStride - smVBStride; // Index to the first vert of the skirt under the bottom row. skirtStartIdx = startIndex + smVBStride * 2; // Step to go one vert to the right. step = 1; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + ( i * step ); // Should this square be skipped? We actually need to test // the vertex one row down as it defines the empty state // for this square. if ( _isVertIndexEmpty( t0 - smVBStride ) ) continue; t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = t1; idxBuff[1] = t0; idxBuff[2] = b0; idxBuff[3] = t1; idxBuff[4] = b0; idxBuff[5] = b1; idxBuff += 6; maxIndex = b1; counter += 6; mTriCount += 2; } // Left edge skirt... // Index to the first vert of the left column. startIndex = 0; // Index to the first vert of the skirt under the left column. skirtStartIdx = smVBStride * smVBStride + smVBStride * 2; // Step to go one vert down. step = smVBStride; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + ( i * step ); // Should this square be skipped? if ( _isVertIndexEmpty(t0) ) continue; t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = t1; idxBuff[1] = t0; idxBuff[2] = b0; idxBuff[3] = t1; idxBuff[4] = b0; idxBuff[5] = b1; idxBuff += 6; maxIndex = b1; counter += 6; mTriCount += 2; } // Right edge skirt... // Index to the first vert of the right column. startIndex = smVBStride - 1; // Index to the first vert of the skirt under the right column. skirtStartIdx = smVBStride * smVBStride + smVBStride * 3; // Step to go one vert down. step = smVBStride; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + ( i * step ); // Should this square be skipped? We actually need to test // the vertex one column to the left as it defines the empty // state for this square. if ( _isVertIndexEmpty( t0 - 1 ) ) continue; t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = b0; idxBuff[1] = t0; idxBuff[2] = t1; idxBuff[3] = b1; idxBuff[4] = b0; idxBuff[5] = t1; idxBuff += 6; maxIndex = b1; counter += 6; mTriCount += 2; } mPrimBuffer.unlock(); prim->numPrimitives = mTriCount; }
bool ProcessedShaderMaterial::stepInstance() { PROFILE_SCOPE(ProcessedShaderMaterial_stepInstance); AssertFatal( mInstancingState, "ProcessedShaderMaterial::stepInstance - This material isn't instanced!" ); return mInstancingState->step( &_getShaderConstBuffer( 0 )->mInstPtr ); }
void ProcessedShaderMaterial::setTextureStages( SceneRenderState *state, const SceneData &sgData, U32 pass ) { PROFILE_SCOPE( ProcessedShaderMaterial_SetTextureStages ); ShaderConstHandles *handles = _getShaderConstHandles(pass); // Set all of the textures we need to render the give pass. #ifdef TORQUE_DEBUG AssertFatal( pass<mPasses.size(), "Pass out of bounds" ); #endif RenderPassData *rpd = mPasses[pass]; GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); NamedTexTarget *texTarget; GFXTextureObject *texObject; for( U32 i=0; i<rpd->mNumTex; i++ ) { U32 currTexFlag = rpd->mTexType[i]; if (!LIGHTMGR || !LIGHTMGR->setTextureStage(sgData, currTexFlag, i, shaderConsts, handles)) { switch( currTexFlag ) { // If the flag is unset then assume its just // a regular texture to set... nothing special. case 0: default: GFX->setTexture(i, rpd->mTexSlot[i].texObject); break; case Material::NormalizeCube: GFX->setCubeTexture(i, Material::GetNormalizeCube()); break; case Material::Lightmap: GFX->setTexture( i, sgData.lightmap ); break; case Material::ToneMapTex: shaderConsts->setSafe(handles->mToneMapTexSC, (S32)i); GFX->setTexture(i, rpd->mTexSlot[i].texObject); break; case Material::Cube: GFX->setCubeTexture( i, rpd->mCubeMap ); break; case Material::SGCube: GFX->setCubeTexture( i, sgData.cubemap ); break; case Material::BackBuff: GFX->setTexture( i, sgData.backBuffTex ); break; case Material::TexTarget: { texTarget = rpd->mTexSlot[i].texTarget; if ( !texTarget ) { GFX->setTexture( i, NULL ); break; } texObject = texTarget->getTexture(); // If no texture is available then map the default 2x2 // black texture to it. This at least will ensure that // we get consistant behavior across GPUs and platforms. if ( !texObject ) texObject = GFXTexHandle::ZERO; if ( handles->mRTParamsSC[i]->isValid() && texObject ) { const Point3I &targetSz = texObject->getSize(); const RectI &targetVp = texTarget->getViewport(); Point4F rtParams; ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); shaderConsts->set(handles->mRTParamsSC[i], rtParams); } GFX->setTexture( i, texObject ); break; } } } } }
void TSShapeInstance::animateVisibility(S32 ss) { PROFILE_SCOPE( TSShapeInstance_animateVisibility ); S32 i; if (!mMeshObjects.size()) return; // find out who needs default values set TSIntegerSet beenSet; beenSet.setAll(mMeshObjects.size()); for (i=0; i<mThreadList.size(); i++) beenSet.takeAway(mThreadList[i]->getSequence()->visMatters); // set defaults S32 a = mShape->subShapeFirstObject[ss]; S32 b = a + mShape->subShapeNumObjects[ss]; for (i=a; i<b; i++) { if (beenSet.test(i)) mMeshObjects[i].visible = mShape->objectStates[i].vis; } // go through each thread and set visibility on those objects that // are not set yet and are controlled by that thread for (i=0; i<mThreadList.size(); i++) { TSThread * th = mThreadList[i]; // For better or worse, object states are stored together (frame, // matFrame, visibility all in one structure). Thus, indexing into // object state array for animation for any of these attributes needs to // take into account whether or not the other attributes are also animated. // The object states should eventually be separated (like the node states were) // in order to save memory and save the following step. TSIntegerSet objectMatters = th->getSequence()->frameMatters; objectMatters.overlap(th->getSequence()->matFrameMatters); objectMatters.overlap(th->getSequence()->visMatters); // skip to beginning of this sub-shape S32 j=0; S32 start = objectMatters.start(); S32 end = b; for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++) { if (!beenSet.test(objectIndex) && th->getSequence()->visMatters.test(objectIndex)) { F32 state1 = mShape->getObjectState(*th->getSequence(),th->keyNum1,j).vis; F32 state2 = mShape->getObjectState(*th->getSequence(),th->keyNum2,j).vis; if ((state1-state2) * (state1-state2) > 0.99f) // goes from 0 to 1 -- discreet jump mMeshObjects[objectIndex].visible = th->keyPos<0.5f ? state1 : state2; else // interpolate between keyframes when visibility change is gradual mMeshObjects[objectIndex].visible = (1.0f-th->keyPos) * state1 + th->keyPos * state2; // record change so that later threads don't over-write us... beenSet.set(objectIndex); } } } }
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 ); } } }
bool ProjectedShadow::_updateDecal( const SceneRenderState *state ) { PROFILE_SCOPE( ProjectedShadow_UpdateDecal ); if ( !LIGHTMGR ) return false; // Get the position of the decal first. const Box3F &objBox = mParentObject->getObjBox(); const Point3F boxCenter = objBox.getCenter(); Point3F decalPos = boxCenter; const MatrixF &renderTransform = mParentObject->getRenderTransform(); { // Set up the decal position. // We use the object space box center // multiplied by the render transform // of the object to ensure we benefit // from interpolation. MatrixF t( renderTransform ); t.setColumn(2,Point3F::UnitZ); t.mulP( decalPos ); } if ( mDecalInstance ) { mDecalInstance->mPosition = decalPos; if ( !shouldRender( state ) ) return false; } // Get the sunlight for the shadow projection. // We want the LightManager to return NULL if it can't // get the "real" sun, so we specify false for the useDefault parameter. LightInfo *lights[4] = {0}; LightQuery query; query.init( mParentObject->getWorldSphere() ); query.getLights( lights, 4 ); Point3F pos = renderTransform.getPosition(); Point3F lightDir( 0, 0, 0 ); Point3F tmp( 0, 0, 0 ); F32 weight = 0; F32 range = 0; U32 lightCount = 0; F32 dist = 0; F32 fade = 0; for ( U32 i = 0; i < 4; i++ ) { // If we got a NULL light, // we're at the end of the list. if ( !lights[i] ) break; if ( !lights[i]->getCastShadows() ) continue; if ( lights[i]->getType() != LightInfo::Point ) tmp = lights[i]->getDirection(); else tmp = pos - lights[i]->getPosition(); range = lights[i]->getRange().x; dist = ( (tmp.lenSquared()) / ((range * range) * 0.5f)); weight = mClampF( 1.0f - ( tmp.lenSquared() / (range * range)), 0.00001f, 1.0f ); if ( lights[i]->getType() == LightInfo::Vector ) fade = getMax( fade, 1.0f ); else fade = getMax( fade, mClampF( 1.0f - dist, 0.00001f, 1.0f ) ); lightDir += tmp * weight; lightCount++; } lightDir.normalize(); // No light... no shadow. if ( !lights[0] ) return false; // Has the light direction // changed since last update? bool lightDirChanged = !mLastLightDir.equal( lightDir ); // Has the parent object moved // or scaled since the last update? bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() ); bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() ); // Set the last light direction // to the current light direction. mLastLightDir = lightDir; mLastObjectPosition = mParentObject->getRenderPosition(); mLastObjectScale = mParentObject->getScale(); // Temps used to generate // tangent vector for DecalInstance below. VectorF right( 0, 0, 0 ); VectorF fwd( 0, 0, 0 ); VectorF tmpFwd( 0, 0, 0 ); U32 idx = lightDir.getLeastComponentIndex(); tmpFwd[idx] = 1.0f; right = mCross( tmpFwd, lightDir ); fwd = mCross( lightDir, right ); right = mCross( fwd, lightDir ); right.normalize(); // Set up the world to light space // matrix, along with proper position // and rotation to be used as the world // matrix for the render to texture later on. static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f)); mWorldToLight.identity(); MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight ); mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) ); mWorldToLight.mul( sRotMat ); mWorldToLight.inverse(); // Get the shapebase datablock if we have one. ShapeBaseData *data = NULL; if ( mShapeBase ) data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() ); // We use the object box's extents multiplied // by the object's scale divided by 2 for the radius // because the object's worldsphere radius is not // rotationally invariant. mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f; if ( data ) mRadius *= data->shadowSphereAdjust; // Create the decal if we don't have one yet. if ( !mDecalInstance ) mDecalInstance = gDecalManager->addDecal( decalPos, lightDir, right, mDecalData, 1.0f, 0, PermanentDecal | ClipDecal | CustomDecal ); if ( !mDecalInstance ) return false; mDecalInstance->mVisibility = fade; // Setup decal parameters. mDecalInstance->mSize = mRadius * 2.0f; mDecalInstance->mNormal = -lightDir; mDecalInstance->mTangent = -right; mDecalInstance->mRotAroundNormal = 0; mDecalInstance->mPosition = decalPos; mDecalInstance->mDataBlock = mDecalData; // If the position of the world // space box center is the same // as the decal's position, and // the light direction has not // changed, we don't need to clip. bool shouldClip = lightDirChanged || hasMoved || hasScaled; // Now, check and see if the object is visible. const Frustum &frust = state->getCullingFrustum(); if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip ) return false; F32 shadowLen = 10.0f; if ( data ) shadowLen = data->shadowProjectionDistance; const Point3F &boxExtents = objBox.getExtents(); mShadowLength = shadowLen * mParentObject->getScale().z; // Set up clip depth, and box half // offset for decal clipping. Point2F clipParams( mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f ); bool render = false; bool clipSucceeded = true; // Clip! if ( shouldClip ) { clipSucceeded = gDecalManager->clipDecal( mDecalInstance, NULL, &clipParams ); } // If the clip failed, // we'll return false in // order to keep from // unnecessarily rendering // into the texture. If // there was no reason to clip // on this update, we'll assume we // should update the texture. render = clipSucceeded; // Tell the DecalManager we've changed this decal. gDecalManager->notifyDecalModified( mDecalInstance ); return render; }
void TSShapeInstance::sortThreads() { PROFILE_SCOPE( TSShapeInstance_sortThreads ); dQsort(mThreadList.address(),mThreadList.size(),sizeof(TSThread*),compareThreads); dQsort(mTransitionThreads.address(),mTransitionThreads.size(),sizeof(TSThread*),compareThreads); }
void LightManager::registerGlobalLights( const Frustum *frustum, bool staticLighting ) { PROFILE_SCOPE( LightManager_RegisterGlobalLights ); // TODO: We need to work this out... // // 1. Why do we register and unregister lights on every // render when they don't often change... shouldn't we // just register once and keep them? // // 2. If we do culling of lights should this happen as part // of registration or somewhere else? // // Grab the lights to process. Vector<SceneObject*> activeLights; const U32 lightMask = LightObjectType; if ( staticLighting || !frustum ) { // We're processing static lighting or want all the lights // in the container registerd... so no culling. getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights ); } else { // Cull the lights using the frustum. getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights ); for (U32 i = 0; i < activeLights.size(); ++i) { if (!getSceneManager()->mRenderedObjectsList.contains(activeLights[i])) { activeLights.erase(i); --i; } } // Store the culling position for sun placement // later... see setSpecialLight. mCullPos = frustum->getPosition(); // HACK: Make sure the control object always gets // processed as lights mounted to it don't change // the shape bounds and can often get culled. GameConnection *conn = GameConnection::getConnectionToServer(); if ( conn->getControlObject() ) { GameBase *conObject = conn->getControlObject(); activeLights.push_back_unique( conObject ); } } // Let the lights register themselves. for ( U32 i = 0; i < activeLights.size(); i++ ) { ISceneLight *lightInterface = dynamic_cast<ISceneLight*>( activeLights[i] ); if ( lightInterface ) lightInterface->submitLights( this, staticLighting ); } }
void LightManager::_update4LightConsts( const SceneData &sgData, GFXShaderConstHandle *lightPositionSC, GFXShaderConstHandle *lightDiffuseSC, GFXShaderConstHandle *lightAmbientSC, GFXShaderConstHandle *lightInvRadiusSqSC, GFXShaderConstHandle *lightSpotDirSC, GFXShaderConstHandle *lightSpotAngleSC, GFXShaderConstHandle *lightSpotFalloffSC, GFXShaderConstBuffer *shaderConsts ) { PROFILE_SCOPE( LightManager_Update4LightConsts ); // Skip over gathering lights if we don't have to! if ( lightPositionSC->isValid() || lightDiffuseSC->isValid() || lightInvRadiusSqSC->isValid() || lightSpotDirSC->isValid() || lightSpotAngleSC->isValid() || lightSpotFalloffSC->isValid() ) { PROFILE_SCOPE( LightManager_Update4LightConsts_setLights ); static AlignedArray<Point4F> lightPositions( 3, sizeof( Point4F ) ); static AlignedArray<Point4F> lightSpotDirs( 3, sizeof( Point4F ) ); static AlignedArray<Point4F> lightColors( 4, sizeof( Point4F ) ); static Point4F lightInvRadiusSq; static Point4F lightSpotAngle; static Point4F lightSpotFalloff; F32 range; // Need to clear the buffers so that we don't leak // lights from previous passes or have NaNs. dMemset( lightPositions.getBuffer(), 0, lightPositions.getBufferSize() ); dMemset( lightSpotDirs.getBuffer(), 0, lightSpotDirs.getBufferSize() ); dMemset( lightColors.getBuffer(), 0, lightColors.getBufferSize() ); lightInvRadiusSq = Point4F::Zero; lightSpotAngle.set( -1.0f, -1.0f, -1.0f, -1.0f ); lightSpotFalloff.set( F32_MAX, F32_MAX, F32_MAX, F32_MAX ); // Gather the data for the first 4 lights. const LightInfo *light; for ( U32 i=0; i < 4; i++ ) { light = sgData.lights[i]; if ( !light ) break; // The light positions and spot directions are // in SoA order to make optimal use of the GPU. const Point3F &lightPos = light->getPosition(); lightPositions[0][i] = lightPos.x; lightPositions[1][i] = lightPos.y; lightPositions[2][i] = lightPos.z; const VectorF &lightDir = light->getDirection(); lightSpotDirs[0][i] = lightDir.x; lightSpotDirs[1][i] = lightDir.y; lightSpotDirs[2][i] = lightDir.z; if ( light->getType() == LightInfo::Spot ) { lightSpotAngle[i] = mCos( mDegToRad( light->getOuterConeAngle() / 2.0f ) ); lightSpotFalloff[i] = 1.0f / getMax( F32_MIN, mCos( mDegToRad( light->getInnerConeAngle() / 2.0f ) ) - lightSpotAngle[i] ); } // Prescale the light color by the brightness to // avoid doing this in the shader. lightColors[i] = Point4F(light->getColor()) * light->getBrightness(); // We need 1 over range^2 here. range = light->getRange().x; lightInvRadiusSq[i] = 1.0f / ( range * range ); } shaderConsts->setSafe( lightPositionSC, lightPositions ); shaderConsts->setSafe( lightDiffuseSC, lightColors ); shaderConsts->setSafe( lightInvRadiusSqSC, lightInvRadiusSq ); shaderConsts->setSafe( lightSpotDirSC, lightSpotDirs ); shaderConsts->setSafe( lightSpotAngleSC, lightSpotAngle ); shaderConsts->setSafe( lightSpotFalloffSC, lightSpotFalloff ); } // Setup the ambient lighting from the first // light which is the directional light if // one exists at all in the scene. if ( lightAmbientSC->isValid() ) shaderConsts->set( lightAmbientSC, sgData.ambientLightColor ); }
//----------------------------------------------------------------------------- // render //----------------------------------------------------------------------------- void RenderMeshMgr::render(SceneRenderState * state) { PROFILE_SCOPE(RenderMeshMgr_render); // Early out if nothing to draw. if(!mElementList.size()) return; GFXDEBUGEVENT_SCOPE( RenderMeshMgr_Render, ColorI::GREEN ); // Automagically save & restore our viewport and transforms. GFXTransformSaver saver; // Restore transforms MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); matrixSet.restoreSceneViewProjection(); // init loop data GFXTextureObject *lastLM = NULL; GFXCubemap *lastCubemap = NULL; GFXTextureObject *lastReflectTex = NULL; GFXTextureObject *lastMiscTex = NULL; GFXTextureObject *lastAccuTex = NULL; SceneData sgData; sgData.init( state ); U32 binSize = mElementList.size(); for( U32 j=0; j<binSize; ) { MeshRenderInst *ri = static_cast<MeshRenderInst*>(mElementList[j].inst); setupSGData( ri, sgData ); BaseMatInstance *mat = ri->matInst; // If we have an override delegate then give it a // chance to swap the material with another. if ( mMatOverrideDelegate ) { mat = mMatOverrideDelegate( mat ); if ( !mat ) { j++; continue; } } if( !mat ) mat = MATMGR->getWarningMatInstance(); U32 matListEnd = j; lastMiscTex = sgData.miscTex; U32 a; while(mat && mat->setupPass(state, sgData )) { for( a=j; a<binSize; a++ ) { MeshRenderInst *passRI = static_cast<MeshRenderInst*>(mElementList[a].inst); // Check to see if we need to break this batch. if ( newPassNeeded( ri, passRI ) || lastMiscTex != passRI->miscTex ) { lastLM = NULL; break; } matrixSet.setWorld(*passRI->objectToWorld); matrixSet.setView(*passRI->worldToCamera); matrixSet.setProjection(*passRI->projection); mat->setTransforms(matrixSet, state); setupSGData( passRI, sgData ); sgData.palette = passRI->palette; mat->setSceneInfo( state, sgData ); // If we're instanced then don't render yet. if ( mat->isInstanced() ) { // Let the material increment the instance buffer, but // break the batch if it runs out of room for more. if ( !mat->stepInstance() ) { a++; break; } continue; } // TODO: This could proably be done in a cleaner way. // // This section of code is dangerous, it overwrites the // lightmap values in sgData. This could be a problem when multiple // render instances use the same multi-pass material. When // the first pass is done, setupPass() is called again on // the material, but the lightmap data has been changed in // sgData to the lightmaps in the last renderInstance rendered. // This section sets the lightmap data for the current batch. // For the first iteration, it sets the same lightmap data, // however the redundancy will be caught by GFXDevice and not // actually sent to the card. This is done for simplicity given // the possible condition mentioned above. Better to set always // than to get bogged down into special case detection. //------------------------------------- bool dirty = false; // set the lightmaps if different if( passRI->lightmap && passRI->lightmap != lastLM ) { sgData.lightmap = passRI->lightmap; lastLM = passRI->lightmap; dirty = true; } // set the cubemap if different. if ( passRI->cubemap != lastCubemap ) { sgData.cubemap = passRI->cubemap; lastCubemap = passRI->cubemap; dirty = true; } if ( passRI->reflectTex != lastReflectTex ) { sgData.reflectTex = passRI->reflectTex; lastReflectTex = passRI->reflectTex; dirty = true; } // Update accumulation texture if it changed. // Note: accumulation texture can be NULL, and must be updated. if ( passRI->accuTex != lastAccuTex ) { sgData.accuTex = passRI->accuTex; lastAccuTex = lastAccuTex; dirty = true; } if ( dirty ) mat->setTextureStages( state, sgData ); // Setup the vertex and index buffers. mat->setBuffers( passRI->vertBuff, passRI->primBuff ); // Render this sucker. if ( passRI->prim ) GFX->drawPrimitive( *passRI->prim ); else GFX->drawPrimitive( passRI->primBuffIndex ); } // Draw the instanced batch. if ( mat->isInstanced() ) { // Sets the buffers including the instancing stream. mat->setBuffers( ri->vertBuff, ri->primBuff ); // Render the instanced stream. if ( ri->prim ) GFX->drawPrimitive( *ri->prim ); else GFX->drawPrimitive( ri->primBuffIndex ); } matListEnd = a; } // force increment if none happened, otherwise go to end of batch j = ( j == matListEnd ) ? j+1 : matListEnd; } }
void Portal::_traverseConnectedZoneSpaces( SceneTraversalState* state ) { PROFILE_SCOPE( Portal_traverseConnectedZoneSpaces ); // Don't traverse out from the portal if it is invalid. if( mClassification == InvalidPortal ) return; AssertFatal( !mIsGeometryDirty, "Portal::_traverseConnectedZoneSpaces - Geometry not up-to-date!" ); // When starting traversal within a portal zone, we cannot really use the portal // plane itself to direct our visibility queries. For example, the camera might // actually be located in front of the portal plane and thus cannot actually look // through the portal, though it will still see what lies on front of where the // portal leads. // // So if we're the start of the traversal chain, i.e. the traversal has started // out in the portal zone, then just put the traversal through to SceneZoneSpace // so it can hand it over to all connected zone managers. // // Otherwise, just do a normal traversal by stepping through the portal. if( state->getTraversalDepth() == 1 ) { Parent::_traverseConnectedZoneSpaces( state ); return; } SceneCullingState* cullingState = state->getCullingState(); const SceneCameraState& cameraState = cullingState->getCameraState(); // Get the data of the zone we're coming from. Note that at this point // the portal zone itself is already on top of the traversal stack, so // we skip over the bottom-most entry. const U32 sourceZoneId = state->getZoneIdFromStack( 1 ); const SceneZoneSpace* sourceZoneSpace = state->getZoneFromStack( 1 ); // Find out which side of the portal we are on given the // source zone. const Portal::Side currentZoneSide = sourceZoneId == SceneZoneSpaceManager::RootZoneId ? ( getInteriorSideOfExteriorPortal() == FrontSide ? BackSide : FrontSide ) : getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() ); // Don't step through portal if the side we're interested in isn't passable. if( !isSidePassable( currentZoneSide ) ) return; // If the viewpoint isn't on the same side of the portal as the source zone, // then stepping through the portal would mean we are stepping back towards // the viewpoint which doesn't make sense; so, skip the portal. const Point3F& viewPos = cameraState.getViewPosition(); const F32 viewPosDistToPortal = mFabs( getPortalPlane().distToPlane( viewPos ) ); if( !mIsZero( viewPosDistToPortal ) && getSideRelativeToPortalPlane( viewPos ) != currentZoneSide ) return; // Before we go ahead and do the real work, try to find out whether // the portal is at a perpendicular or near-perpendicular angle to the view // direction. If so, there's no point in going further since we can't really // see much through the portal anyway. It also prevents us from stepping // over front/back side ambiguities. Point3F viewDirection = cameraState.getViewDirection(); const F32 dotProduct = mDot( viewDirection, getPortalPlane() ); if( mIsZero( dotProduct ) ) return; // Finally, if we have come through a portal to the current zone, check if the target // portal we are trying to step through now completely lies on the "backside"--i.e. // the side of portal on which our current zone lies--of the source portal. If so, // we can be sure this portal leads us in the wrong direction. This prevents the // outdoor zone from having just arrived through a window on one side of a house just // to go round the house and re-enter it from the other side. Portal* sourcePortal = state->getTraversalDepth() > 2 ? dynamic_cast< Portal* >( state->getZoneFromStack( 2 ) ) : NULL; if( sourcePortal != NULL ) { const Side sourcePortalFrontSide = sourceZoneId == SceneZoneSpaceManager::RootZoneId ? sourcePortal->getInteriorSideOfExteriorPortal() : sourcePortal->getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() ); const PlaneF::Side sourcePortalPlaneFrontSide = sourcePortalFrontSide == FrontSide ? PlaneF::Front : PlaneF::Back; bool allPortalVerticesOnBackside = true; const U32 numVertices = mPortalPolygonWS.size(); for( U32 i = 0; i < numVertices; ++ i ) { // Not using getSideRelativeToPortalPlane here since we want PlaneF::On to be // counted as backside here. if( sourcePortal->mPortalPlane.whichSide( mPortalPolygonWS[ i ] ) == sourcePortalPlaneFrontSide ) { allPortalVerticesOnBackside = false; break; } } if( allPortalVerticesOnBackside ) return; } // If we come from the outdoor zone, then we don't want to step through any portal // where the interior zones are actually on the same side as our camera since that // would mean we are stepping into an interior through the backside of a portal. if( sourceZoneId == SceneZoneSpaceManager::RootZoneId ) { const Portal::Side cameraSide = getSideRelativeToPortalPlane( viewPos ); if( cameraSide == getInteriorSideOfExteriorPortal() ) return; } // Clip the current culling volume against the portal's polygon. If the polygon // lies completely outside the volume or for some other reason there's no good resulting // volume, _generateCullingVolume() will return false and we terminate this portal sequence // here. // // However, don't attempt to clip the portal if we are standing really close to or // even below the near distance away from the portal plane. In that case, trying to // clip the portal will only result in trouble so we stick to the original culling volume // in that case. bool haveClipped = false; if( viewPosDistToPortal > ( cameraState.getFrustum().getNearDist() + 0.1f ) ) { SceneCullingVolume volume; if( !_generateCullingVolume( state, volume ) ) return; state->pushCullingVolume( volume ); haveClipped = true; } // Short-circuit things if we are stepping from an interior zone outside. In this // case we know that the only zone we care about is the outdoor zone so head straight // into it. if( isExteriorPortal() && sourceZoneId != SceneZoneSpaceManager::RootZoneId ) getSceneManager()->getZoneManager()->getRootZone()->traverseZones( state ); else { // Go through the zones that the portal connects to and // traverse into them. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) { SceneZoneSpace* targetSpace = ref->mZoneSpace; if( targetSpace == sourceZoneSpace ) continue; // Skip space we originated from. // We have handled the case of stepping into the outdoor zone above and // by skipping the zone we originated from, we have implicitly handled the // case of stepping out of the outdoor zone. Thus, we should not see the // outdoor zone here. Important as getPosition() is meaningless for it. AssertFatal( targetSpace->getZoneRangeStart() != SceneZoneSpaceManager::RootZoneId, "Portal::_traverseConnectedZoneSpaces - Outdoor zone must have been handled already" ); // Skip zones that lie on the same side as the zone // we originated from. if( getSideRelativeToPortalPlane( targetSpace->getPosition() ) == currentZoneSide ) continue; // Traverse into the space. targetSpace->traverseZones( state ); } } // If we have pushed our own clipping volume, // remove that from the stack now. if( haveClipped ) state->popCullingVolume(); }
void ConvexShape::Geometry::generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents ) { PROFILE_SCOPE( Geometry_generate ); points.clear(); faces.clear(); AssertFatal( planes.size() == tangents.size(), "ConvexShape - incorrect plane/tangent count." ); #ifdef TORQUE_ENABLE_ASSERTS for ( S32 i = 0; i < planes.size(); i++ ) { F32 dt = mDot( planes[i], tangents[i] ); AssertFatal( mIsZero( dt, 0.0001f ), "ConvexShape - non perpendicular input vectors." ); AssertFatal( planes[i].isUnitLength() && tangents[i].isUnitLength(), "ConvexShape - non unit length input vector." ); } #endif const U32 planeCount = planes.size(); Point3F linePt, lineDir; for ( S32 i = 0; i < planeCount; i++ ) { Vector< MathUtils::Line > collideLines; // Find the lines defined by the intersection of this plane with all others. for ( S32 j = 0; j < planeCount; j++ ) { if ( i == j ) continue; if ( planes[i].intersect( planes[j], linePt, lineDir ) ) { collideLines.increment(); MathUtils::Line &line = collideLines.last(); line.origin = linePt; line.direction = lineDir; } } if ( collideLines.empty() ) continue; // Find edges and points defined by the intersection of these lines. // As we find them we fill them into our working ConvexShape::Face // structure. Face newFace; for ( S32 j = 0; j < collideLines.size(); j++ ) { Vector< Point3F > collidePoints; for ( S32 k = 0; k < collideLines.size(); k++ ) { if ( j == k ) continue; MathUtils::LineSegment segment; MathUtils::mShortestSegmentBetweenLines( collideLines[j], collideLines[k], &segment ); F32 dist = ( segment.p0 - segment.p1 ).len(); if ( dist < 0.0005f ) { S32 l = 0; for ( ; l < planeCount; l++ ) { if ( planes[l].whichSide( segment.p0 ) == PlaneF::Front ) break; } if ( l == planeCount ) collidePoints.push_back( segment.p0 ); } } //AssertFatal( collidePoints.size() <= 2, "A line can't collide with more than 2 other lines in a convex shape..." ); if ( collidePoints.size() != 2 ) continue; // Push back collision points into our points vector // if they are not duplicates and determine the id // index for those points to be used by Edge(s). const Point3F &pnt0 = collidePoints[0]; const Point3F &pnt1 = collidePoints[1]; S32 idx0 = -1; S32 idx1 = -1; for ( S32 k = 0; k < points.size(); k++ ) { if ( pnt0.equal( points[k] ) ) { idx0 = k; break; } } for ( S32 k = 0; k < points.size(); k++ ) { if ( pnt1.equal( points[k] ) ) { idx1 = k; break; } } if ( idx0 == -1 ) { points.push_back( pnt0 ); idx0 = points.size() - 1; } if ( idx1 == -1 ) { points.push_back( pnt1 ); idx1 = points.size() - 1; } // Construct the Face::Edge defined by this collision. S32 localIdx0 = newFace.points.push_back_unique( idx0 ); S32 localIdx1 = newFace.points.push_back_unique( idx1 ); newFace.edges.increment(); ConvexShape::Edge &newEdge = newFace.edges.last(); newEdge.p0 = localIdx0; newEdge.p1 = localIdx1; } if ( newFace.points.size() < 3 ) continue; //AssertFatal( newFace.points.size() == newFace.edges.size(), "ConvexShape - face point count does not equal edge count." ); // Fill in some basic Face information. newFace.id = i; newFace.normal = planes[i]; newFace.tangent = tangents[i]; // Make a working array of Point3Fs on this face. U32 pntCount = newFace.points.size(); Point3F *workPoints = new Point3F[ pntCount ]; for ( S32 j = 0; j < pntCount; j++ ) workPoints[j] = points[ newFace.points[j] ]; // Calculate the average point for calculating winding order. Point3F averagePnt = Point3F::Zero; for ( S32 j = 0; j < pntCount; j++ ) averagePnt += workPoints[j]; averagePnt /= pntCount; // Sort points in correct winding order. U32 *vertMap = new U32[pntCount]; MatrixF quadMat( true ); quadMat.setPosition( averagePnt ); quadMat.setColumn( 0, newFace.tangent ); quadMat.setColumn( 1, mCross( newFace.normal, newFace.tangent ) ); quadMat.setColumn( 2, newFace.normal ); quadMat.inverse(); // Transform working points into quad space // so we can work with them as 2D points. for ( S32 j = 0; j < pntCount; j++ ) quadMat.mulP( workPoints[j] ); MathUtils::sortQuadWindingOrder( true, workPoints, vertMap, pntCount ); // Save points in winding order. for ( S32 j = 0; j < pntCount; j++ ) newFace.winding.push_back( vertMap[j] ); // Calculate the area and centroid of the face. newFace.area = 0.0f; for ( S32 j = 0; j < pntCount; j++ ) { S32 k = ( j + 1 ) % pntCount; const Point3F &p0 = workPoints[ vertMap[j] ]; const Point3F &p1 = workPoints[ vertMap[k] ]; // Note that this calculation returns positive area for clockwise winding // and negative area for counterclockwise winding. newFace.area += p0.y * p1.x; newFace.area -= p0.x * p1.y; } //AssertFatal( newFace.area > 0.0f, "ConvexShape - face area was not positive." ); if ( newFace.area > 0.0f ) newFace.area /= 2.0f; F32 factor; F32 cx = 0.0f, cy = 0.0f; for ( S32 j = 0; j < pntCount; j++ ) { S32 k = ( j + 1 ) % pntCount; const Point3F &p0 = workPoints[ vertMap[j] ]; const Point3F &p1 = workPoints[ vertMap[k] ]; factor = p0.x * p1.y - p1.x * p0.y; cx += ( p0.x + p1.x ) * factor; cy += ( p0.y + p1.y ) * factor; } factor = 1.0f / ( newFace.area * 6.0f ); newFace.centroid.set( cx * factor, cy * factor, 0.0f ); quadMat.inverse(); quadMat.mulP( newFace.centroid ); delete [] workPoints; workPoints = NULL; // Make polygons / triangles for this face. const U32 polyCount = pntCount - 2; newFace.triangles.setSize( polyCount ); for ( S32 j = 0; j < polyCount; j++ ) { ConvexShape::Triangle &poly = newFace.triangles[j]; poly.p0 = vertMap[0]; if ( j == 0 ) { poly.p1 = vertMap[ 1 ]; poly.p2 = vertMap[ 2 ]; } else { poly.p1 = vertMap[ 1 + j ]; poly.p2 = vertMap[ 2 + j ]; } } delete [] vertMap; // Calculate texture coordinates for each point in this face. const Point3F binormal = mCross( newFace.normal, newFace.tangent ); PlaneF planey( newFace.centroid - 0.5f * binormal, binormal ); PlaneF planex( newFace.centroid - 0.5f * newFace.tangent, newFace.tangent ); newFace.texcoords.setSize( newFace.points.size() ); for ( S32 j = 0; j < newFace.points.size(); j++ ) { F32 x = planex.distToPlane( points[ newFace.points[ j ] ] ); F32 y = planey.distToPlane( points[ newFace.points[ j ] ] ); newFace.texcoords[j].set( -x, -y ); } // Data verification tests. #ifdef TORQUE_ENABLE_ASSERTS //S32 triCount = newFace.triangles.size(); //S32 edgeCount = newFace.edges.size(); //AssertFatal( triCount == edgeCount - 2, "ConvexShape - triangle/edge count do not match." ); /* for ( S32 j = 0; j < triCount; j++ ) { F32 area = MathUtils::mTriangleArea( points[ newFace.points[ newFace.triangles[j][0] ] ], points[ newFace.points[ newFace.triangles[j][1] ] ], points[ newFace.points[ newFace.triangles[j][2] ] ] ); AssertFatal( area > 0.0f, "ConvexShape - triangle winding bad." ); }*/ #endif // Done with this Face. faces.push_back( newFace ); } }
bool TerrainCellMaterial::setupPass( const SceneRenderState *state, const SceneData &sceneData ) { PROFILE_SCOPE( TerrainCellMaterial_SetupPass ); if ( mCurrPass >= mPasses.size() ) { mCurrPass = 0; return false; } Pass &pass = mPasses[mCurrPass]; _updateMaterialConsts( &pass ); if ( pass.baseTexMapConst->isValid() ) GFX->setTexture( pass.baseTexMapConst->getSamplerRegister(), mTerrain->mBaseTex.getPointer() ); if ( pass.layerTexConst->isValid() ) GFX->setTexture( pass.layerTexConst->getSamplerRegister(), mTerrain->mLayerTex.getPointer() ); if ( pass.lightMapTexConst->isValid() ) GFX->setTexture( pass.lightMapTexConst->getSamplerRegister(), mTerrain->getLightMapTex() ); if ( sceneData.wireframe ) GFX->setStateBlock( pass.wireframeStateBlock ); else GFX->setStateBlock( pass.stateBlock ); GFX->setShader( pass.shader ); GFX->setShaderConstBuffer( pass.consts ); // Let the light manager prepare any light stuff it needs. LIGHTMGR->setLightInfo( NULL, NULL, sceneData, state, mCurrPass, pass.consts ); for ( U32 i=0; i < pass.materials.size(); i++ ) { MaterialInfo *matInfo = pass.materials[i]; if ( matInfo->detailTexConst->isValid() ) GFX->setTexture( matInfo->detailTexConst->getSamplerRegister(), matInfo->detailTex ); if ( matInfo->macroTexConst->isValid() ) GFX->setTexture( matInfo->macroTexConst->getSamplerRegister(), matInfo->macroTex ); if ( matInfo->normalTexConst->isValid() ) GFX->setTexture( matInfo->normalTexConst->getSamplerRegister(), matInfo->normalTex ); } pass.consts->setSafe( pass.layerSizeConst, (F32)mTerrain->mLayerTex.getWidth() ); if ( pass.oneOverTerrainSize->isValid() ) { F32 oneOverTerrainSize = 1.0f / mTerrain->getWorldBlockSize(); pass.consts->set( pass.oneOverTerrainSize, oneOverTerrainSize ); } pass.consts->setSafe( pass.squareSize, mTerrain->getSquareSize() ); if ( pass.fogDataConst->isValid() ) { Point3F fogData; fogData.x = sceneData.fogDensity; fogData.y = sceneData.fogDensityOffset; fogData.z = sceneData.fogHeightFalloff; pass.consts->set( pass.fogDataConst, fogData ); } pass.consts->setSafe( pass.fogColorConst, sceneData.fogColor ); if ( pass.lightInfoBufferConst->isValid() && pass.lightParamsConst->isValid() ) { if ( !mLightInfoTarget ) mLightInfoTarget = NamedTexTarget::find( "lightinfo" ); GFXTextureObject *texObject = mLightInfoTarget->getTexture(); // TODO: Sometimes during reset of the light manager we get a // NULL texture here. This is corrected on the next frame, but // we should still investigate why that happens. if ( texObject ) { GFX->setTexture( pass.lightInfoBufferConst->getSamplerRegister(), texObject ); const Point3I &targetSz = texObject->getSize(); const RectI &targetVp = mLightInfoTarget->getViewport(); Point4F rtParams; ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); pass.consts->setSafe( pass.lightParamsConst, rtParams ); } } ++mCurrPass; return true; }
void TerrainCellMaterial::_updateMaterialConsts( Pass *pass ) { PROFILE_SCOPE( TerrainCellMaterial_UpdateMaterialConsts ); for ( U32 j=0; j < pass->materials.size(); j++ ) { MaterialInfo *matInfo = pass->materials[j]; F32 detailSize = matInfo->mat->getDetailSize(); F32 detailScale = 1.0f; if ( !mIsZero( detailSize ) ) detailScale = mTerrain->getWorldBlockSize() / detailSize; // Scale the distance by the global scalar. const F32 distance = mTerrain->smDetailScale * matInfo->mat->getDetailDistance(); // NOTE: The negation of the y scale is to make up for // my mistake early in development and passing the wrong // y texture coord into the system. // // This negation fixes detail, normal, and parallax mapping // without harming the layer id blending code. // // Eventually we should rework this to correct this little // mistake, but there isn't really a hurry to. // Point4F detailScaleAndFade( detailScale, -detailScale, distance, 0 ); if ( !mIsZero( distance ) ) detailScaleAndFade.w = 1.0f / distance; Point3F detailIdStrengthParallax( matInfo->layerId, matInfo->mat->getDetailStrength(), matInfo->mat->getParallaxScale() ); pass->consts->setSafe( matInfo->detailInfoVConst, detailScaleAndFade ); pass->consts->setSafe( matInfo->detailInfoPConst, detailIdStrengthParallax ); // macro texture info F32 macroSize = matInfo->mat->getMacroSize(); F32 macroScale = 1.0f; if ( !mIsZero( macroSize ) ) macroScale = mTerrain->getWorldBlockSize() / macroSize; // Scale the distance by the global scalar. const F32 macroDistance = mTerrain->smDetailScale * matInfo->mat->getMacroDistance(); Point4F macroScaleAndFade( macroScale, -macroScale, macroDistance, 0 ); if ( !mIsZero( macroDistance ) ) macroScaleAndFade.w = 1.0f / macroDistance; Point3F macroIdStrengthParallax( matInfo->layerId, matInfo->mat->getMacroStrength(), 0 ); pass->consts->setSafe( matInfo->macroInfoVConst, macroScaleAndFade ); pass->consts->setSafe( matInfo->macroInfoPConst, macroIdStrengthParallax ); } }
bool Etherform::updatePos(const F32 travelTime) { PROFILE_SCOPE(Etherform_UpdatePos); getTransform().getColumn(3, &delta.posVec); // When mounted to another object, only Z rotation used. if(isMounted()) { mVelocity = mMount.object->getVelocity(); setPosition(Point3F(0.0f, 0.0f, 0.0f), mRot); setMaskBits(MoveMask); return true; } Point3F newPos; Collision col; dMemset(&col, 0, sizeof(col)); // DEBUG: //Point3F savedVelocity = mVelocity; if(mVelocity.isZero()) newPos = delta.posVec; else newPos = _move(travelTime, &col); _handleCollision(col); // DEBUG: //if ( isClientObject() ) // Con::printf( "(client) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z ); //else // Con::printf( "(server) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z ); // Set new position // If on the client, calc delta for backstepping if(isClientObject()) { delta.pos = newPos; delta.rot = mRot; delta.posVec = delta.posVec - delta.pos; delta.rotVec = delta.rotVec - delta.rot; delta.dt = 1.0f; } this->setPosition(newPos, mRot); this->setMaskBits(MoveMask); this->updateContainer(); if (!isGhost()) { // Collisions are only queued on the server and can be // generated by either updateMove or updatePos notifyCollision(); // Do mission area callbacks on the server as well //checkMissionArea(); } // Check the total distance moved. If it is more than 1000th of the velocity, then // we moved a fair amount... //if (totalMotion >= (0.001f * initialSpeed)) return true; //else //return false; }
void FrustumData::_update() const { if( !mDirty ) return; PROFILE_SCOPE( Frustum_update ); const Point3F& cameraPos = mPosition; // Build the frustum points in camera space first. if( mIsOrtho ) { mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop ); mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop ); mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom ); mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom ); mPoints[ FarTopLeft ].set( mNearLeft, mFarDist, mNearTop ); mPoints[ FarTopRight ].set( mNearRight, mFarDist, mNearTop ); mPoints[ FarBottomLeft ].set( mNearLeft, mFarDist, mNearBottom ); mPoints[ FarBottomRight ].set( mNearRight, mFarDist, mNearBottom ); } else { const F32 farOverNear = mFarDist / mNearDist; mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop ); mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop ); mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom ); mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom ); mPoints[ FarTopLeft ].set( mNearLeft * farOverNear, mFarDist, mNearTop * farOverNear ); mPoints[ FarTopRight ].set( mNearRight * farOverNear, mFarDist, mNearTop * farOverNear ); mPoints[ FarBottomLeft ].set( mNearLeft * farOverNear, mFarDist, mNearBottom * farOverNear ); mPoints[ FarBottomRight ].set( mNearRight * farOverNear, mFarDist, mNearBottom * farOverNear ); } // Transform the points into the desired culling space. for( U32 i = 0; i < mPoints.size(); ++ i ) mTransform.mulP( mPoints[ i ] ); // Update the axis aligned bounding box from // the newly transformed points. mBounds = Box3F::aroundPoints( mPoints.address(), mPoints.size() ); // Finally build the planes. if( mIsOrtho ) { mPlanes[ PlaneLeft ].set( mPoints[ NearBottomLeft ], mPoints[ FarTopLeft ], mPoints[ FarBottomLeft ] ); mPlanes[ PlaneRight ].set( mPoints[ NearTopRight ], mPoints[ FarBottomRight ], mPoints[ FarTopRight ] ); mPlanes[ PlaneTop ].set( mPoints[ FarTopRight ], mPoints[ NearTopLeft ], mPoints[ NearTopRight ] ); mPlanes[ PlaneBottom ].set( mPoints[ NearBottomRight ], mPoints[ FarBottomLeft ], mPoints[ FarBottomRight ] ); mPlanes[ PlaneNear ].set( mPoints[ NearTopLeft ], mPoints[ NearBottomLeft ], mPoints[ NearTopRight ] ); mPlanes[ PlaneFar ].set( mPoints[ FarTopLeft ], mPoints[ FarTopRight ], mPoints[ FarBottomLeft ] ); } else { mPlanes[ PlaneLeft ].set( cameraPos, mPoints[ NearTopLeft ], mPoints[ NearBottomLeft ] ); mPlanes[ PlaneRight ].set( cameraPos, mPoints[ NearBottomRight ], mPoints[ NearTopRight ] ); mPlanes[ PlaneTop ].set( cameraPos, mPoints[ NearTopRight ], mPoints[ NearTopLeft ] ); mPlanes[ PlaneBottom ].set( cameraPos, mPoints[ NearBottomLeft ], mPoints[ NearBottomRight ] ); mPlanes[ PlaneNear ].set( mPoints[ NearTopLeft ], mPoints[ NearBottomLeft ], mPoints[ NearTopRight ] ); mPlanes[ PlaneFar ].set( mPoints[ FarTopLeft ], mPoints[ FarTopRight ], mPoints[ FarBottomLeft ] ); } // If the frustum plane orientation doesn't match mIsInverted // now, invert all the plane normals. // // Note that if we have a transform matrix with a negative scale, // then the initial planes we have computed will always be inverted. const bool inverted = mPlanes[ PlaneNear ].whichSide( cameraPos ) == PlaneF::Front; if( inverted != mIsInverted ) { for( U32 i = 0; i < mPlanes.size(); ++ i ) mPlanes[ i ].invert(); } AssertFatal( mPlanes[ PlaneNear ].whichSide( cameraPos ) != PlaneF::Front, "Frustum::_update - Viewpoint lies on front side of near plane!" ); // And now the center points which are mostly just used in debug rendering. mPlaneCenters[ PlaneLeftCenter ] = ( mPoints[ NearTopLeft ] + mPoints[ NearBottomLeft ] + mPoints[ FarTopLeft ] + mPoints[ FarBottomLeft ] ) / 4.0f; mPlaneCenters[ PlaneRightCenter ] = ( mPoints[ NearTopRight ] + mPoints[ NearBottomRight ] + mPoints[ FarTopRight ] + mPoints[ FarBottomRight ] ) / 4.0f; mPlaneCenters[ PlaneTopCenter ] = ( mPoints[ NearTopLeft ] + mPoints[ NearTopRight ] + mPoints[ FarTopLeft ] + mPoints[ FarTopRight ] ) / 4.0f; mPlaneCenters[ PlaneBottomCenter ] = ( mPoints[ NearBottomLeft ] + mPoints[ NearBottomRight ] + mPoints[ FarBottomLeft ] + mPoints[ FarBottomRight ] ) / 4.0f; mPlaneCenters[ PlaneNearCenter ] = ( mPoints[ NearTopLeft ] + mPoints[ NearTopRight ] + mPoints[ NearBottomLeft ] + mPoints[ NearBottomRight ] ) / 4.0f; mPlaneCenters[ PlaneFarCenter ] = ( mPoints[ FarTopLeft ] + mPoints[ FarTopRight ] + mPoints[ FarBottomLeft ] + mPoints[ FarBottomRight ] ) / 4.0f; // Done. mDirty = false; }
void ScatterSky::_getColor( const Point3F &pos, ColorF *outColor ) { PROFILE_SCOPE( ScatterSky_GetColor ); F32 scaleOverScaleDepth = mScale / mRayleighScaleDepth; F32 rayleighBrightness = mRayleighScattering * mSkyBrightness; F32 mieBrightness = mMieScattering * mSkyBrightness; Point3F invWaveLength( 1.0f / mWavelength4[0], 1.0f / mWavelength4[1], 1.0f / mWavelength4[2] ); Point3F v3Pos = pos / 6378000.0f; v3Pos.z += mSphereInnerRadius; Point3F newCamPos( 0, 0, smViewerHeight ); VectorF v3Ray = v3Pos - newCamPos; F32 fFar = v3Ray.len(); v3Ray / fFar; v3Ray.normalizeSafe(); Point3F v3Start = newCamPos; F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight ) ); F32 fStartAngle = mDot( v3Ray, v3Start ); F32 fStartOffset = fDepth * _vernierScale( fStartAngle ); F32 fSampleLength = fFar / 2.0f; F32 fScaledLength = fSampleLength * mScale; VectorF v3SampleRay = v3Ray * fSampleLength; Point3F v3SamplePoint = v3Start + v3SampleRay * 0.5f; Point3F v3FrontColor( 0, 0, 0 ); for ( U32 i = 0; i < 2; i++ ) { F32 fHeight = v3SamplePoint.len(); F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight) ); F32 fLightAngle = mDot( mLightDir, v3SamplePoint ) / fHeight; F32 fCameraAngle = mDot( v3Ray, v3SamplePoint ) / fHeight; F32 fScatter = (fStartOffset + fDepth * ( _vernierScale( fLightAngle ) - _vernierScale( fCameraAngle ) )); Point3F v3Attenuate( 0, 0, 0 ); F32 tmp = mExp( -fScatter * (invWaveLength[0] * mRayleighScattering4PI + mMieScattering4PI) ); v3Attenuate.x = tmp; tmp = mExp( -fScatter * (invWaveLength[1] * mRayleighScattering4PI + mMieScattering4PI) ); v3Attenuate.y = tmp; tmp = mExp( -fScatter * (invWaveLength[2] * mRayleighScattering4PI + mMieScattering4PI) ); v3Attenuate.z = tmp; v3FrontColor += v3Attenuate * (fDepth * fScaledLength); v3SamplePoint += v3SampleRay; } Point3F mieColor = v3FrontColor * mieBrightness; Point3F rayleighColor = v3FrontColor * (invWaveLength * rayleighBrightness); Point3F v3Direction = newCamPos - v3Pos; v3Direction.normalize(); F32 fCos = mDot( mLightDir, v3Direction ) / v3Direction.len(); F32 fCos2 = fCos * fCos; F32 g = -0.991f; F32 g2 = g * g; F32 miePhase = _getMiePhase( fCos, fCos2, g, g2 ); Point3F color = rayleighColor + (miePhase * mieColor); ColorF tmp( color.x, color.y, color.z, color.y ); Point3F expColor( 0, 0, 0 ); expColor.x = 1.0f - exp(-mExposure * color.x); expColor.y = 1.0f - exp(-mExposure * color.y); expColor.z = 1.0f - exp(-mExposure * color.z); tmp.set( expColor.x, expColor.y, expColor.z, 1.0f ); if ( !tmp.isValidColor() ) { F32 len = expColor.len(); if ( len > 0 ) expColor /= len; } outColor->set( expColor.x, expColor.y, expColor.z, 1.0f ); }
void ClippedPolyList::end() { PROFILE_SCOPE( ClippedPolyList_Clip ); Poly& poly = mPolyList.last(); // Reject polygons facing away from our normal. if ( mDot( poly.plane, mNormal ) < mNormalTolCosineRadians ) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } // Build initial inside/outside plane masks U32 indexStart = poly.vertexStart; U32 vertexCount = mIndexList.size() - indexStart; U32 frontMask = 0,backMask = 0; U32 i; for (i = indexStart; i < mIndexList.size(); i++) { U32 mask = mVertexList[mIndexList[i]].mask; frontMask |= mask; backMask |= ~mask; } // Trivial accept if all the vertices are on the backsides of // all the planes. if (!frontMask) { poly.vertexCount = vertexCount; return; } // Trivial reject if any plane not crossed has all it's points // on the front. U32 crossMask = frontMask & backMask; if (~crossMask & frontMask) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } // Potentially, this will add up to mPlaneList.size() * (indexStart - indexEnd) // elements to mIndexList, so ensure that it has enough space to store that // so we can use push_back_noresize. If you find this code block getting hit // frequently, changing the value of 'IndexListReserveSize' or doing some selective // allocation is suggested // // TODO: Re-visit this, since it obviously does not work correctly, and than // re-enable the push_back_noresize //while(mIndexList.size() + mPlaneList.size() * (mIndexList.size() - indexStart) > mIndexList.capacity() ) // mIndexList.reserve(mIndexList.capacity() * 2); // Need to do some clipping for (U32 p = 0; p < mPlaneList.size(); p++) { U32 pmask = 1 << p; // Only test against this plane if we have something // on both sides if (!(crossMask & pmask)) continue; U32 indexEnd = mIndexList.size(); U32 i1 = indexEnd - 1; U32 mask1 = mVertexList[mIndexList[i1]].mask; for (U32 i2 = indexStart; i2 < indexEnd; i2++) { U32 mask2 = mVertexList[mIndexList[i2]].mask; if ((mask1 ^ mask2) & pmask) { // mVertexList.increment(); VectorF& v1 = mVertexList[mIndexList[i1]].point; VectorF& v2 = mVertexList[mIndexList[i2]].point; VectorF vv = v2 - v1; F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv); mNormalList.increment(); VectorF& n1 = mNormalList[mIndexList[i1]]; VectorF& n2 = mNormalList[mIndexList[i1]]; VectorF nn = mLerp( n1, n2, t ); nn.normalizeSafe(); mNormalList.last() = nn; mIndexList.push_back/*_noresize*/(mVertexList.size() - 1); Vertex& iv = mVertexList.last(); iv.point.x = v1.x + vv.x * t; iv.point.y = v1.y + vv.y * t; iv.point.z = v1.z + vv.z * t; iv.mask = 0; // Test against the remaining planes for (U32 i = p + 1; i < mPlaneList.size(); i++) if (mPlaneList[i].distToPlane(iv.point) > 0) { iv.mask = 1 << i; break; } } if (!(mask2 & pmask)) { U32 index = mIndexList[i2]; mIndexList.push_back/*_noresize*/(index); } mask1 = mask2; i1 = i2; } // Check for degenerate indexStart = indexEnd; if (mIndexList.size() - indexStart < 3) { mIndexList.setSize(poly.vertexStart); mPolyList.decrement(); return; } } // Emit what's left and compress the index list. poly.vertexCount = mIndexList.size() - indexStart; memcpy(&mIndexList[poly.vertexStart], &mIndexList[indexStart],poly.vertexCount); mIndexList.setSize(poly.vertexStart + poly.vertexCount); }
void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata ) { PROFILE_SCOPE( ProjectedShadow_RenderToTexture ); GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) ); RenderPassManager *renderPass = _getRenderPass(); if ( !renderPass ) return; GFXTransformSaver saver; // NOTE: GFXTransformSaver does not save/restore the frustum // so we must save it here before we modify it. F32 l, r, b, t, n, f; bool ortho; GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho ); // Set the orthographic projection // matrix up, to be based on the radius // generated based on our shape. GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true ); // Set the world to light space // matrix set up in shouldRender(). GFX->setWorldMatrix( mWorldToLight ); // Get the shapebase datablock if we have one. ShapeBaseData *data = NULL; if ( mShapeBase ) data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() ); // Init or update the shadow texture size. if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) ) { U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar ); mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" ); } GFX->pushActiveRenderTarget(); if ( !mRenderTarget ) mRenderTarget = GFX->allocRenderToTextureTarget(); mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) ); mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture ); GFX->setActiveRenderTarget( mRenderTarget ); GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 ); const SceneRenderState *diffuseState = rdata.getSceneState(); SceneManager *sceneManager = diffuseState->getSceneManager(); SceneRenderState baseState ( sceneManager, SPT_Shadow, SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), renderPass ); baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial ); baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); baseState.getCullingState().disableZoneCulling( true ); mParentObject->prepRenderImage( &baseState ); renderPass->renderPass( &baseState ); // Delete the SceneRenderState we allocated. mRenderTarget->resolve(); GFX->popActiveRenderTarget(); // If we're close enough then filter the shadow. if ( camDist < BasicLightManager::getShadowFilterDistance() ) { if ( !smShadowFilter ) { PostEffect *filter = NULL; if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) ) Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" ); smShadowFilter = filter; } if ( smShadowFilter ) smShadowFilter->process( NULL, mShadowTexture ); } // Restore frustum if (!ortho) GFX->setFrustum(l, r, b, t, n, f); else GFX->setOrtho(l, r, b, t, n, f); // Set the last render time. mLastRenderTime = Platform::getVirtualMilliseconds(); // HACK: Will remove in future release! mDecalInstance->mCustomTex = &mShadowTexture; }
void ClippedPolyList::cullUnusedVerts() { PROFILE_SCOPE( ClippedPolyList_CullUnusedVerts ); U32 i = 0; U32 k, n, numDeleted; bool result; IndexListIterator iNextIter; VertexListIterator nextVIter; VertexListIterator vIter; for ( vIter = mVertexList.begin(); vIter != mVertexList.end(); vIter++, i++ ) { // Is this vertex used? iNextIter = find( mIndexList.begin(), mIndexList.end(), i ); if ( iNextIter != mIndexList.end() ) continue; // If not, find the next used vertex. // i is an unused vertex // k is a used vertex // delete the vertices from i to j - 1 k = 0; n = i + 1; result = false; numDeleted = 0; for ( nextVIter = vIter + 1; nextVIter != mVertexList.end(); nextVIter++, n++ ) { iNextIter = find( mIndexList.begin(), mIndexList.end(), n ); // If we found a used vertex // grab its index for later use // and set our result bool. if ( (*iNextIter) == n ) { k = n; result = true; break; } } // All the remaining verts are unused. if ( !result ) { mVertexList.setSize( i ); mNormalList.setSize( i ); break; } // Erase unused verts. numDeleted = (k-1) - i + 1; mVertexList.erase( i, numDeleted ); mNormalList.erase( i, numDeleted ); // Find any references to vertices after those deleted // in the mIndexList and correct with an offset for ( iNextIter = mIndexList.begin(); iNextIter != mIndexList.end(); iNextIter++ ) { if ( (*iNextIter) > i ) (*iNextIter) -= numDeleted; } // After the erase the current iter should // point at the used vertex we found... the // loop will continue with the next vert. } }
void TSShapeInstance::animateNodes(S32 ss) { PROFILE_SCOPE( TSShapeInstance_animateNodes ); if (!mShape->nodes.size()) return; // @todo: When a node is added, we need to make sure to resize the nodeTransforms array as well mNodeTransforms.setSize(mShape->nodes.size()); // temporary storage for node transforms smNodeCurrentRotations.setSize(mShape->nodes.size()); smNodeCurrentTranslations.setSize(mShape->nodes.size()); smNodeLocalTransforms.setSize(mShape->nodes.size()); smRotationThreads.setSize(mShape->nodes.size()); smTranslationThreads.setSize(mShape->nodes.size()); TSIntegerSet rotBeenSet; TSIntegerSet tranBeenSet; TSIntegerSet scaleBeenSet; rotBeenSet.setAll(mShape->nodes.size()); tranBeenSet.setAll(mShape->nodes.size()); scaleBeenSet.setAll(mShape->nodes.size()); smNodeLocalTransformDirty.clearAll(); S32 i,j,nodeIndex,a,b,start,end,firstBlend = mThreadList.size(); for (i=0; i<mThreadList.size(); i++) { TSThread * th = mThreadList[i]; if (th->getSequence()->isBlend()) { // blend sequences need default (if not set by other sequence) // break rather than continue because the rest will be blends too firstBlend = i; break; } rotBeenSet.takeAway(th->getSequence()->rotationMatters); tranBeenSet.takeAway(th->getSequence()->translationMatters); scaleBeenSet.takeAway(th->getSequence()->scaleMatters); } rotBeenSet.takeAway(mCallbackNodes); rotBeenSet.takeAway(mHandsOffNodes); rotBeenSet.overlap(mMaskRotationNodes); TSIntegerSet maskPosNodes=mMaskPosXNodes; maskPosNodes.overlap(mMaskPosYNodes); maskPosNodes.overlap(mMaskPosZNodes); tranBeenSet.overlap(maskPosNodes); tranBeenSet.takeAway(mCallbackNodes); tranBeenSet.takeAway(mHandsOffNodes); // can't add masked nodes since x, y, & z masked separately... // we'll set default regardless of mask status // all the nodes marked above need to have the default transform a = mShape->subShapeFirstNode[ss]; b = a + mShape->subShapeNumNodes[ss]; for (i=a; i<b; i++) { if (rotBeenSet.test(i)) { mShape->defaultRotations[i].getQuatF(&smNodeCurrentRotations[i]); smRotationThreads[i] = NULL; } if (tranBeenSet.test(i)) { smNodeCurrentTranslations[i] = mShape->defaultTranslations[i]; smTranslationThreads[i] = NULL; } } // don't want a transform in these cases... rotBeenSet.overlap(mHandsOffNodes); rotBeenSet.overlap(mCallbackNodes); tranBeenSet.takeAway(maskPosNodes); tranBeenSet.overlap(mHandsOffNodes); tranBeenSet.overlap(mCallbackNodes); // default scale if (scaleCurrentlyAnimated()) handleDefaultScale(a,b,scaleBeenSet); // handle non-blend sequences for (i=0; i<firstBlend; i++) { TSThread * th = mThreadList[i]; j=0; start = th->getSequence()->rotationMatters.start(); end = b; for (nodeIndex=start; nodeIndex<end; th->getSequence()->rotationMatters.next(nodeIndex), j++) { // skip nodes outside of this detail if (nodeIndex<a) continue; if (!rotBeenSet.test(nodeIndex)) { QuatF q1,q2; mShape->getRotation(*th->getSequence(),th->keyNum1,j,&q1); mShape->getRotation(*th->getSequence(),th->keyNum2,j,&q2); TSTransform::interpolate(q1,q2,th->keyPos,&smNodeCurrentRotations[nodeIndex]); rotBeenSet.set(nodeIndex); smRotationThreads[nodeIndex] = th; } } j=0; start = th->getSequence()->translationMatters.start(); end = b; for (nodeIndex=start; nodeIndex<end; th->getSequence()->translationMatters.next(nodeIndex), j++) { if (nodeIndex<a) continue; if (!tranBeenSet.test(nodeIndex)) { if (maskPosNodes.test(nodeIndex)) handleMaskedPositionNode(th,nodeIndex,j); else { const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,j); const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,j); TSTransform::interpolate(p1,p2,th->keyPos,&smNodeCurrentTranslations[nodeIndex]); smTranslationThreads[nodeIndex] = th; } tranBeenSet.set(nodeIndex); } } if (scaleCurrentlyAnimated()) handleAnimatedScale(th,a,b,scaleBeenSet); } // compute transforms for (i=a; i<b; i++) { if (!mHandsOffNodes.test(i)) TSTransform::setMatrix(smNodeCurrentRotations[i],smNodeCurrentTranslations[i],&smNodeLocalTransforms[i]); else smNodeLocalTransforms[i] = mNodeTransforms[i]; // in case mNodeTransform was changed externally } // add scale onto transforms if (scaleCurrentlyAnimated()) handleNodeScale(a,b); // get callbacks... start = getMax(mCallbackNodes.start(),a); end = getMin(mCallbackNodes.end(),b); for (i=0; i<mNodeCallbacks.size(); i++) { AssertFatal(mNodeCallbacks[i].callback, "No callback method defined"); S32 nodeIndex = mNodeCallbacks[i].nodeIndex; if (nodeIndex>=start && nodeIndex<end) { mNodeCallbacks[i].callback->setNodeTransform(this, nodeIndex, smNodeLocalTransforms[nodeIndex]); smNodeLocalTransformDirty.set(nodeIndex); } } // handle blend sequences for (i=firstBlend; i<mThreadList.size(); i++) { TSThread * th = mThreadList[i]; if (th->blendDisabled) continue; handleBlendSequence(th,a,b); } // transitions... if (inTransition()) handleTransitionNodes(a,b); // multiply transforms... for (i=a; i<b; i++) { S32 parentIdx = mShape->nodes[i].parentIndex; if (parentIdx < 0) mNodeTransforms[i] = smNodeLocalTransforms[i]; else mNodeTransforms[i].mul(mNodeTransforms[parentIdx],smNodeLocalTransforms[i]); } }
void SingleLightShadowMap::_render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ) { PROFILE_SCOPE(SingleLightShadowMap_render); const LightMapParams *lmParams = mLight->getExtended<LightMapParams>(); const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true; const U32 texSize = getBestTexSize(); if ( mShadowMapTex.isNull() || mTexSize != texSize ) { mTexSize = texSize; mShadowMapTex.set( mTexSize, mTexSize, ShadowMapFormat, &ShadowMapProfile, "SingleLightShadowMap" ); } GFXFrustumSaver frustSaver; GFXTransformSaver saver; MatrixF lightMatrix; calcLightMatrices( lightMatrix, diffuseState->getCameraFrustum() ); lightMatrix.inverse(); GFX->setWorldMatrix(lightMatrix); const MatrixF& lightProj = GFX->getProjectionMatrix(); mWorldToLightProj = lightProj * lightMatrix; // Render the shadowmap! GFX->pushActiveRenderTarget(); mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex ); mTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) ); GFX->setActiveRenderTarget(mTarget); GFX->clear(GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0); SceneManager* sceneManager = diffuseState->getSceneManager(); SceneRenderState shadowRenderState ( sceneManager, SPT_Shadow, SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), renderPass ); shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); shadowRenderState.renderNonLightmappedMeshes( true ); shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK ); _debugRender( &shadowRenderState ); mTarget->resolve(); GFX->popActiveRenderTarget(); }
void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) { PROFILE_SCOPE( SceneGraph_renderScene ); // Get the lights for rendering the scene. PROFILE_START( SceneGraph_registerLights ); LIGHTMGR->registerGlobalLights( &renderState->getFrustum(), false ); PROFILE_END(); // If its a diffuse pass, update the current ambient light level. // To do that find the starting zone and determine whether it has a custom // ambient light color. If so, pass it on to the ambient light manager. // If not, use the ambient light color of the sunlight. // // Note that we retain the starting zone information here and pass it // on to renderSceneNoLights so that we don't need to look it up twice. if( renderState->isDiffusePass() ) { if( !baseObject && getZoneManager() ) { getZoneManager()->findZone( renderState->getCameraPosition(), baseObject, baseZone ); AssertFatal( baseObject != NULL, "SceneManager::renderScene - findZone() did not return an object" ); } ColorF zoneAmbient; if( baseObject && baseObject->getZoneAmbientLightColor( baseZone, zoneAmbient ) ) mAmbientLightColor.setTargetValue( zoneAmbient ); else { const LightInfo* sunlight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); if( sunlight ) mAmbientLightColor.setTargetValue( sunlight->getAmbient() ); } renderState->setAmbientLightColor( mAmbientLightColor.getCurrentValue() ); } // Trigger the pre-render signal. PROFILE_START( SceneGraph_preRenderSignal); mCurrentRenderState = renderState; getPreRenderSignal().trigger( this, renderState ); mCurrentRenderState = NULL; PROFILE_END(); // Render the scene. if(GFX->getCurrentRenderStyle() == GFXDevice::RS_StereoSideBySide) { // Store previous values RectI originalVP = GFX->getViewport(); MatrixF originalWorld = GFX->getWorldMatrix(); Point2F projOffset = GFX->getCurrentProjectionOffset(); Point3F eyeOffset = GFX->getStereoEyeOffset(); // Render left half of display RectI leftVP = originalVP; leftVP.extent.x *= 0.5; GFX->setViewport(leftVP); MatrixF leftWorldTrans(true); leftWorldTrans.setPosition(Point3F(eyeOffset.x, eyeOffset.y, eyeOffset.z)); MatrixF leftWorld(originalWorld); leftWorld.mulL(leftWorldTrans); GFX->setWorldMatrix(leftWorld); Frustum gfxFrustum = GFX->getFrustum(); gfxFrustum.setProjectionOffset(Point2F(projOffset.x, projOffset.y)); GFX->setFrustum(gfxFrustum); SceneCameraState cameraStateLeft = SceneCameraState::fromGFX(); SceneRenderState renderStateLeft( this, renderState->getScenePassType(), cameraStateLeft ); renderStateLeft.setSceneRenderStyle(SRS_SideBySide); renderStateLeft.setSceneRenderField(0); renderSceneNoLights( &renderStateLeft, objectMask, baseObject, baseZone ); // Render right half of display RectI rightVP = originalVP; rightVP.extent.x *= 0.5; rightVP.point.x += rightVP.extent.x; GFX->setViewport(rightVP); MatrixF rightWorldTrans(true); rightWorldTrans.setPosition(Point3F(-eyeOffset.x, eyeOffset.y, eyeOffset.z)); MatrixF rightWorld(originalWorld); rightWorld.mulL(rightWorldTrans); GFX->setWorldMatrix(rightWorld); gfxFrustum = GFX->getFrustum(); gfxFrustum.setProjectionOffset(Point2F(-projOffset.x, projOffset.y)); GFX->setFrustum(gfxFrustum); SceneCameraState cameraStateRight = SceneCameraState::fromGFX(); SceneRenderState renderStateRight( this, renderState->getScenePassType(), cameraStateRight ); renderStateRight.setSceneRenderStyle(SRS_SideBySide); renderStateRight.setSceneRenderField(1); renderSceneNoLights( &renderStateRight, objectMask, baseObject, baseZone ); // Restore previous values GFX->setWorldMatrix(originalWorld); gfxFrustum.clearProjectionOffset(); GFX->setFrustum(gfxFrustum); GFX->setViewport(originalVP); } else { renderSceneNoLights( renderState, objectMask, baseObject, baseZone ); } // Trigger the post-render signal. PROFILE_START( SceneGraphRender_postRenderSignal ); mCurrentRenderState = renderState; getPostRenderSignal().trigger( this, renderState ); mCurrentRenderState = NULL; PROFILE_END(); // Remove the previously registered lights. PROFILE_START( SceneGraph_unregisterLights); LIGHTMGR->unregisterAllLights(); PROFILE_END(); }
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 ); } }
void ShaderGen::generateShader( const MaterialFeatureData &featureData, char *vertFile, char *pixFile, F32 *pixVersion, const GFXVertexFormat *vertexFormat, const char* cacheName, Vector<GFXShaderMacro> ¯os ) { PROFILE_SCOPE( ShaderGen_GenerateShader ); mFeatureData = featureData; mVertexFormat = vertexFormat; _uninit(); _init(); char vertShaderName[256]; char pixShaderName[256]; // Note: We use a postfix of _V/_P here so that it sorts the matching // vert and pixel shaders together when listed alphabetically. dSprintf( vertShaderName, sizeof(vertShaderName), "shadergen:/%s_V.%s", cacheName, mFileEnding.c_str() ); dSprintf( pixShaderName, sizeof(pixShaderName), "shadergen:/%s_P.%s", cacheName, mFileEnding.c_str() ); dStrcpy( vertFile, vertShaderName ); dStrcpy( pixFile, pixShaderName ); // this needs to change - need to optimize down to ps v.1.1 *pixVersion = GFX->getPixelShaderVersion(); if ( !Con::getBoolVariable( "ShaderGen::GenNewShaders", true ) ) { // If we are not regenerating the shader we will return here. // But we must fill in the shader macros first! _processVertFeatures( macros, true ); _processPixFeatures( macros, true ); return; } // create vertex shader //------------------------ FileStream* s = new FileStream(); if(!s->open(vertShaderName, Torque::FS::File::Write )) { AssertFatal(false, "Failed to open Shader Stream" ); return; } mOutput = new MultiLine; mInstancingFormat.clear(); _processVertFeatures(macros); _printVertShader( *s ); delete s; ((ShaderConnector*)mComponents[C_CONNECTOR])->reset(); LangElement::deleteElements(); // create pixel shader //------------------------ s = new FileStream(); if(!s->open(pixShaderName, Torque::FS::File::Write )) { AssertFatal(false, "Failed to open Shader Stream" ); return; } mOutput = new MultiLine; _processPixFeatures(macros); _printPixShader( *s ); delete s; LangElement::deleteElements(); }
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 ProcessedShaderMaterial::_determineFeatures( U32 stageNum, MaterialFeatureData &fd, const FeatureSet &features ) { PROFILE_SCOPE( ProcessedShaderMaterial_DetermineFeatures ); const float shaderVersion = GFX->getPixelShaderVersion(); AssertFatal(shaderVersion > 0.0 , "Cannot create a shader material if we don't support shaders"); bool lastStage = stageNum == (mMaxStages-1); // First we add all the features which the // material has defined. if ( mMaterial->isTranslucent() ) { // Note: This is for decal blending into the prepass // for AL... it probably needs to be made clearer. if ( mMaterial->mTranslucentBlendOp == Material::LerpAlpha && mMaterial->mTranslucentZWrite ) fd.features.addFeature( MFT_IsTranslucentZWrite ); else { fd.features.addFeature( MFT_IsTranslucent ); fd.features.addFeature( MFT_ForwardShading ); } } // TODO: This sort of sucks... BL should somehow force this // feature on from the outside and not this way. if ( dStrcmp( LIGHTMGR->getId(), "BLM" ) == 0 ) fd.features.addFeature( MFT_ForwardShading ); // Disabling the InterlacedPrePass feature for now. It is not ready for prime-time // and it should not be triggered off of the DoubleSided parameter. [2/5/2010 Pat] /*if ( mMaterial->isDoubleSided() ) { fd.features.addFeature( MFT_InterlacedPrePass ); }*/ // Allow instancing if it was requested and the card supports // SM 3.0 or above. // // We also disable instancing for non-single pass materials // and glowing materials because its untested/unimplemented. // if ( features.hasFeature( MFT_UseInstancing ) && mMaxStages == 1 && !mMaterial->mGlow[0] && shaderVersion >= 3.0f ) fd.features.addFeature( MFT_UseInstancing ); if ( mMaterial->mAlphaTest ) fd.features.addFeature( MFT_AlphaTest ); // start jc if ( mMaterial->mAlphaScatter ) fd.features.addFeature( MFT_AlphaScatter ); // end jc if ( mMaterial->mEmissive[stageNum] ) fd.features.addFeature( MFT_IsEmissive ); else fd.features.addFeature( MFT_RTLighting ); if ( mMaterial->mAnimFlags[stageNum] ) fd.features.addFeature( MFT_TexAnim ); if ( mMaterial->mVertLit[stageNum] ) fd.features.addFeature( MFT_VertLit ); // cubemaps only available on stage 0 for now - bramage if ( stageNum < 1 && ( ( mMaterial->mCubemapData && mMaterial->mCubemapData->mCubemap ) || mMaterial->mDynamicCubemap ) ) fd.features.addFeature( MFT_CubeMap ); fd.features.addFeature( MFT_Visibility ); // start jc /* if ( lastStage && ( !gClientSceneGraph->usePostEffectFog() || fd.features.hasFeature( MFT_IsTranslucent ) || fd.features.hasFeature( MFT_ForwardShading )) ) fd.features.addFeature( MFT_Fog ); */ if ( lastStage && ( !gClientSceneGraph->usePostEffectFog() || fd.features.hasFeature( MFT_IsTranslucent ) || fd.features.hasFeature( MFT_ForwardShading )) ) { if(mMaterial->mTranslucentBlendOp == Material::Add) fd.features.addFeature( MFT_FogBlendAdd ); else fd.features.addFeature( MFT_Fog ); } // end jc if ( mMaterial->mMinnaertConstant[stageNum] > 0.0f ) fd.features.addFeature( MFT_MinnaertShading ); if ( mMaterial->mSubSurface[stageNum] ) fd.features.addFeature( MFT_SubSurface ); if ( !mMaterial->mCellLayout[stageNum].isZero() ) { fd.features.addFeature( MFT_DiffuseMapAtlas ); if ( mMaterial->mNormalMapAtlas ) fd.features.addFeature( MFT_NormalMapAtlas ); } // Grab other features like normal maps, base texture, etc. FeatureSet mergeFeatures; mStages[stageNum].getFeatureSet( &mergeFeatures ); fd.features.merge( mergeFeatures ); if ( fd.features[ MFT_NormalMap ] ) { if ( mStages[stageNum].getTex( MFT_NormalMap )->mFormat == GFXFormatDXT5 && !mStages[stageNum].getTex( MFT_NormalMap )->mHasTransparency ) fd.features.addFeature( MFT_IsDXTnm ); // start jc if ( mMaterial->mObjectSpaceNormals ) fd.features.addFeature( MFT_IsObjectSpaceNormals ); // end jc } // Now for some more advanced features that we // cannot do on SM 2.0 and below. if ( shaderVersion > 2.0f ) { // Only allow parallax if we have a normal map and // we're not using DXTnm compression. if ( mMaterial->mParallaxScale[stageNum] > 0.0f && fd.features[ MFT_NormalMap ] && !fd.features[ MFT_IsDXTnm ] ) fd.features.addFeature( MFT_Parallax ); // If not parallax then allow per-pixel specular if // we have real time lighting enabled. else if ( fd.features[MFT_RTLighting] && mMaterial->mPixelSpecular[stageNum] ) fd.features.addFeature( MFT_PixSpecular ); } // Without realtime lighting and on lower end // shader models disable the specular map. if ( !fd.features[ MFT_RTLighting ] || shaderVersion == 2.0 ) fd.features.removeFeature( MFT_SpecularMap ); // If we have a specular map then make sure we // have per-pixel specular enabled. if( fd.features[ MFT_SpecularMap ] ) { fd.features.addFeature( MFT_PixSpecular ); // Check for an alpha channel on the specular map. If it has one (and it // has values less than 255) than the artist has put the gloss map into // the alpha channel. if( mStages[stageNum].getTex( MFT_SpecularMap )->mHasTransparency ) fd.features.addFeature( MFT_GlossMap ); } // Without a base texture use the diffuse color // feature to ensure some sort of output. if (!fd.features[MFT_DiffuseMap]) { fd.features.addFeature( MFT_DiffuseColor ); // No texture coords... no overlay. fd.features.removeFeature( MFT_OverlayMap ); } // If we have a diffuse map and the alpha on the diffuse isn't // zero and the color isn't pure white then multiply the color. else if ( mMaterial->mDiffuse[stageNum].alpha > 0.0f && mMaterial->mDiffuse[stageNum] != ColorF::WHITE ) fd.features.addFeature( MFT_DiffuseColor ); // If lightmaps or tonemaps are enabled or we // don't have a second UV set then we cannot // use the overlay texture. if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] || mVertexFormat->getTexCoordCount() < 2 ) fd.features.removeFeature( MFT_OverlayMap ); // If tonemaps are enabled don't use lightmap if ( fd.features[MFT_ToneMap] || mVertexFormat->getTexCoordCount() < 2 ) fd.features.removeFeature( MFT_LightMap ); // Don't allow tonemaps if we don't have a second UV set if ( mVertexFormat->getTexCoordCount() < 2 ) fd.features.removeFeature( MFT_ToneMap ); // Always add the HDR output feature. // // It will be filtered out if it was disabled // for this material creation below. // // Also the shader code will evaluate to a nop // if HDR is not enabled in the scene. // fd.features.addFeature( MFT_HDROut ); // If vertex color is enabled on the material's stage and // color is present in vertex format, add diffuse vertex // color feature. if ( mMaterial->mVertColor[ stageNum ] && mVertexFormat->hasColor() ) fd.features.addFeature( MFT_DiffuseVertColor ); // Allow features to add themselves. for ( U32 i = 0; i < FEATUREMGR->getFeatureCount(); i++ ) { const FeatureInfo &info = FEATUREMGR->getAt( i ); info.feature->determineFeature( mMaterial, mVertexFormat, stageNum, *info.type, features, &fd ); } // Now disable any features that were // not part of the input feature handle. fd.features.filter( features ); }
void StdServerProcessList::onTickObject( ProcessObject *pobj ) { PROFILE_SCOPE( StdServerProcessList_OnTickObject ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. GameConnection *con = pobj->getControllingClient(); if ( pobj->mIsGameBase && con && con->getControlObject() == pobj ) { // In case the object is deleted during its own tick. SimObjectPtr<GameBase> obj = getGameBase( pobj ); Move* movePtr; U32 m, numMoves; con->mMoveList->getMoves( &movePtr, &numMoves ); // For debugging it can be useful to know when this happens. //if ( numMoves > 1 ) // Con::printf( "numMoves: %i", numMoves ); // Do we really need to test the control object each iteration? Does it change? for ( m = 0; m < numMoves && con && con->getControlObject() == obj; m++, movePtr++ ) { #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); #endif if ( obj->isTicking() ) obj->processTick( movePtr ); if ( con && con->getControlObject() == obj ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // check move checksum if ( movePtr->checksum != newsum ) { #ifdef TORQUE_DEBUG_NET_MOVES if( !obj->isAIControlled() ) Con::printf("move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)", movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z); #endif movePtr->checksum = Move::ChecksumMismatch; } else { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("move %i checksum agree: %i == %i, (start %i), (move %f %f %f)", movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z); #endif } } } con->mMoveList->clearMoves( m ); } else if ( pobj->isTicking() ) pobj->processTick( 0 ); }
void ProcessedShaderMaterial::_setTextureTransforms(const U32 pass) { PROFILE_SCOPE( ProcessedShaderMaterial_SetTextureTransforms ); ShaderConstHandles* handles = _getShaderConstHandles(pass); if (handles->mTexMatSC->isValid()) { MatrixF texMat( true ); mMaterial->updateTimeBasedParams(); F32 waveOffset = _getWaveOffset( pass ); // offset is between 0.0 and 1.0 // handle scroll anim type if( mMaterial->mAnimFlags[pass] & Material::Scroll ) { if( mMaterial->mAnimFlags[pass] & Material::Wave ) { Point3F scrollOffset; scrollOffset.x = mMaterial->mScrollDir[pass].x * waveOffset; scrollOffset.y = mMaterial->mScrollDir[pass].y * waveOffset; scrollOffset.z = 1.0; texMat.setColumn( 3, scrollOffset ); } else { Point3F offset( mMaterial->mScrollOffset[pass].x, mMaterial->mScrollOffset[pass].y, 1.0 ); texMat.setColumn( 3, offset ); } } // handle rotation if( mMaterial->mAnimFlags[pass] & Material::Rotate ) { if( mMaterial->mAnimFlags[pass] & Material::Wave ) { F32 rotPos = waveOffset * M_2PI; texMat.set( EulerF( 0.0, 0.0, rotPos ) ); texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) ); MatrixF test( true ); test.setColumn( 3, Point3F( mMaterial->mRotPivotOffset[pass].x, mMaterial->mRotPivotOffset[pass].y, 0.0 ) ); texMat.mul( test ); } else { texMat.set( EulerF( 0.0, 0.0, mMaterial->mRotPos[pass] ) ); texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) ); MatrixF test( true ); test.setColumn( 3, Point3F( mMaterial->mRotPivotOffset[pass].x, mMaterial->mRotPivotOffset[pass].y, 0.0 ) ); texMat.mul( test ); } } // Handle scale + wave offset if( mMaterial->mAnimFlags[pass] & Material::Scale && mMaterial->mAnimFlags[pass] & Material::Wave ) { F32 wOffset = fabs( waveOffset ); texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) ); MatrixF temp( true ); temp.setRow( 0, Point3F( wOffset, 0.0, 0.0 ) ); temp.setRow( 1, Point3F( 0.0, wOffset, 0.0 ) ); temp.setRow( 2, Point3F( 0.0, 0.0, wOffset ) ); temp.setColumn( 3, Point3F( -wOffset * 0.5, -wOffset * 0.5, 0.0 ) ); texMat.mul( temp ); } // handle sequence if( mMaterial->mAnimFlags[pass] & Material::Sequence ) { U32 frameNum = (U32)(MATMGR->getTotalTime() * mMaterial->mSeqFramePerSec[pass]); F32 offset = frameNum * mMaterial->mSeqSegSize[pass]; if ( mMaterial->mAnimFlags[pass] & Material::Scale ) texMat.scale( Point3F( mMaterial->mSeqSegSize[pass], 1.0f, 1.0f ) ); Point3F texOffset = texMat.getPosition(); texOffset.x += offset; texMat.setPosition( texOffset ); } GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); shaderConsts->setSafe(handles->mTexMatSC, texMat); } }
void TerrCell::createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer ) { PROFILE_SCOPE( TerrCell_AllocPrimBuffer ); primBuffer->set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" ); // We don't use the primitive for normal clipmap // rendering, but it is used for the shadow pass. GFXPrimitive *prim = primBuffer->getPointer()->mPrimitiveArray; prim->type = GFXTriangleList; prim->numPrimitives = smTriCount; prim->numVertices = smVBSize; // // The vertex pattern for the terrain is as // follows... // // 0----1----2.....n // |\ | /| // | \ | / | // | \ | / | // | \|/ | // n----n----n // | /|\ | // | / | \ | // | / | \ | // |/ | \| // n----n----n // // Lock and fill it up! U16 *idxBuff; primBuffer->lock( &idxBuff ); U32 counter = 0; U32 maxIndex = 0; for ( U32 y = 0; y < smMinCellSize; y++ ) { const U32 yTess = y % 2; for ( U32 x = 0; x < smMinCellSize; x++ ) { U32 index = ( y * smVBStride ) + x; const U32 xTess = x % 2; if ( ( xTess == 0 && yTess == 0 ) || ( xTess != 0 && yTess != 0 ) ) { idxBuff[0] = index + 0; idxBuff[1] = index + smVBStride; idxBuff[2] = index + smVBStride + 1; idxBuff[3] = index + 0; idxBuff[4] = index + smVBStride + 1; idxBuff[5] = index + 1; } else { idxBuff[0] = index + 1; idxBuff[1] = index; idxBuff[2] = index + smVBStride; idxBuff[3] = index + 1; idxBuff[4] = index + smVBStride; idxBuff[5] = index + smVBStride + 1; } idxBuff += 6; maxIndex = index + 1 + smVBStride; counter += 6; } } // Now add indices for the 'skirts'. // These could probably be reduced to a loop. // Temporaries that hold triangle indices. // Top/Bottom - 0,1 U32 t0, t1, b0, b1; // Top edge skirt... // Index to the first vert of the top row. U32 startIndex = 0; // Index to the first vert of the skirt under the top row. U32 skirtStartIdx = smVBStride * smVBStride; // Step to go one vert to the right. U32 step = 1; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + i * step; t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = b0; idxBuff[1] = t0; idxBuff[2] = t1; idxBuff[3] = b1; idxBuff[4] = b0; idxBuff[5] = t1; idxBuff += 6; maxIndex = b1; counter += 6; } // Bottom edge skirt... // Index to the first vert of the bottom row. startIndex = smVBStride * smVBStride - smVBStride; // Index to the first vert of the skirt under the bottom row. skirtStartIdx = startIndex + smVBStride * 2; // Step to go one vert to the right. step = 1; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + ( i * step ); t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = t1; idxBuff[1] = t0; idxBuff[2] = b0; idxBuff[3] = t1; idxBuff[4] = b0; idxBuff[5] = b1; idxBuff += 6; maxIndex = b1; counter += 6; } // Left edge skirt... // Index to the first vert of the left column. startIndex = 0; // Index to the first vert of the skirt under the left column. skirtStartIdx = smVBStride * smVBStride + smVBStride * 2; // Step to go one vert down. step = smVBStride; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + ( i * step ); t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = t1; idxBuff[1] = t0; idxBuff[2] = b0; idxBuff[3] = t1; idxBuff[4] = b0; idxBuff[5] = b1; idxBuff += 6; maxIndex = b1; counter += 6; } // Right edge skirt... // Index to the first vert of the right column. startIndex = smVBStride - 1; // Index to the first vert of the skirt under the right column. skirtStartIdx = smVBStride * smVBStride + smVBStride * 3; // Step to go one vert down. step = smVBStride; for ( U32 i = 0; i < smMinCellSize; i++ ) { t0 = startIndex + ( i * step ); t1 = t0 + step; b0 = skirtStartIdx + i; b1 = skirtStartIdx + i + 1; idxBuff[0] = b0; idxBuff[1] = t0; idxBuff[2] = t1; idxBuff[3] = b1; idxBuff[4] = b0; idxBuff[5] = t1; idxBuff += 6; maxIndex = b1; counter += 6; } primBuffer->unlock(); }