mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 15:14:35 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
728
Engine/source/terrain/glsl/terrFeatureGLSL.cpp
Normal file
728
Engine/source/terrain/glsl/terrFeatureGLSL.cpp
Normal file
|
|
@ -0,0 +1,728 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/glsl/terrFeatureGLSL.h"
|
||||
|
||||
#include "terrain/terrFeatureTypes.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "materials/materialFeatureData.h"
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "shaderGen/langElement.h"
|
||||
#include "shaderGen/shaderOp.h"
|
||||
#include "shaderGen/featureMgr.h"
|
||||
#include "core/module.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( TerrainFeatGLSL )
|
||||
|
||||
MODULE_INIT_AFTER( ShaderGenFeatureMgr )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
FEATUREMGR->registerFeature( MFT_TerrainBaseMap, new TerrainBaseMapFeatGLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainParallaxMap, new TerrainParallaxMapFeatGLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainDetailMap, new TerrainDetailMapFeatGLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainNormalMap, new TerrainNormalMapFeatGLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL );
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
Var* TerrainFeatGLSL::_getUniformVar( const char *name, const char *type, ConstantSortPosition csp )
|
||||
{
|
||||
Var *theVar = (Var*)LangElement::find( name );
|
||||
if ( !theVar )
|
||||
{
|
||||
theVar = new Var;
|
||||
theVar->setType( type );
|
||||
theVar->setName( name );
|
||||
theVar->uniform = true;
|
||||
theVar->constSortPos = csp;
|
||||
}
|
||||
|
||||
return theVar;
|
||||
}
|
||||
|
||||
Var* TerrainFeatGLSL::_getInDetailCoord( Vector<ShaderComponent*> &componentList )
|
||||
{
|
||||
String name( String::ToString( "outDetCoord%d", getProcessIndex() ) );
|
||||
Var *inDet = (Var*)LangElement::find( name );
|
||||
|
||||
if ( !inDet )
|
||||
{
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
|
||||
inDet = connectComp->getElement( RT_TEXCOORD );
|
||||
inDet->setName( name );
|
||||
inDet->setType( "vec4" );
|
||||
inDet->mapsToSampler = true;
|
||||
}
|
||||
|
||||
return inDet;
|
||||
}
|
||||
|
||||
Var* TerrainFeatGLSL::_getNormalMapTex()
|
||||
{
|
||||
String name( String::ToString( "normalMap%d", getProcessIndex() ) );
|
||||
Var *normalMap = (Var*)LangElement::find( name );
|
||||
|
||||
if ( !normalMap )
|
||||
{
|
||||
normalMap = new Var;
|
||||
normalMap->setType( "sampler2D" );
|
||||
normalMap->setName( name );
|
||||
normalMap->uniform = true;
|
||||
normalMap->sampler = true;
|
||||
normalMap->constNum = Var::getTexUnitNum();
|
||||
}
|
||||
|
||||
return normalMap;
|
||||
}
|
||||
|
||||
Var* TerrainFeatGLSL::_getDetailIdStrengthParallax()
|
||||
{
|
||||
String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) );
|
||||
|
||||
Var *detailInfo = (Var*)LangElement::find( name );
|
||||
if ( !detailInfo )
|
||||
{
|
||||
detailInfo = new Var;
|
||||
detailInfo->setType( "vec3" );
|
||||
detailInfo->setName( name );
|
||||
detailInfo->uniform = true;
|
||||
detailInfo->constSortPos = cspPotentialPrimitive;
|
||||
}
|
||||
|
||||
return detailInfo;
|
||||
}
|
||||
|
||||
void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
MultiLine *meta = new MultiLine;
|
||||
output = meta;
|
||||
|
||||
// Generate the incoming texture var.
|
||||
Var *inTex;
|
||||
{
|
||||
Var *inPos = (Var*)LangElement::find( "inPosition" );
|
||||
if ( !inPos )
|
||||
inPos = (Var*)LangElement::find( "position" );
|
||||
|
||||
inTex = new Var( "texCoord", "vec3" );
|
||||
|
||||
Var *oneOverTerrainSize = _getUniformVar( "oneOverTerrainSize", "float", cspPass );
|
||||
|
||||
// NOTE: The y coord here should be negative to have
|
||||
// the texture maps not end up flipped which also caused
|
||||
// normal and parallax mapping to be incorrect.
|
||||
//
|
||||
// This mistake early in development means that the layer
|
||||
// id bilinear blend depends on it being that way.
|
||||
//
|
||||
// So instead i fixed this by flipping the base and detail
|
||||
// coord y scale to compensate when rendering.
|
||||
//
|
||||
meta->addStatement( new GenOp( " @ = @.xyz * vec3( @, @, -@ );\r\n",
|
||||
new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) );
|
||||
}
|
||||
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
|
||||
// Pass the texture coord to the pixel shader.
|
||||
Var *outTex = connectComp->getElement( RT_TEXCOORD );
|
||||
outTex->setName( "outTexCoord" );
|
||||
outTex->setType( "vec3" );
|
||||
outTex->mapsToSampler = true;
|
||||
meta->addStatement( new GenOp( " @.xy = @.xy;\r\n", outTex, inTex ) );
|
||||
|
||||
// If this shader has a side projected layer then we
|
||||
// pass the dot product between the +Y and the normal
|
||||
// thru outTexCoord.z for use in blending the textures.
|
||||
if ( fd.features.hasFeature( MFT_TerrainSideProject ) )
|
||||
{
|
||||
Var *inNormal = (Var*)LangElement::find( "normal" );
|
||||
meta->addStatement(
|
||||
new GenOp( " @.z = pow( abs( dot( normalize( vec3( @.x, @.y, 0.0 ) ), vec3( 0, 1, 0 ) ) ), 10.0 );\r\n",
|
||||
outTex, inNormal, inNormal ) );
|
||||
}
|
||||
else
|
||||
meta->addStatement( new GenOp( " @.z = 0;\r\n", outTex ) );
|
||||
|
||||
// HACK: This is sort of lazy... we generate the tanget
|
||||
// vector here so that we're sure it exists in the parallax
|
||||
// and normal features which will expect "T" to exist.
|
||||
//
|
||||
// If this shader doesn't use it the shader compiler will
|
||||
// optimize away this code.
|
||||
//
|
||||
Var *inTangentZ = getVertTexCoord( "tcTangentZ" );
|
||||
Var *inTanget = new Var( "T", "vec3" );
|
||||
Var *squareSize = _getUniformVar( "squareSize", "float", cspPass );
|
||||
meta->addStatement( new GenOp( " @ = normalize( vec3( @, 0.0, @ ) );\r\n",
|
||||
new DecOp( inTanget ), squareSize, inTangentZ ) );
|
||||
}
|
||||
|
||||
void TerrainBaseMapFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// grab connector texcoord register
|
||||
Var *texCoord = getInTexCoord( "outTexCoord", "vec3", true, componentList );
|
||||
|
||||
// We do nothing more if this is a prepass.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
return;
|
||||
|
||||
// create texture var
|
||||
Var *diffuseMap = new Var;
|
||||
diffuseMap->setType( "sampler2D" );
|
||||
diffuseMap->setName( "baseTexMap" );
|
||||
diffuseMap->uniform = true;
|
||||
diffuseMap->sampler = true;
|
||||
diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
Var *baseColor = new Var;
|
||||
baseColor->setType( "vec4" );
|
||||
baseColor->setName( "baseColor" );
|
||||
meta->addStatement( new GenOp( " @ = texture2D( @, @.xy );\r\n", new DecOp( baseColor ), diffuseMap, texCoord ) );
|
||||
meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul ) ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainBaseMapFeatGLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
res.numTexReg = 1;
|
||||
|
||||
// We only sample from the base map during a diffuse pass.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
res.numTex = 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL()
|
||||
: mTerrainDep( "shaders/common/terrain/terrain.glsl" )
|
||||
{
|
||||
addDependency( &mTerrainDep );
|
||||
}
|
||||
|
||||
void TerrainDetailMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
const U32 detailIndex = getProcessIndex();
|
||||
|
||||
|
||||
// If this is a prepass and we don't have a
|
||||
// matching normal map... we have nothing to do.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) &&
|
||||
!fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) )
|
||||
return;
|
||||
|
||||
// Grab incoming texture coords... the base map feature
|
||||
// made sure this was created.
|
||||
Var *inTex = (Var*)LangElement::find( "texCoord" );
|
||||
AssertFatal( inTex, "The texture coord is missing!" );
|
||||
|
||||
// Grab the input position.
|
||||
Var *inPos = (Var*)LangElement::find( "inPosition" );
|
||||
if ( !inPos )
|
||||
inPos = (Var*)LangElement::find( "position" );
|
||||
|
||||
// Get the object space eye position.
|
||||
Var *eyePos = _getUniformVar( "eyePos", "vec3", cspPotentialPrimitive );
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// Get the distance from the eye to this vertex.
|
||||
Var *dist = (Var*)LangElement::find( "dist" );
|
||||
if ( !dist )
|
||||
{
|
||||
dist = new Var;
|
||||
dist->setType( "float" );
|
||||
dist->setName( "dist" );
|
||||
|
||||
meta->addStatement( new GenOp( " @ = distance( @.xyz, @ );\r\n",
|
||||
new DecOp( dist ), inPos, eyePos ) );
|
||||
}
|
||||
|
||||
// grab connector texcoord register
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
Var *outTex = connectComp->getElement( RT_TEXCOORD );
|
||||
outTex->setName( String::ToString( "outDetCoord%d", detailIndex ) );
|
||||
outTex->setType( "vec4" );
|
||||
outTex->mapsToSampler = true;
|
||||
|
||||
// Get the detail scale and fade info.
|
||||
Var *detScaleAndFade = new Var;
|
||||
detScaleAndFade->setType( "vec4" );
|
||||
detScaleAndFade->setName( String::ToString( "detailScaleAndFade%d", detailIndex ) );
|
||||
detScaleAndFade->uniform = true;
|
||||
detScaleAndFade->constSortPos = cspPotentialPrimitive;
|
||||
|
||||
// Setup the detail coord.
|
||||
//
|
||||
// NOTE: You see here we scale the texture coord by 'xyx'
|
||||
// to generate the detail coord. This y is here because
|
||||
// its scale is flipped to correct for the non negative y
|
||||
// in texCoord.
|
||||
//
|
||||
// See TerrainBaseMapFeatHLSL::processVert().
|
||||
//
|
||||
meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) );
|
||||
|
||||
// And sneak the detail fade thru the w detailCoord.
|
||||
meta->addStatement( new GenOp( " @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n",
|
||||
outTex, detScaleAndFade, dist, detScaleAndFade ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
void TerrainDetailMapFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
const U32 detailIndex = getProcessIndex();
|
||||
|
||||
// If this is a prepass and we don't have a
|
||||
// matching normal map... we have nothing to do.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) &&
|
||||
!fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) )
|
||||
return;
|
||||
|
||||
Var *inTex = getVertTexCoord( "outTexCoord" );
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// Get the layer samples.
|
||||
Var *layerSample = (Var*)LangElement::find( "layerSample" );
|
||||
if ( !layerSample )
|
||||
{
|
||||
layerSample = new Var;
|
||||
layerSample->setType( "vec4" );
|
||||
layerSample->setName( "layerSample" );
|
||||
|
||||
// Get the layer texture var
|
||||
Var *layerTex = new Var;
|
||||
layerTex->setType( "sampler2D" );
|
||||
layerTex->setName( "layerTex" );
|
||||
layerTex->uniform = true;
|
||||
layerTex->sampler = true;
|
||||
layerTex->constNum = Var::getTexUnitNum();
|
||||
|
||||
// Read the layer texture to get the samples.
|
||||
meta->addStatement( new GenOp( " @ = round( texture2D( @, @.xy ) * 255.0f );\r\n",
|
||||
new DecOp( layerSample ), layerTex, inTex ) );
|
||||
}
|
||||
|
||||
Var *layerSize = (Var*)LangElement::find( "layerSize" );
|
||||
if ( !layerSize )
|
||||
{
|
||||
layerSize = new Var;
|
||||
layerSize->setType( "float" );
|
||||
layerSize->setName( "layerSize" );
|
||||
layerSize->uniform = true;
|
||||
layerSize->constSortPos = cspPass;
|
||||
}
|
||||
|
||||
// Grab the incoming detail coord.
|
||||
Var *inDet = _getInDetailCoord( componentList );
|
||||
|
||||
// Get the detail id.
|
||||
Var *detailInfo = _getDetailIdStrengthParallax();
|
||||
|
||||
// Create the detail blend var.
|
||||
Var *detailBlend = new Var;
|
||||
detailBlend->setType( "float" );
|
||||
detailBlend->setName( String::ToString( "detailBlend%d", detailIndex ) );
|
||||
|
||||
// Calculate the blend for this detail texture.
|
||||
meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n",
|
||||
new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) );
|
||||
|
||||
// Get a var and accumulate the blend amount.
|
||||
Var *blendTotal = (Var*)LangElement::find( "blendTotal" );
|
||||
if ( !blendTotal )
|
||||
{
|
||||
blendTotal = new Var;
|
||||
blendTotal->setName( "blendTotal" );
|
||||
blendTotal->setType( "float" );
|
||||
meta->addStatement( new GenOp( " @ = 0.0;\r\n", new DecOp( blendTotal ) ) );
|
||||
}
|
||||
|
||||
// Add to the blend total.
|
||||
meta->addStatement( new GenOp( " @ += @;\r\n", blendTotal, detailBlend ) );
|
||||
//meta->addStatement( new GenOp( " @ += @ * @.y * @.w;\r\n",
|
||||
//blendTotal, detailBlend, detailInfo, inDet ) );
|
||||
|
||||
// Nothing more to do for a detail texture in prepass.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
{
|
||||
output = meta;
|
||||
return;
|
||||
}
|
||||
|
||||
Var *detailColor = (Var*)LangElement::find( "detailColor" );
|
||||
if ( !detailColor )
|
||||
{
|
||||
detailColor = new Var;
|
||||
detailColor->setType( "vec4" );
|
||||
detailColor->setName( "detailColor" );
|
||||
meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) );
|
||||
}
|
||||
|
||||
// Get the detail texture.
|
||||
Var *detailMap = new Var;
|
||||
detailMap->setType( "sampler2D" );
|
||||
detailMap->setName( String::ToString( "detailMap%d", detailIndex ) );
|
||||
detailMap->uniform = true;
|
||||
detailMap->sampler = true;
|
||||
detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here
|
||||
|
||||
// If we're using SM 3.0 then take advantage of
|
||||
// dynamic branching to skip layers per-pixel.
|
||||
|
||||
if ( GFX->getPixelShaderVersion() >= 3.0f )
|
||||
meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) );
|
||||
|
||||
meta->addStatement( new GenOp( " {\r\n" ) );
|
||||
|
||||
// Note that we're doing the standard greyscale detail
|
||||
// map technique here which can darken and lighten the
|
||||
// diffuse texture.
|
||||
//
|
||||
// We take two color samples and lerp between them for
|
||||
// side projection layers... else a single sample.
|
||||
//
|
||||
if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) )
|
||||
{
|
||||
meta->addStatement( new GenOp( " @ = ( mix( texture2D( @, @.yz ), texture2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n",
|
||||
detailColor, detailMap, inDet, detailMap, inDet, inTex ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
meta->addStatement( new GenOp( " @ = ( texture2D( @, @.xy ) * 2.0 ) - 1.0;\r\n",
|
||||
detailColor, detailMap, inDet ) );
|
||||
}
|
||||
|
||||
meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n",
|
||||
detailColor, detailInfo, inDet ) );
|
||||
|
||||
Var *baseColor = (Var*)LangElement::find( "baseColor" );
|
||||
Var *outColor = (Var*)LangElement::find( "col" );
|
||||
|
||||
meta->addStatement( new GenOp( " @ = mix( @, @ + @, @ );\r\n",
|
||||
outColor, outColor, baseColor, detailColor, detailBlend ) );
|
||||
|
||||
meta->addStatement( new GenOp( " }\r\n" ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainDetailMapFeatGLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
{
|
||||
// If this is a prepass and we don't have a
|
||||
// matching normal map... we use no resources.
|
||||
if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() ) )
|
||||
return res;
|
||||
|
||||
// If this is the first matching normal map then
|
||||
// it also samples from the layer tex.
|
||||
if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) )
|
||||
res.numTex += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is the first detail pass then it
|
||||
// also samples from the layer tex.
|
||||
if ( !fd.features.hasFeature( MFT_TerrainDetailMap, getProcessIndex() - 1 ) )
|
||||
res.numTex += 1;
|
||||
|
||||
res.numTex += 1;
|
||||
}
|
||||
|
||||
res.numTexReg += 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TerrainNormalMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// We only need to process normals during the prepass.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// Make sure the world to tangent transform
|
||||
// is created and available for the pixel shader.
|
||||
getOutViewToTangent( componentList, meta, fd );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
void TerrainNormalMapFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// We only need to process normals during the prepass.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
Var *viewToTangent = getInViewToTangent( componentList );
|
||||
|
||||
// This var is read from GBufferConditionerGLSL and
|
||||
// used in the prepass output.
|
||||
Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
|
||||
if ( !gbNormal )
|
||||
{
|
||||
gbNormal = new Var;
|
||||
gbNormal->setName( "gbNormal" );
|
||||
gbNormal->setType( "vec3" );
|
||||
meta->addStatement( new GenOp( " @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) );
|
||||
}
|
||||
|
||||
const U32 normalIndex = getProcessIndex();
|
||||
|
||||
Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) );
|
||||
AssertFatal( detailBlend, "The detail blend is missing!" );
|
||||
|
||||
// If we're using SM 3.0 then take advantage of
|
||||
// dynamic branching to skip layers per-pixel.
|
||||
|
||||
if ( GFX->getPixelShaderVersion() >= 3.0f )
|
||||
meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) );
|
||||
|
||||
meta->addStatement( new GenOp( " {\r\n" ) );
|
||||
|
||||
// Get the normal map texture.
|
||||
Var *normalMap = _getNormalMapTex();
|
||||
|
||||
/// Get the texture coord.
|
||||
Var *inDet = _getInDetailCoord( componentList );
|
||||
Var *inTex = getVertTexCoord( "outTexCoord" );
|
||||
|
||||
// Sample the normal map.
|
||||
//
|
||||
// We take two normal samples and lerp between them for
|
||||
// side projection layers... else a single sample.
|
||||
LangElement *texOp;
|
||||
if ( fd.features.hasFeature( MFT_TerrainSideProject, normalIndex ) )
|
||||
{
|
||||
texOp = new GenOp( "mix( texture2D( @, @.yz ), texture2D( @, @.xz ), @.z )",
|
||||
normalMap, inDet, normalMap, inDet, inTex );
|
||||
}
|
||||
else
|
||||
texOp = new GenOp( "texture2D(@, @.xy)", normalMap, inDet );
|
||||
|
||||
// create bump normal
|
||||
Var *bumpNorm = new Var;
|
||||
bumpNorm->setName( "bumpNormal" );
|
||||
bumpNorm->setType( "vec4" );
|
||||
|
||||
LangElement *bumpNormDecl = new DecOp( bumpNorm );
|
||||
meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
|
||||
|
||||
// Normalize is done later...
|
||||
// Note: The reverse mul order is intentional. Affine matrix.
|
||||
meta->addStatement( new GenOp( " @ = mix( @, @.xyz * @, min( @, @.w ) );\r\n",
|
||||
gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) );
|
||||
|
||||
// End the conditional block.
|
||||
meta->addStatement( new GenOp( " }\r\n" ) );
|
||||
|
||||
// If this is the last normal map then we
|
||||
// can test to see the total blend value
|
||||
// to see if we should clip the result.
|
||||
//if ( fd.features.getNextFeatureIndex( MFT_TerrainNormalMap, normalIndex ) == -1 )
|
||||
//meta->addStatement( new GenOp( " clip( @ - 0.0001f );\r\n", blendTotal ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainNormalMapFeatGLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
|
||||
// We only need to process normals during the prepass.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
{
|
||||
// If this is the first normal map then it
|
||||
// will generate the worldToTanget transform.
|
||||
if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) )
|
||||
res.numTexReg = 3;
|
||||
|
||||
res.numTex = 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TerrainParallaxMapFeatGLSL::TerrainParallaxMapFeatGLSL()
|
||||
: mIncludeDep( "shaders/common/gl/torque.glsl" )
|
||||
{
|
||||
addDependency( &mIncludeDep );
|
||||
}
|
||||
|
||||
void TerrainParallaxMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
if ( LangElement::find( "outNegViewTS" ) )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// Grab the input position.
|
||||
Var *inPos = (Var*)LangElement::find( "inPosition" );
|
||||
if ( !inPos )
|
||||
inPos = (Var*)LangElement::find( "position" );
|
||||
|
||||
// Get the object space eye position and the
|
||||
// object to tangent transform.
|
||||
Var *eyePos = _getUniformVar( "eyePos", "vec3" , cspPotentialPrimitive );
|
||||
Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta,fd );
|
||||
|
||||
// Now send the negative view vector in tangent space to the pixel shader.
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD );
|
||||
outNegViewTS->setName( "outNegViewTS" );
|
||||
outNegViewTS->setType( "vec3" );
|
||||
meta->addStatement( new GenOp( " @ = @ * vec3( @ - @.xyz );\r\n",
|
||||
outNegViewTS, objToTangentSpace, eyePos, inPos ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
void TerrainParallaxMapFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
|
||||
// We need the negative tangent space view vector
|
||||
// as in parallax mapping we step towards the camera.
|
||||
Var *negViewTS = (Var*)LangElement::find( "negViewTS" );
|
||||
if ( !negViewTS )
|
||||
{
|
||||
Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" );
|
||||
if ( !inNegViewTS )
|
||||
{
|
||||
inNegViewTS = connectComp->getElement( RT_TEXCOORD );
|
||||
inNegViewTS->setName( "outNegViewTS" );
|
||||
inNegViewTS->setType( "vec3" );
|
||||
}
|
||||
|
||||
negViewTS = new Var( "negViewTS", "vec3" );
|
||||
meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) );
|
||||
}
|
||||
|
||||
// Get the rest of our inputs.
|
||||
Var *detailInfo = _getDetailIdStrengthParallax();
|
||||
Var *normalMap = _getNormalMapTex();
|
||||
Var *texCoord = _getInDetailCoord( componentList );
|
||||
|
||||
// Call the library function to do the rest.
|
||||
meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @.z );\r\n",
|
||||
texCoord, normalMap, texCoord, negViewTS, detailInfo ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainParallaxMapFeatGLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
|
||||
// If this is the first parallax feature then
|
||||
// it will generate the tangetEye vector and
|
||||
// the worldToTanget transform.
|
||||
if ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() - 1 ) )
|
||||
res.numTexReg = 4;
|
||||
|
||||
// If this isn't the prepass then we will
|
||||
// be adding a normal map.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
res.numTex = 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TerrainLightMapFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// grab connector texcoord register
|
||||
Var *inTex = (Var*)LangElement::find( "outTexCoord" );
|
||||
if ( !inTex )
|
||||
return;
|
||||
|
||||
// Get the lightmap texture.
|
||||
Var *lightMap = new Var;
|
||||
lightMap->setType( "sampler2D" );
|
||||
lightMap->setName( "lightMapTex" );
|
||||
lightMap->uniform = true;
|
||||
lightMap->sampler = true;
|
||||
lightMap->constNum = Var::getTexUnitNum();
|
||||
|
||||
// Create a 'lightMask' value which is read by
|
||||
// RTLighting to mask out the directional lighting.
|
||||
Var *lightMask = new Var;
|
||||
lightMask->setType( "vec3" );
|
||||
lightMask->setName( "lightMask" );
|
||||
|
||||
output = new GenOp( " @ = texture2D( @, @.xy ).rgb;\r\n", new DecOp( lightMask ), lightMap, inTex );
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainLightMapFeatGLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
res.numTex = 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void TerrainAdditiveFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
Var *color = (Var*) LangElement::find( "col" );
|
||||
Var *blendTotal = (Var*)LangElement::find( "blendTotal" );
|
||||
if ( !color || !blendTotal )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
meta->addStatement( new GenOp( " if ( @ - 0.0001 < 0.0 ) discard;\r\n", blendTotal ) );
|
||||
meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
145
Engine/source/terrain/glsl/terrFeatureGLSL.h
Normal file
145
Engine/source/terrain/glsl/terrFeatureGLSL.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRFEATUREGLSL_H_
|
||||
#define _TERRFEATUREGLSL_H_
|
||||
|
||||
#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_
|
||||
#include "shaderGen/GLSL/shaderFeatureGLSL.h"
|
||||
#endif
|
||||
#ifndef _LANG_ELEMENT_H_
|
||||
#include "shaderGen/langElement.h"
|
||||
#endif
|
||||
|
||||
/// A shared base class for terrain features which
|
||||
/// includes some helper functions.
|
||||
class TerrainFeatGLSL : public ShaderFeatureGLSL
|
||||
{
|
||||
protected:
|
||||
|
||||
Var* _getInDetailCoord(Vector<ShaderComponent*> &componentList );
|
||||
|
||||
Var* _getNormalMapTex();
|
||||
|
||||
static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp );
|
||||
|
||||
Var* _getDetailIdStrengthParallax();
|
||||
|
||||
};
|
||||
|
||||
class TerrainBaseMapFeatGLSL : public TerrainFeatGLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Base Texture"; }
|
||||
};
|
||||
|
||||
class TerrainDetailMapFeatGLSL : public TerrainFeatGLSL
|
||||
{
|
||||
protected:
|
||||
|
||||
ShaderIncludeDependency mTerrainDep;
|
||||
|
||||
public:
|
||||
|
||||
TerrainDetailMapFeatGLSL();
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Detail Texture"; }
|
||||
};
|
||||
|
||||
|
||||
class TerrainNormalMapFeatGLSL : public TerrainFeatGLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Normal Texture"; }
|
||||
};
|
||||
|
||||
class TerrainParallaxMapFeatGLSL : public TerrainFeatGLSL
|
||||
{
|
||||
protected:
|
||||
|
||||
ShaderIncludeDependency mIncludeDep;
|
||||
|
||||
public:
|
||||
|
||||
TerrainParallaxMapFeatGLSL();
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Parallax Texture"; }
|
||||
};
|
||||
|
||||
class TerrainLightMapFeatGLSL : public TerrainFeatGLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Lightmap Texture"; }
|
||||
};
|
||||
|
||||
|
||||
class TerrainAdditiveFeatGLSL : public TerrainFeatGLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Additive"; }
|
||||
};
|
||||
|
||||
#endif // _TERRFEATUREGLSL_H_
|
||||
712
Engine/source/terrain/hlsl/terrFeatureHLSL.cpp
Normal file
712
Engine/source/terrain/hlsl/terrFeatureHLSL.cpp
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/hlsl/terrFeatureHLSL.h"
|
||||
|
||||
#include "terrain/terrFeatureTypes.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "materials/materialFeatureData.h"
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "shaderGen/langElement.h"
|
||||
#include "shaderGen/shaderOp.h"
|
||||
#include "shaderGen/featureMgr.h"
|
||||
#include "core/module.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( TerrainFeatHLSL )
|
||||
|
||||
MODULE_INIT_AFTER( ShaderGenFeatureMgr )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
FEATUREMGR->registerFeature( MFT_TerrainBaseMap, new TerrainBaseMapFeatHLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainParallaxMap, new NamedFeatureHLSL( "Terrain Parallax Texture" ) );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainDetailMap, new TerrainDetailMapFeatHLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainNormalMap, new TerrainNormalMapFeatHLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) );
|
||||
FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatHLSL );
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
Var* TerrainFeatHLSL::_getUniformVar( const char *name, const char *type, ConstantSortPosition csp )
|
||||
{
|
||||
Var *theVar = (Var*)LangElement::find( name );
|
||||
if ( !theVar )
|
||||
{
|
||||
theVar = new Var;
|
||||
theVar->setType( type );
|
||||
theVar->setName( name );
|
||||
theVar->uniform = true;
|
||||
theVar->constSortPos = csp;
|
||||
}
|
||||
|
||||
return theVar;
|
||||
}
|
||||
|
||||
Var* TerrainFeatHLSL::_getInDetailCoord( Vector<ShaderComponent*> &componentList )
|
||||
{
|
||||
String name( String::ToString( "detCoord%d", getProcessIndex() ) );
|
||||
Var *inDet = (Var*)LangElement::find( name );
|
||||
|
||||
if ( !inDet )
|
||||
{
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
|
||||
inDet = connectComp->getElement( RT_TEXCOORD );
|
||||
inDet->setName( name );
|
||||
inDet->setStructName( "IN" );
|
||||
inDet->setType( "float4" );
|
||||
inDet->mapsToSampler = true;
|
||||
}
|
||||
|
||||
return inDet;
|
||||
}
|
||||
|
||||
Var* TerrainFeatHLSL::_getNormalMapTex()
|
||||
{
|
||||
String name( String::ToString( "normalMap%d", getProcessIndex() ) );
|
||||
Var *normalMap = (Var*)LangElement::find( name );
|
||||
|
||||
if ( !normalMap )
|
||||
{
|
||||
normalMap = new Var;
|
||||
normalMap->setType( "sampler2D" );
|
||||
normalMap->setName( name );
|
||||
normalMap->uniform = true;
|
||||
normalMap->sampler = true;
|
||||
normalMap->constNum = Var::getTexUnitNum();
|
||||
}
|
||||
|
||||
return normalMap;
|
||||
}
|
||||
|
||||
Var* TerrainFeatHLSL::_getDetailIdStrengthParallax()
|
||||
{
|
||||
String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) );
|
||||
|
||||
Var *detailInfo = (Var*)LangElement::find( name );
|
||||
if ( !detailInfo )
|
||||
{
|
||||
detailInfo = new Var;
|
||||
detailInfo->setType( "float3" );
|
||||
detailInfo->setName( name );
|
||||
detailInfo->uniform = true;
|
||||
detailInfo->constSortPos = cspPotentialPrimitive;
|
||||
}
|
||||
|
||||
return detailInfo;
|
||||
}
|
||||
|
||||
void TerrainBaseMapFeatHLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
MultiLine *meta = new MultiLine;
|
||||
output = meta;
|
||||
|
||||
// Generate the incoming texture var.
|
||||
Var *inTex;
|
||||
{
|
||||
Var *inPos = (Var*)LangElement::find( "inPosition" );
|
||||
if ( !inPos )
|
||||
inPos = (Var*)LangElement::find( "position" );
|
||||
|
||||
inTex = new Var( "texCoord", "float3" );
|
||||
|
||||
Var *oneOverTerrainSize = _getUniformVar( "oneOverTerrainSize", "float", cspPass );
|
||||
|
||||
// NOTE: The y coord here should be negative to have
|
||||
// the texture maps not end up flipped which also caused
|
||||
// normal and parallax mapping to be incorrect.
|
||||
//
|
||||
// This mistake early in development means that the layer
|
||||
// id bilinear blend depends on it being that way.
|
||||
//
|
||||
// So instead i fixed this by flipping the base and detail
|
||||
// coord y scale to compensate when rendering.
|
||||
//
|
||||
meta->addStatement( new GenOp( " @ = @.xyz * float3( @, @, -@ );\r\n",
|
||||
new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) );
|
||||
}
|
||||
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
|
||||
// Pass the texture coord to the pixel shader.
|
||||
Var *outTex = connectComp->getElement( RT_TEXCOORD );
|
||||
outTex->setName( "outTexCoord" );
|
||||
outTex->setStructName( "OUT" );
|
||||
outTex->setType( "float3" );
|
||||
outTex->mapsToSampler = true;
|
||||
meta->addStatement( new GenOp( " @.xy = @.xy;\r\n", outTex, inTex ) );
|
||||
|
||||
// If this shader has a side projected layer then we
|
||||
// pass the dot product between the +Y and the normal
|
||||
// thru outTexCoord.z for use in blending the textures.
|
||||
if ( fd.features.hasFeature( MFT_TerrainSideProject ) )
|
||||
{
|
||||
Var *inNormal = (Var*)LangElement::find( "normal" );
|
||||
meta->addStatement(
|
||||
new GenOp( " @.z = pow( abs( dot( normalize( float3( @.x, @.y, 0 ) ), float3( 0, 1, 0 ) ) ), 10.0 );\r\n",
|
||||
outTex, inNormal, inNormal ) );
|
||||
}
|
||||
else
|
||||
meta->addStatement( new GenOp( " @.z = 0;\r\n", outTex ) );
|
||||
|
||||
// HACK: This is sort of lazy... we generate the tanget
|
||||
// vector here so that we're sure it exists in the parallax
|
||||
// and normal features which will expect "T" to exist.
|
||||
//
|
||||
// If this shader doesn't use it the shader compiler will
|
||||
// optimize away this code.
|
||||
//
|
||||
Var *inTangentZ = getVertTexCoord( "tcTangentZ" );
|
||||
Var *inTanget = new Var( "T", "float3" );
|
||||
Var *squareSize = _getUniformVar( "squareSize", "float", cspPass );
|
||||
meta->addStatement( new GenOp( " @ = normalize( float3( @, 0, @ ) );\r\n",
|
||||
new DecOp( inTanget ), squareSize, inTangentZ ) );
|
||||
}
|
||||
|
||||
void TerrainBaseMapFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// grab connector texcoord register
|
||||
Var *texCoord = getInTexCoord( "texCoord", "float3", true, componentList );
|
||||
|
||||
// We do nothing more if this is a prepass.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
return;
|
||||
|
||||
// create texture var
|
||||
Var *diffuseMap = new Var;
|
||||
diffuseMap->setType( "sampler2D" );
|
||||
diffuseMap->setName( "baseTexMap" );
|
||||
diffuseMap->uniform = true;
|
||||
diffuseMap->sampler = true;
|
||||
diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
Var *baseColor = new Var;
|
||||
baseColor->setType( "float4" );
|
||||
baseColor->setName( "baseColor" );
|
||||
meta->addStatement( new GenOp( " @ = tex2D( @, @.xy );\r\n", new DecOp( baseColor ), diffuseMap, texCoord ) );
|
||||
meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul ) ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainBaseMapFeatHLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
res.numTexReg = 1;
|
||||
|
||||
// We only sample from the base map during a diffuse pass.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
res.numTex = 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TerrainDetailMapFeatHLSL::TerrainDetailMapFeatHLSL()
|
||||
: mTorqueDep( "shaders/common/torque.hlsl" ),
|
||||
mTerrainDep( "shaders/common/terrain/terrain.hlsl" )
|
||||
|
||||
{
|
||||
addDependency( &mTorqueDep );
|
||||
addDependency( &mTerrainDep );
|
||||
}
|
||||
|
||||
void TerrainDetailMapFeatHLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
const U32 detailIndex = getProcessIndex();
|
||||
|
||||
// Grab incoming texture coords... the base map feature
|
||||
// made sure this was created.
|
||||
Var *inTex = (Var*)LangElement::find( "texCoord" );
|
||||
AssertFatal( inTex, "The texture coord is missing!" );
|
||||
|
||||
// Grab the input position.
|
||||
Var *inPos = (Var*)LangElement::find( "inPosition" );
|
||||
if ( !inPos )
|
||||
inPos = (Var*)LangElement::find( "position" );
|
||||
|
||||
// Get the object space eye position.
|
||||
Var *eyePos = _getUniformVar( "eyePos", "float3", cspPotentialPrimitive );
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// If we have parallax mapping then make sure we've sent
|
||||
// the negative view vector to the pixel shader.
|
||||
if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) &&
|
||||
!LangElement::find( "outNegViewTS" ) )
|
||||
{
|
||||
// Get the object to tangent transform which
|
||||
// will consume 3 output registers.
|
||||
Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta, fd );
|
||||
|
||||
// Now use a single output register to send the negative
|
||||
// view vector in tangent space to the pixel shader.
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD );
|
||||
outNegViewTS->setName( "outNegViewTS" );
|
||||
outNegViewTS->setStructName( "OUT" );
|
||||
outNegViewTS->setType( "float3" );
|
||||
meta->addStatement( new GenOp( " @ = mul( @, float3( @ - @.xyz ) );\r\n",
|
||||
outNegViewTS, objToTangentSpace, eyePos, inPos ) );
|
||||
}
|
||||
|
||||
// Get the distance from the eye to this vertex.
|
||||
Var *dist = (Var*)LangElement::find( "dist" );
|
||||
if ( !dist )
|
||||
{
|
||||
dist = new Var;
|
||||
dist->setType( "float" );
|
||||
dist->setName( "dist" );
|
||||
|
||||
meta->addStatement( new GenOp( " @ = distance( @.xyz, @ );\r\n",
|
||||
new DecOp( dist ), inPos, eyePos ) );
|
||||
}
|
||||
|
||||
// grab connector texcoord register
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
Var *outTex = connectComp->getElement( RT_TEXCOORD );
|
||||
outTex->setName( String::ToString( "detCoord%d", detailIndex ) );
|
||||
outTex->setStructName( "OUT" );
|
||||
outTex->setType( "float4" );
|
||||
outTex->mapsToSampler = true;
|
||||
|
||||
// Get the detail scale and fade info.
|
||||
Var *detScaleAndFade = new Var;
|
||||
detScaleAndFade->setType( "float4" );
|
||||
detScaleAndFade->setName( String::ToString( "detailScaleAndFade%d", detailIndex ) );
|
||||
detScaleAndFade->uniform = true;
|
||||
detScaleAndFade->constSortPos = cspPotentialPrimitive;
|
||||
|
||||
// Setup the detail coord.
|
||||
//
|
||||
// NOTE: You see here we scale the texture coord by 'xyx'
|
||||
// to generate the detail coord. This y is here because
|
||||
// its scale is flipped to correct for the non negative y
|
||||
// in texCoord.
|
||||
//
|
||||
// See TerrainBaseMapFeatHLSL::processVert().
|
||||
//
|
||||
meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) );
|
||||
|
||||
// And sneak the detail fade thru the w detailCoord.
|
||||
meta->addStatement( new GenOp( " @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n",
|
||||
outTex, detScaleAndFade, dist, detScaleAndFade ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
void TerrainDetailMapFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
const U32 detailIndex = getProcessIndex();
|
||||
Var *inTex = getVertTexCoord( "texCoord" );
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// We need the negative tangent space view vector
|
||||
// as in parallax mapping we step towards the camera.
|
||||
Var *negViewTS = (Var*)LangElement::find( "negViewTS" );
|
||||
if ( !negViewTS &&
|
||||
fd.features.hasFeature( MFT_TerrainParallaxMap ) )
|
||||
{
|
||||
Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" );
|
||||
if ( !inNegViewTS )
|
||||
{
|
||||
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
|
||||
inNegViewTS = connectComp->getElement( RT_TEXCOORD );
|
||||
inNegViewTS->setName( "outNegViewTS" );
|
||||
inNegViewTS->setStructName( "IN" );
|
||||
inNegViewTS->setType( "float3" );
|
||||
}
|
||||
|
||||
negViewTS = new Var( "negViewTS", "float3" );
|
||||
meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) );
|
||||
}
|
||||
|
||||
// Get the layer samples.
|
||||
Var *layerSample = (Var*)LangElement::find( "layerSample" );
|
||||
if ( !layerSample )
|
||||
{
|
||||
layerSample = new Var;
|
||||
layerSample->setType( "float4" );
|
||||
layerSample->setName( "layerSample" );
|
||||
|
||||
// Get the layer texture var
|
||||
Var *layerTex = new Var;
|
||||
layerTex->setType( "sampler2D" );
|
||||
layerTex->setName( "layerTex" );
|
||||
layerTex->uniform = true;
|
||||
layerTex->sampler = true;
|
||||
layerTex->constNum = Var::getTexUnitNum();
|
||||
|
||||
// Read the layer texture to get the samples.
|
||||
meta->addStatement( new GenOp( " @ = round( tex2D( @, @.xy ) * 255.0f );\r\n",
|
||||
new DecOp( layerSample ), layerTex, inTex ) );
|
||||
}
|
||||
|
||||
Var *layerSize = (Var*)LangElement::find( "layerSize" );
|
||||
if ( !layerSize )
|
||||
{
|
||||
layerSize = new Var;
|
||||
layerSize->setType( "float" );
|
||||
layerSize->setName( "layerSize" );
|
||||
layerSize->uniform = true;
|
||||
layerSize->constSortPos = cspPass;
|
||||
}
|
||||
|
||||
// Grab the incoming detail coord.
|
||||
Var *inDet = _getInDetailCoord( componentList );
|
||||
|
||||
// Get the detail id.
|
||||
Var *detailInfo = _getDetailIdStrengthParallax();
|
||||
|
||||
// Create the detail blend var.
|
||||
Var *detailBlend = new Var;
|
||||
detailBlend->setType( "float" );
|
||||
detailBlend->setName( String::ToString( "detailBlend%d", detailIndex ) );
|
||||
|
||||
// Calculate the blend for this detail texture.
|
||||
meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n",
|
||||
new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) );
|
||||
|
||||
// Get a var and accumulate the blend amount.
|
||||
Var *blendTotal = (Var*)LangElement::find( "blendTotal" );
|
||||
if ( !blendTotal )
|
||||
{
|
||||
blendTotal = new Var;
|
||||
blendTotal->setName( "blendTotal" );
|
||||
blendTotal->setType( "float" );
|
||||
meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) );
|
||||
}
|
||||
|
||||
// Add to the blend total.
|
||||
meta->addStatement( new GenOp( " @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend ) );
|
||||
|
||||
// If we had a parallax feature... then factor in the parallax
|
||||
// amount so that it fades out with the layer blending.
|
||||
if ( fd.features.hasFeature( MFT_TerrainParallaxMap, detailIndex ) )
|
||||
{
|
||||
// Get the rest of our inputs.
|
||||
Var *normalMap = _getNormalMapTex();
|
||||
|
||||
// Call the library function to do the rest.
|
||||
meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @.z * @ );\r\n",
|
||||
inDet, normalMap, inDet, negViewTS, detailInfo, detailBlend ) );
|
||||
}
|
||||
|
||||
// If this is a prepass then we skip color.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
{
|
||||
// Check to see if we have a gbuffer normal.
|
||||
Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
|
||||
|
||||
// If we have a gbuffer normal and we don't have a
|
||||
// normal map feature then we need to lerp in a
|
||||
// default normal else the normals below this layer
|
||||
// will show thru.
|
||||
if ( gbNormal &&
|
||||
!fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) )
|
||||
{
|
||||
Var *viewToTangent = getInViewToTangent( componentList );
|
||||
|
||||
meta->addStatement( new GenOp( " @ = lerp( @, @[2], min( @, @.w ) );\r\n",
|
||||
gbNormal, gbNormal, viewToTangent, detailBlend, inDet ) );
|
||||
}
|
||||
|
||||
output = meta;
|
||||
return;
|
||||
}
|
||||
|
||||
Var *detailColor = (Var*)LangElement::find( "detailColor" );
|
||||
if ( !detailColor )
|
||||
{
|
||||
detailColor = new Var;
|
||||
detailColor->setType( "float4" );
|
||||
detailColor->setName( "detailColor" );
|
||||
meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) );
|
||||
}
|
||||
|
||||
// Get the detail texture.
|
||||
Var *detailMap = new Var;
|
||||
detailMap->setType( "sampler2D" );
|
||||
detailMap->setName( String::ToString( "detailMap%d", detailIndex ) );
|
||||
detailMap->uniform = true;
|
||||
detailMap->sampler = true;
|
||||
detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here
|
||||
|
||||
// If we're using SM 3.0 then take advantage of
|
||||
// dynamic branching to skip layers per-pixel.
|
||||
|
||||
|
||||
if ( GFX->getPixelShaderVersion() >= 3.0f )
|
||||
meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) );
|
||||
|
||||
meta->addStatement( new GenOp( " {\r\n" ) );
|
||||
|
||||
// Note that we're doing the standard greyscale detail
|
||||
// map technique here which can darken and lighten the
|
||||
// diffuse texture.
|
||||
//
|
||||
// We take two color samples and lerp between them for
|
||||
// side projection layers... else a single sample.
|
||||
//
|
||||
if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) )
|
||||
{
|
||||
meta->addStatement( new GenOp( " @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n",
|
||||
detailColor, detailMap, inDet, detailMap, inDet, inTex ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
meta->addStatement( new GenOp( " @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n",
|
||||
detailColor, detailMap, inDet ) );
|
||||
}
|
||||
|
||||
meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n",
|
||||
detailColor, detailInfo, inDet ) );
|
||||
|
||||
Var *baseColor = (Var*)LangElement::find( "baseColor" );
|
||||
Var *outColor = (Var*)LangElement::find( "col" );
|
||||
|
||||
meta->addStatement( new GenOp( " @ = lerp( @, @ + @, @ );\r\n",
|
||||
outColor, outColor, baseColor, detailColor, detailBlend ) );
|
||||
|
||||
meta->addStatement( new GenOp( " }\r\n" ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainDetailMapFeatHLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
|
||||
if ( getProcessIndex() == 0 )
|
||||
{
|
||||
// If this is the first detail pass then we
|
||||
// samples from the layer tex.
|
||||
res.numTex += 1;
|
||||
|
||||
// If this material also does parallax then it
|
||||
// will generate the negative view vector and the
|
||||
// worldToTanget transform.
|
||||
if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) )
|
||||
res.numTexReg += 4;
|
||||
}
|
||||
|
||||
// If this isn't the prepass then we sample
|
||||
// from the detail texture for diffuse coloring.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
res.numTex += 1;
|
||||
|
||||
// If we have parallax for this layer then we'll also
|
||||
// be sampling the normal map for the parallax heightmap.
|
||||
if ( fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() ) )
|
||||
res.numTex += 1;
|
||||
|
||||
// Finally we always send the detail texture
|
||||
// coord to the pixel shader.
|
||||
res.numTexReg += 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TerrainNormalMapFeatHLSL::processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// We only need to process normals during the prepass.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// Make sure the world to tangent transform
|
||||
// is created and available for the pixel shader.
|
||||
getOutViewToTangent( componentList, meta, fd );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
void TerrainNormalMapFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// We only need to process normals during the prepass.
|
||||
if ( !fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
Var *viewToTangent = getInViewToTangent( componentList );
|
||||
|
||||
// This var is read from GBufferConditionerHLSL and
|
||||
// used in the prepass output.
|
||||
Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
|
||||
if ( !gbNormal )
|
||||
{
|
||||
gbNormal = new Var;
|
||||
gbNormal->setName( "gbNormal" );
|
||||
gbNormal->setType( "float3" );
|
||||
meta->addStatement( new GenOp( " @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) );
|
||||
}
|
||||
|
||||
const U32 normalIndex = getProcessIndex();
|
||||
|
||||
Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) );
|
||||
AssertFatal( detailBlend, "The detail blend is missing!" );
|
||||
|
||||
// If we're using SM 3.0 then take advantage of
|
||||
// dynamic branching to skip layers per-pixel.
|
||||
if ( GFX->getPixelShaderVersion() >= 3.0f )
|
||||
meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) );
|
||||
|
||||
meta->addStatement( new GenOp( " {\r\n" ) );
|
||||
|
||||
// Get the normal map texture.
|
||||
Var *normalMap = _getNormalMapTex();
|
||||
|
||||
/// Get the texture coord.
|
||||
Var *inDet = _getInDetailCoord( componentList );
|
||||
Var *inTex = getVertTexCoord( "texCoord" );
|
||||
|
||||
// Sample the normal map.
|
||||
//
|
||||
// We take two normal samples and lerp between them for
|
||||
// side projection layers... else a single sample.
|
||||
LangElement *texOp;
|
||||
if ( fd.features.hasFeature( MFT_TerrainSideProject, normalIndex ) )
|
||||
{
|
||||
texOp = new GenOp( "lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )",
|
||||
normalMap, inDet, normalMap, inDet, inTex );
|
||||
}
|
||||
else
|
||||
texOp = new GenOp( "tex2D(@, @.xy)", normalMap, inDet );
|
||||
|
||||
// create bump normal
|
||||
Var *bumpNorm = new Var;
|
||||
bumpNorm->setName( "bumpNormal" );
|
||||
bumpNorm->setType( "float4" );
|
||||
|
||||
LangElement *bumpNormDecl = new DecOp( bumpNorm );
|
||||
meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
|
||||
|
||||
// Normalize is done later...
|
||||
// Note: The reverse mul order is intentional. Affine matrix.
|
||||
meta->addStatement( new GenOp( " @ = lerp( @, mul( @.xyz, @ ), min( @, @.w ) );\r\n",
|
||||
gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) );
|
||||
|
||||
// End the conditional block.
|
||||
meta->addStatement( new GenOp( " }\r\n" ) );
|
||||
|
||||
// If this is the last normal map then we
|
||||
// can test to see the total blend value
|
||||
// to see if we should clip the result.
|
||||
//if ( fd.features.getNextFeatureIndex( MFT_TerrainNormalMap, normalIndex ) == -1 )
|
||||
//meta->addStatement( new GenOp( " clip( @ - 0.0001f );\r\n", blendTotal ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainNormalMapFeatHLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
|
||||
// We only need to process normals during the prepass.
|
||||
if ( fd.features.hasFeature( MFT_PrePassConditioner ) )
|
||||
{
|
||||
// If this is the first normal map and there
|
||||
// are no parallax features then we will
|
||||
// generate the worldToTanget transform.
|
||||
if ( !fd.features.hasFeature( MFT_TerrainParallaxMap ) &&
|
||||
( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) )
|
||||
res.numTexReg = 3;
|
||||
|
||||
res.numTex = 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TerrainLightMapFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
// grab connector texcoord register
|
||||
Var *inTex = (Var*)LangElement::find( "texCoord" );
|
||||
if ( !inTex )
|
||||
return;
|
||||
|
||||
// Get the lightmap texture.
|
||||
Var *lightMap = new Var;
|
||||
lightMap->setType( "sampler2D" );
|
||||
lightMap->setName( "lightMapTex" );
|
||||
lightMap->uniform = true;
|
||||
lightMap->sampler = true;
|
||||
lightMap->constNum = Var::getTexUnitNum();
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
// Find or create the lightMask value which is read by
|
||||
// RTLighting to mask out the lights.
|
||||
//
|
||||
// The first light is always the sunlight so we apply
|
||||
// the shadow mask to only the first channel.
|
||||
//
|
||||
Var *lightMask = (Var*)LangElement::find( "lightMask" );
|
||||
if ( !lightMask )
|
||||
{
|
||||
lightMask = new Var( "lightMask", "float4" );
|
||||
meta->addStatement( new GenOp( " @ = 1;\r\n", new DecOp( lightMask ) ) );
|
||||
}
|
||||
|
||||
meta->addStatement( new GenOp( " @[0] = tex2D( @, @.xy ).r;\r\n", lightMask, lightMap, inTex ) );
|
||||
output = meta;
|
||||
}
|
||||
|
||||
ShaderFeature::Resources TerrainLightMapFeatHLSL::getResources( const MaterialFeatureData &fd )
|
||||
{
|
||||
Resources res;
|
||||
res.numTex = 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void TerrainAdditiveFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd )
|
||||
{
|
||||
Var *color = (Var*) LangElement::find( "col" );
|
||||
Var *blendTotal = (Var*)LangElement::find( "blendTotal" );
|
||||
if ( !color || !blendTotal )
|
||||
return;
|
||||
|
||||
MultiLine *meta = new MultiLine;
|
||||
|
||||
meta->addStatement( new GenOp( " clip( @ - 0.0001 );\r\n", blendTotal ) );
|
||||
meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) );
|
||||
|
||||
output = meta;
|
||||
}
|
||||
128
Engine/source/terrain/hlsl/terrFeatureHLSL.h
Normal file
128
Engine/source/terrain/hlsl/terrFeatureHLSL.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRFEATUREHLSL_H_
|
||||
#define _TERRFEATUREHLSL_H_
|
||||
|
||||
#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_
|
||||
#include "shaderGen/HLSL/shaderFeatureHLSL.h"
|
||||
#endif
|
||||
#ifndef _LANG_ELEMENT_H_
|
||||
#include "shaderGen/langElement.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// A shared base class for terrain features which
|
||||
/// includes some helper functions.
|
||||
class TerrainFeatHLSL : public ShaderFeatureHLSL
|
||||
{
|
||||
protected:
|
||||
|
||||
Var* _getInDetailCoord(Vector<ShaderComponent*> &componentList );
|
||||
|
||||
Var* _getNormalMapTex();
|
||||
|
||||
static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp );
|
||||
|
||||
Var* _getDetailIdStrengthParallax();
|
||||
|
||||
};
|
||||
|
||||
|
||||
class TerrainBaseMapFeatHLSL : public TerrainFeatHLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Base Texture"; }
|
||||
};
|
||||
|
||||
|
||||
class TerrainDetailMapFeatHLSL : public TerrainFeatHLSL
|
||||
{
|
||||
protected:
|
||||
|
||||
ShaderIncludeDependency mTorqueDep;
|
||||
ShaderIncludeDependency mTerrainDep;
|
||||
|
||||
public:
|
||||
|
||||
TerrainDetailMapFeatHLSL();
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Detail Texture"; }
|
||||
};
|
||||
|
||||
|
||||
class TerrainNormalMapFeatHLSL : public TerrainFeatHLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processVert( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Normal Texture"; }
|
||||
};
|
||||
|
||||
class TerrainLightMapFeatHLSL : public TerrainFeatHLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual Resources getResources( const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Lightmap Texture"; }
|
||||
};
|
||||
|
||||
|
||||
class TerrainAdditiveFeatHLSL : public TerrainFeatHLSL
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void processPix( Vector<ShaderComponent*> &componentList,
|
||||
const MaterialFeatureData &fd );
|
||||
|
||||
virtual String getName() { return "Terrain Additive"; }
|
||||
};
|
||||
|
||||
#endif // _TERRFEATUREHLSL_H_
|
||||
1091
Engine/source/terrain/terrCell.cpp
Normal file
1091
Engine/source/terrain/terrCell.cpp
Normal file
File diff suppressed because it is too large
Load diff
248
Engine/source/terrain/terrCell.h
Normal file
248
Engine/source/terrain/terrCell.h
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRCELL_H_
|
||||
#define _TERRCELL_H_
|
||||
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
#ifndef _MORIENTEDBOX_H_
|
||||
#include "math/mOrientedBox.h"
|
||||
#endif
|
||||
#ifndef _BITVECTOR_H_
|
||||
#include "core/bitVector.h"
|
||||
#endif
|
||||
|
||||
class TerrainBlock;
|
||||
class TerrainCellMaterial;
|
||||
class Frustum;
|
||||
class SceneRenderState;
|
||||
class SceneZoneSpaceManager;
|
||||
|
||||
|
||||
/// The TerrainCell vertex format optimized to
|
||||
/// 32 bytes for optimal vertex cache performance.
|
||||
GFXDeclareVertexFormat( TerrVertex )
|
||||
{
|
||||
/// The position.
|
||||
Point3F point;
|
||||
|
||||
/// The normal.
|
||||
Point3F normal;
|
||||
|
||||
/// The height for calculating the
|
||||
/// tangent vector on the GPU.
|
||||
F32 tangentZ;
|
||||
|
||||
/// The empty flag state which is either
|
||||
/// -1 or 1 so we can do the special
|
||||
/// interpolation trick.
|
||||
F32 empty;
|
||||
};
|
||||
|
||||
|
||||
/// The TerrCell is a single quadrant of the terrain geometry quadtree.
|
||||
class TerrCell
|
||||
{
|
||||
protected:
|
||||
|
||||
/// The handle to the static vertex buffer which holds the
|
||||
/// vertices for this cell.
|
||||
GFXVertexBufferHandle<TerrVertex> mVertexBuffer;
|
||||
|
||||
/// The handle to the static primitive buffer for this cell.
|
||||
/// It is only used if this cell has any empty squares
|
||||
GFXPrimitiveBufferHandle mPrimBuffer;
|
||||
|
||||
///
|
||||
Point2I mPoint;
|
||||
|
||||
///
|
||||
U32 mSize;
|
||||
|
||||
/// The level of this cell within the quadtree (of cells) where
|
||||
/// zero is the root and one is a direct child of the root, etc.
|
||||
U32 mLevel;
|
||||
|
||||
/// Statics used in VB and PB generation.
|
||||
static const U32 smVBStride;
|
||||
static const U32 smMinCellSize;
|
||||
static const U32 smVBSize;
|
||||
static const U32 smPBSize;
|
||||
static const U32 smTriCount;
|
||||
|
||||
/// Triangle count for our own primitive buffer, if any
|
||||
U32 mTriCount;
|
||||
|
||||
/// Indicates if this cell has any empty squares
|
||||
bool mHasEmpty;
|
||||
|
||||
/// A list of all empty vertices for this cell
|
||||
Vector<U32> mEmptyVertexList;
|
||||
|
||||
/// The terrain this cell is based on.
|
||||
TerrainBlock *mTerrain;
|
||||
|
||||
/// The material used to render the cell.
|
||||
TerrainCellMaterial *mMaterial;
|
||||
|
||||
/// The bounding box of this cell in
|
||||
/// TerrainBlock object space.
|
||||
Box3F mBounds;
|
||||
|
||||
/// The OBB of this cell in world space.
|
||||
OrientedBox3F mOBB;
|
||||
|
||||
/// The bounding radius of this cell.
|
||||
F32 mRadius;
|
||||
|
||||
/// The child cells of this one.
|
||||
TerrCell *mChildren[4];
|
||||
|
||||
/// This bit flag tells us which materials effect
|
||||
/// this cell and is used for optimizing rendering.
|
||||
/// @see TerrainFile::mMaterialAlphaMap
|
||||
U64 mMaterials;
|
||||
|
||||
/// Whether this cell is fully contained inside interior zones.
|
||||
bool mIsInteriorOnly;
|
||||
|
||||
/// The zone overlap for this cell.
|
||||
/// @note The bit for the outdoor zone is never set.
|
||||
BitVector mZoneOverlap;
|
||||
|
||||
///
|
||||
void _updateBounds();
|
||||
|
||||
/// Update #mOBB from the current terrain transform state.
|
||||
void _updateOBB();
|
||||
|
||||
//
|
||||
void _init( TerrainBlock *terrain,
|
||||
const Point2I &point,
|
||||
U32 size,
|
||||
U32 level );
|
||||
|
||||
//
|
||||
void _updateVertexBuffer();
|
||||
|
||||
//
|
||||
void _updatePrimitiveBuffer();
|
||||
|
||||
//
|
||||
void _updateMaterials();
|
||||
|
||||
//
|
||||
bool _isVertIndexEmpty( U32 index ) const;
|
||||
|
||||
public:
|
||||
|
||||
TerrCell();
|
||||
virtual ~TerrCell();
|
||||
|
||||
static TerrCell* init( TerrainBlock *terrain );
|
||||
|
||||
void getRenderPrimitive( GFXPrimitive *prim,
|
||||
GFXVertexBufferHandleBase *vertBuff,
|
||||
GFXPrimitiveBufferHandle *primBuff ) const;
|
||||
|
||||
void updateGrid( const RectI &gridRect, bool opacityOnly = false );
|
||||
|
||||
/// Update the world-space OBBs used for culling.
|
||||
void updateOBBs();
|
||||
|
||||
///
|
||||
void updateZoning( const SceneZoneSpaceManager *zoneManager );
|
||||
|
||||
void cullCells( const SceneRenderState *state,
|
||||
const Point3F &objLodPos,
|
||||
Vector<TerrCell*> *outCells );
|
||||
|
||||
const Box3F& getBounds() const { return mBounds; }
|
||||
|
||||
/// Returns the object space sphere bounds.
|
||||
SphereF getSphereBounds() const { return SphereF( mBounds.getCenter(), mRadius ); }
|
||||
|
||||
F32 getSqDistanceTo( const Point3F &pt ) const;
|
||||
|
||||
F32 getDistanceTo( const Point3F &pt ) const;
|
||||
|
||||
U64 getMaterials() const { return mMaterials; }
|
||||
|
||||
/// Returns a bit vector of what zones overlap this cell.
|
||||
const BitVector& getZoneOverlap() const { return mZoneOverlap; }
|
||||
|
||||
/// Forces the loading of the materials for this
|
||||
/// cell and all its child cells.
|
||||
void preloadMaterials();
|
||||
|
||||
TerrainCellMaterial* getMaterial();
|
||||
|
||||
/// Return true if this is a leaf cell, i.e. a cell without children.
|
||||
bool isLeaf() const { return !mChildren[ 0 ]; }
|
||||
|
||||
/// Deletes the materials for this cell
|
||||
/// and all its children. They will be
|
||||
/// recreate on the next request.
|
||||
void deleteMaterials();
|
||||
|
||||
U32 getSize() const { return mSize; }
|
||||
|
||||
Point2I getPoint() const { return mPoint; }
|
||||
|
||||
/// Initializes a primitive buffer for rendering any cell.
|
||||
static void createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer );
|
||||
|
||||
/// Debug Rendering
|
||||
/// @{
|
||||
|
||||
/// Renders the debug bounds for this cell.
|
||||
void renderBounds() const;
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
inline F32 TerrCell::getDistanceTo( const Point3F &pt ) const
|
||||
{
|
||||
return ( mBounds.getCenter() - pt ).len() - mRadius;
|
||||
}
|
||||
|
||||
inline bool TerrCell::_isVertIndexEmpty( U32 index ) const
|
||||
{
|
||||
for ( U32 i = 0; i < mEmptyVertexList.size(); ++i )
|
||||
{
|
||||
if ( mEmptyVertexList[i] == index )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // _TERRCELL_H_
|
||||
780
Engine/source/terrain/terrCellMaterial.cpp
Normal file
780
Engine/source/terrain/terrCellMaterial.cpp
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrCellMaterial.h"
|
||||
|
||||
#include "terrain/terrData.h"
|
||||
#include "terrain/terrCell.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "terrain/terrFeatureTypes.h"
|
||||
#include "terrain/terrMaterial.h"
|
||||
#include "renderInstance/renderPrePassMgr.h"
|
||||
#include "shaderGen/shaderGen.h"
|
||||
#include "shaderGen/featureMgr.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "materials/sceneData.h"
|
||||
#include "gfx/util/screenspace.h"
|
||||
#include "lighting/advanced/advancedLightBinManager.h"
|
||||
|
||||
|
||||
AFTER_MODULE_INIT( MaterialManager )
|
||||
{
|
||||
Con::NotifyDelegate callabck( &TerrainCellMaterial::_updateDefaultAnisotropy );
|
||||
Con::addVariableNotify( "$pref::Video::defaultAnisotropy", callabck );
|
||||
}
|
||||
|
||||
Vector<TerrainCellMaterial*> TerrainCellMaterial::smAllMaterials;
|
||||
|
||||
TerrainCellMaterial::TerrainCellMaterial()
|
||||
: mCurrPass( 0 ),
|
||||
mTerrain( NULL ),
|
||||
mPrePassMat( NULL ),
|
||||
mReflectMat( NULL )
|
||||
{
|
||||
smAllMaterials.push_back( this );
|
||||
}
|
||||
|
||||
TerrainCellMaterial::~TerrainCellMaterial()
|
||||
{
|
||||
SAFE_DELETE( mPrePassMat );
|
||||
SAFE_DELETE( mReflectMat );
|
||||
smAllMaterials.remove( this );
|
||||
}
|
||||
|
||||
void TerrainCellMaterial::_updateDefaultAnisotropy()
|
||||
{
|
||||
// TODO: We need to split the stateblock initialization
|
||||
// from the shader constant lookup and pass setup in a
|
||||
// future version of terrain materials.
|
||||
//
|
||||
// For now use some custom code in a horrible loop to
|
||||
// change the anisotropy directly and fast.
|
||||
//
|
||||
|
||||
const U32 maxAnisotropy = MATMGR->getDefaultAnisotropy();
|
||||
|
||||
Vector<TerrainCellMaterial*>::iterator iter = smAllMaterials.begin();
|
||||
for ( ; iter != smAllMaterials.end(); iter++ )
|
||||
{
|
||||
for ( U32 p=0; p < (*iter)->mPasses.size(); p++ )
|
||||
{
|
||||
Pass &pass = (*iter)->mPasses[p];
|
||||
|
||||
// Start from the existing state block.
|
||||
GFXStateBlockDesc desc = pass.stateBlock->getDesc();
|
||||
|
||||
for ( U32 m=0; m < pass.materials.size(); m++ )
|
||||
{
|
||||
const MaterialInfo *matInfo = pass.materials[m];
|
||||
|
||||
if ( matInfo->detailTexConst->isValid() )
|
||||
{
|
||||
const S32 sampler = matInfo->detailTexConst->getSamplerRegister();
|
||||
|
||||
if ( maxAnisotropy > 1 )
|
||||
{
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
|
||||
desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
|
||||
}
|
||||
else
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
|
||||
}
|
||||
|
||||
if ( matInfo->normalTexConst->isValid() )
|
||||
{
|
||||
const S32 sampler = matInfo->normalTexConst->getSamplerRegister();
|
||||
|
||||
if ( maxAnisotropy > 1 )
|
||||
{
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
|
||||
desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
|
||||
}
|
||||
else
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
|
||||
}
|
||||
|
||||
} // for ( U32 m=0; m < pass.materials.size(); m++ )
|
||||
|
||||
// Set the updated stateblock.
|
||||
pass.stateBlock = GFX->createStateBlock( desc );
|
||||
|
||||
// Create the wireframe state blocks.
|
||||
GFXStateBlockDesc wireframe( desc );
|
||||
wireframe.fillMode = GFXFillWireframe;
|
||||
pass.wireframeStateBlock = GFX->createStateBlock( wireframe );
|
||||
|
||||
} // for ( U32 p=0; i < (*iter)->mPasses.size(); p++ )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TerrainCellMaterial::setTransformAndEye( const MatrixF &modelXfm,
|
||||
const MatrixF &viewXfm,
|
||||
const MatrixF &projectXfm,
|
||||
F32 farPlane )
|
||||
{
|
||||
PROFILE_SCOPE( TerrainCellMaterial_SetTransformAndEye );
|
||||
|
||||
MatrixF modelViewProj = projectXfm * viewXfm * modelXfm;
|
||||
|
||||
MatrixF invViewXfm( viewXfm );
|
||||
invViewXfm.inverse();
|
||||
Point3F eyePos = invViewXfm.getPosition();
|
||||
|
||||
MatrixF invModelXfm( modelXfm );
|
||||
invModelXfm.inverse();
|
||||
|
||||
Point3F objEyePos = eyePos;
|
||||
invModelXfm.mulP( objEyePos );
|
||||
|
||||
VectorF vEye = invViewXfm.getForwardVector();
|
||||
vEye.normalize( 1.0f / farPlane );
|
||||
|
||||
for ( U32 i=0; i < mPasses.size(); i++ )
|
||||
{
|
||||
Pass &pass = mPasses[i];
|
||||
|
||||
pass.consts->setSafe( pass.modelViewProjConst, modelViewProj );
|
||||
|
||||
if( pass.viewToObj->isValid() || pass.worldViewOnly->isValid() )
|
||||
{
|
||||
MatrixF worldViewOnly = viewXfm * modelXfm;
|
||||
|
||||
pass.consts->setSafe( pass.worldViewOnly, worldViewOnly );
|
||||
|
||||
if( pass.viewToObj->isValid() )
|
||||
{
|
||||
worldViewOnly.affineInverse();
|
||||
pass.consts->set( pass.viewToObj, worldViewOnly);
|
||||
}
|
||||
}
|
||||
|
||||
pass.consts->setSafe( pass.eyePosWorldConst, eyePos );
|
||||
pass.consts->setSafe( pass.eyePosConst, objEyePos );
|
||||
pass.consts->setSafe( pass.objTransConst, modelXfm );
|
||||
pass.consts->setSafe( pass.worldToObjConst, invModelXfm );
|
||||
pass.consts->setSafe( pass.vEyeConst, vEye );
|
||||
}
|
||||
}
|
||||
|
||||
TerrainCellMaterial* TerrainCellMaterial::getPrePassMat()
|
||||
{
|
||||
if ( !mPrePassMat )
|
||||
{
|
||||
mPrePassMat = new TerrainCellMaterial();
|
||||
mPrePassMat->init( mTerrain, mMaterials, true, false, mMaterials == 0 );
|
||||
}
|
||||
|
||||
return mPrePassMat;
|
||||
}
|
||||
|
||||
TerrainCellMaterial* TerrainCellMaterial::getReflectMat()
|
||||
{
|
||||
if ( !mReflectMat )
|
||||
{
|
||||
mReflectMat = new TerrainCellMaterial();
|
||||
mReflectMat->init( mTerrain, mMaterials, false, true, true );
|
||||
}
|
||||
|
||||
return mReflectMat;
|
||||
}
|
||||
|
||||
void TerrainCellMaterial::init( TerrainBlock *block,
|
||||
U64 activeMaterials,
|
||||
bool prePassMat,
|
||||
bool reflectMat,
|
||||
bool baseOnly )
|
||||
{
|
||||
// This isn't allowed for now.
|
||||
AssertFatal( !( prePassMat && reflectMat ), "TerrainCellMaterial::init - We shouldn't get prepass and reflection in the same material!" );
|
||||
|
||||
mTerrain = block;
|
||||
mMaterials = activeMaterials;
|
||||
|
||||
Vector<MaterialInfo*> materials;
|
||||
|
||||
for ( U32 i = 0; i < 64; i++ )
|
||||
{
|
||||
if ( !( mMaterials & ((U64)1 << i ) ) )
|
||||
continue;
|
||||
|
||||
TerrainMaterial *mat = block->getMaterial( i );
|
||||
|
||||
MaterialInfo *info = new MaterialInfo();
|
||||
info->layerId = i;
|
||||
info->mat = mat;
|
||||
materials.push_back( info );
|
||||
}
|
||||
|
||||
mCurrPass = 0;
|
||||
mPasses.clear();
|
||||
|
||||
// Ok... loop till we successfully generate all
|
||||
// the shader passes for the materials.
|
||||
while ( materials.size() > 0 || baseOnly )
|
||||
{
|
||||
mPasses.increment();
|
||||
|
||||
if ( !_createPass( &materials,
|
||||
&mPasses.last(),
|
||||
mPasses.size() == 1,
|
||||
prePassMat,
|
||||
reflectMat,
|
||||
baseOnly ) )
|
||||
{
|
||||
Con::errorf( "TerrainCellMaterial::init - Failed to create pass!" );
|
||||
|
||||
// The pass failed to be generated... give up.
|
||||
mPasses.last().materials.clear();
|
||||
mPasses.clear();
|
||||
for_each( materials.begin(), materials.end(), delete_pointer() );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( baseOnly )
|
||||
break;
|
||||
}
|
||||
|
||||
// Cleanup any remaining matinfo.
|
||||
for_each( materials.begin(), materials.end(), delete_pointer() );
|
||||
|
||||
// If we have attached mats then update them too.
|
||||
if ( mPrePassMat )
|
||||
mPrePassMat->init( mTerrain, mMaterials, true, false, baseOnly );
|
||||
if ( mReflectMat )
|
||||
mReflectMat->init( mTerrain, mMaterials, false, true, baseOnly );
|
||||
}
|
||||
|
||||
bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
|
||||
Pass *pass,
|
||||
bool firstPass,
|
||||
bool prePassMat,
|
||||
bool reflectMat,
|
||||
bool baseOnly )
|
||||
{
|
||||
if ( GFX->getPixelShaderVersion() < 3.0f )
|
||||
baseOnly = true;
|
||||
|
||||
// NOTE: At maximum we only try to combine 3 materials
|
||||
// into a single pass. This is sub-optimal for the simplest
|
||||
// cases, but the most common case results in much fewer
|
||||
// shader generation failures and permutations leading to
|
||||
// faster load time and less hiccups during gameplay.
|
||||
U32 matCount = getMin( 3, materials->size() );
|
||||
|
||||
Vector<GFXTexHandle> normalMaps;
|
||||
|
||||
// See if we're currently running under the
|
||||
// basic lighting manager.
|
||||
//
|
||||
// TODO: This seems ugly... we should trigger
|
||||
// features like this differently in the future.
|
||||
//
|
||||
bool useBLM = dStrcmp( LIGHTMGR->getId(), "BLM" ) == 0;
|
||||
|
||||
// Do we need to disable normal mapping?
|
||||
const bool disableNormalMaps = MATMGR->getExclusionFeatures().hasFeature( MFT_NormalMap ) || useBLM;
|
||||
|
||||
// How about parallax?
|
||||
const bool disableParallaxMaps = GFX->getPixelShaderVersion() < 3.0f ||
|
||||
MATMGR->getExclusionFeatures().hasFeature( MFT_Parallax );
|
||||
|
||||
// Has advanced lightmap support been enabled for prepass.
|
||||
bool advancedLightmapSupport = false;
|
||||
if ( prePassMat )
|
||||
{
|
||||
// This sucks... but it works.
|
||||
AdvancedLightBinManager *lightBin;
|
||||
if ( Sim::findObject( "AL_LightBinMgr", lightBin ) )
|
||||
advancedLightmapSupport = lightBin->MRTLightmapsDuringPrePass();
|
||||
}
|
||||
|
||||
// Loop till we create a valid shader!
|
||||
while( true )
|
||||
{
|
||||
FeatureSet features;
|
||||
features.addFeature( MFT_VertTransform );
|
||||
features.addFeature( MFT_TerrainBaseMap );
|
||||
|
||||
if ( prePassMat )
|
||||
{
|
||||
features.addFeature( MFT_EyeSpaceDepthOut );
|
||||
features.addFeature( MFT_PrePassConditioner );
|
||||
|
||||
if ( advancedLightmapSupport )
|
||||
features.addFeature( MFT_RenderTarget1_Zero );
|
||||
}
|
||||
else
|
||||
{
|
||||
features.addFeature( MFT_RTLighting );
|
||||
|
||||
// The HDR feature is always added... it will compile out
|
||||
// if HDR is not enabled in the engine.
|
||||
features.addFeature( MFT_HDROut );
|
||||
}
|
||||
|
||||
// Enable lightmaps and fogging if we're in BL.
|
||||
if ( reflectMat || useBLM )
|
||||
{
|
||||
features.addFeature( MFT_Fog );
|
||||
features.addFeature( MFT_ForwardShading );
|
||||
}
|
||||
if ( useBLM )
|
||||
features.addFeature( MFT_TerrainLightMap );
|
||||
|
||||
// The additional passes need to be lerp blended into the
|
||||
// target to maintain the results of the previous passes.
|
||||
if ( !firstPass )
|
||||
features.addFeature( MFT_TerrainAdditive );
|
||||
|
||||
normalMaps.clear();
|
||||
pass->materials.clear();
|
||||
|
||||
// Now add all the material layer features.
|
||||
|
||||
for ( U32 i=0; i < matCount && !baseOnly; i++ )
|
||||
{
|
||||
TerrainMaterial *mat = (*materials)[i]->mat;
|
||||
|
||||
if ( mat == NULL )
|
||||
continue;
|
||||
|
||||
// We only include materials that
|
||||
// have more than a base texture.
|
||||
if ( mat->getDetailSize() <= 0 ||
|
||||
mat->getDetailDistance() <= 0 ||
|
||||
mat->getDetailMap().isEmpty() )
|
||||
continue;
|
||||
|
||||
S32 featureIndex = pass->materials.size();
|
||||
|
||||
features.addFeature( MFT_TerrainDetailMap, featureIndex );
|
||||
|
||||
pass->materials.push_back( (*materials)[i] );
|
||||
normalMaps.increment();
|
||||
|
||||
// Skip normal maps if we need to.
|
||||
if ( !disableNormalMaps && mat->getNormalMap().isNotEmpty() )
|
||||
{
|
||||
features.addFeature( MFT_TerrainNormalMap, featureIndex );
|
||||
|
||||
normalMaps.last().set( mat->getNormalMap(),
|
||||
&GFXDefaultStaticNormalMapProfile, "TerrainCellMaterial::_createPass() - NormalMap" );
|
||||
|
||||
if ( normalMaps.last().getFormat() == GFXFormatDXT5 )
|
||||
features.addFeature( MFT_IsDXTnm, featureIndex );
|
||||
|
||||
// Do we need and can we do parallax mapping?
|
||||
if ( !disableParallaxMaps &&
|
||||
mat->getParallaxScale() > 0.0f &&
|
||||
!mat->useSideProjection() )
|
||||
features.addFeature( MFT_TerrainParallaxMap, featureIndex );
|
||||
}
|
||||
|
||||
// Is this layer got side projection?
|
||||
if ( mat->useSideProjection() )
|
||||
features.addFeature( MFT_TerrainSideProject, featureIndex );
|
||||
}
|
||||
|
||||
MaterialFeatureData featureData;
|
||||
featureData.features = features;
|
||||
featureData.materialFeatures = features;
|
||||
|
||||
// Check to see how many vertex shader output
|
||||
// registers we're gonna need.
|
||||
U32 numTex = 0;
|
||||
U32 numTexReg = 0;
|
||||
for ( U32 i=0; i < features.getCount(); i++ )
|
||||
{
|
||||
S32 index;
|
||||
const FeatureType &type = features.getAt( i, &index );
|
||||
ShaderFeature* sf = FEATUREMGR->getByType( type );
|
||||
if ( !sf )
|
||||
continue;
|
||||
|
||||
sf->setProcessIndex( index );
|
||||
ShaderFeature::Resources res = sf->getResources( featureData );
|
||||
|
||||
numTex += res.numTex;
|
||||
numTexReg += res.numTexReg;
|
||||
}
|
||||
|
||||
// Can we build the shader?
|
||||
//
|
||||
// NOTE: The 10 is sort of an abitrary SM 3.0
|
||||
// limit. Its really supposed to be 11, but that
|
||||
// always fails to compile so far.
|
||||
//
|
||||
if ( numTex < GFX->getNumSamplers() &&
|
||||
numTexReg <= 10 )
|
||||
{
|
||||
// NOTE: We really shouldn't be getting errors building the shaders,
|
||||
// but we can generate more instructions than the ps_2_x will allow.
|
||||
//
|
||||
// There is no way to deal with this case that i know of other than
|
||||
// letting the compile fail then recovering by trying to build it
|
||||
// with fewer materials.
|
||||
//
|
||||
// We normally disable the shader error logging so that the user
|
||||
// isn't fooled into thinking there is a real bug. That is until
|
||||
// we get down to a single material. If a single material case
|
||||
// fails it means it cannot generate any passes at all!
|
||||
const bool logErrors = matCount == 1;
|
||||
GFXShader::setLogging( logErrors, true );
|
||||
|
||||
pass->shader = SHADERGEN->getShader( featureData, getGFXVertexFormat<TerrVertex>(), NULL );
|
||||
}
|
||||
|
||||
// If we got a shader then we can continue.
|
||||
if ( pass->shader )
|
||||
break;
|
||||
|
||||
// If the material count is already 1 then this
|
||||
// is a real shader error... give up!
|
||||
if ( matCount <= 1 )
|
||||
return false;
|
||||
|
||||
// If we failed we next try half the input materials
|
||||
// so that we can more quickly arrive at a valid shader.
|
||||
matCount -= matCount / 2;
|
||||
}
|
||||
|
||||
// Setup the constant buffer.
|
||||
pass->consts = pass->shader->allocConstBuffer();
|
||||
|
||||
// Prepare the basic constants.
|
||||
pass->modelViewProjConst = pass->shader->getShaderConstHandle( "$modelview" );
|
||||
pass->worldViewOnly = pass->shader->getShaderConstHandle( "$worldViewOnly" );
|
||||
pass->viewToObj = pass->shader->getShaderConstHandle( "$viewToObj" );
|
||||
pass->eyePosWorldConst = pass->shader->getShaderConstHandle( "$eyePosWorld" );
|
||||
pass->eyePosConst = pass->shader->getShaderConstHandle( "$eyePos" );
|
||||
pass->vEyeConst = pass->shader->getShaderConstHandle( "$vEye" );
|
||||
pass->layerSizeConst = pass->shader->getShaderConstHandle( "$layerSize" );
|
||||
pass->objTransConst = pass->shader->getShaderConstHandle( "$objTrans" );
|
||||
pass->worldToObjConst = pass->shader->getShaderConstHandle( "$worldToObj" );
|
||||
pass->lightInfoBufferConst = pass->shader->getShaderConstHandle( "$lightInfoBuffer" );
|
||||
pass->baseTexMapConst = pass->shader->getShaderConstHandle( "$baseTexMap" );
|
||||
pass->layerTexConst = pass->shader->getShaderConstHandle( "$layerTex" );
|
||||
pass->fogDataConst = pass->shader->getShaderConstHandle( "$fogData" );
|
||||
pass->fogColorConst = pass->shader->getShaderConstHandle( "$fogColor" );
|
||||
pass->lightMapTexConst = pass->shader->getShaderConstHandle( "$lightMapTex" );
|
||||
pass->oneOverTerrainSize = pass->shader->getShaderConstHandle( "$oneOverTerrainSize" );
|
||||
pass->squareSize = pass->shader->getShaderConstHandle( "$squareSize" );
|
||||
|
||||
// NOTE: We're assuming rtParams0 here as we know its the only
|
||||
// render target we currently get in a terrain material and the
|
||||
// DeferredRTLightingFeatHLSL will always use 0.
|
||||
//
|
||||
// This could change in the future and we would need to fix
|
||||
// the ShaderFeature API to allow us to do this right.
|
||||
//
|
||||
pass->lightParamsConst = pass->shader->getShaderConstHandle( "$rtParams0" );
|
||||
|
||||
// Now prepare the basic stateblock.
|
||||
GFXStateBlockDesc desc;
|
||||
if ( !firstPass )
|
||||
{
|
||||
desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha );
|
||||
|
||||
// If this is the prepass then we don't want to
|
||||
// write to the last two color channels (where
|
||||
// depth is usually encoded).
|
||||
//
|
||||
// This trick works in combination with the
|
||||
// MFT_TerrainAdditive feature to lerp the
|
||||
// output normal with the previous pass.
|
||||
//
|
||||
if ( prePassMat )
|
||||
desc.setColorWrites( true, true, false, false );
|
||||
}
|
||||
|
||||
// We write to the zbuffer if this is a prepass
|
||||
// material or if the prepass is disabled.
|
||||
// We also write the zbuffer if we're using OpenGL, because in OpenGL the prepass
|
||||
// cannot share the same zbuffer as the backbuffer.
|
||||
desc.setZReadWrite( true, !MATMGR->getPrePassEnabled() ||
|
||||
GFX->getAdapterType() == OpenGL ||
|
||||
prePassMat ||
|
||||
reflectMat );
|
||||
|
||||
desc.samplersDefined = true;
|
||||
if ( pass->baseTexMapConst->isValid() )
|
||||
desc.samplers[pass->baseTexMapConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear();
|
||||
|
||||
if ( pass->layerTexConst->isValid() )
|
||||
desc.samplers[pass->layerTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint();
|
||||
|
||||
if ( pass->lightInfoBufferConst->isValid() )
|
||||
desc.samplers[pass->lightInfoBufferConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint();
|
||||
|
||||
if ( pass->lightMapTexConst->isValid() )
|
||||
desc.samplers[pass->lightMapTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear();
|
||||
|
||||
const U32 maxAnisotropy = MATMGR->getDefaultAnisotropy();
|
||||
|
||||
// Finally setup the material specific shader
|
||||
// constants and stateblock state.
|
||||
//
|
||||
// NOTE: If this changes be sure to check TerrainCellMaterial::_updateDefaultAnisotropy
|
||||
// to see if it needs the same changes.
|
||||
//
|
||||
for ( U32 i=0; i < pass->materials.size(); i++ )
|
||||
{
|
||||
MaterialInfo *matInfo = pass->materials[i];
|
||||
|
||||
matInfo->detailInfoVConst = pass->shader->getShaderConstHandle( avar( "$detailScaleAndFade%d", i ) );
|
||||
matInfo->detailInfoPConst = pass->shader->getShaderConstHandle( avar( "$detailIdStrengthParallax%d", i ) );
|
||||
|
||||
matInfo->detailTexConst = pass->shader->getShaderConstHandle( avar( "$detailMap%d", i ) );
|
||||
if ( matInfo->detailTexConst->isValid() )
|
||||
{
|
||||
const S32 sampler = matInfo->detailTexConst->getSamplerRegister();
|
||||
|
||||
desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear();
|
||||
desc.samplers[sampler].magFilter = GFXTextureFilterLinear;
|
||||
desc.samplers[sampler].mipFilter = GFXTextureFilterLinear;
|
||||
|
||||
if ( maxAnisotropy > 1 )
|
||||
{
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
|
||||
desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
|
||||
}
|
||||
else
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
|
||||
|
||||
matInfo->detailTex.set( matInfo->mat->getDetailMap(),
|
||||
&GFXDefaultStaticDiffuseProfile, "TerrainCellMaterial::_createPass() - DetailMap" );
|
||||
}
|
||||
|
||||
matInfo->normalTexConst = pass->shader->getShaderConstHandle( avar( "$normalMap%d", i ) );
|
||||
if ( matInfo->normalTexConst->isValid() )
|
||||
{
|
||||
const S32 sampler = matInfo->normalTexConst->getSamplerRegister();
|
||||
|
||||
desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear();
|
||||
desc.samplers[sampler].magFilter = GFXTextureFilterLinear;
|
||||
desc.samplers[sampler].mipFilter = GFXTextureFilterLinear;
|
||||
|
||||
if ( maxAnisotropy > 1 )
|
||||
{
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
|
||||
desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
|
||||
}
|
||||
else
|
||||
desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
|
||||
|
||||
matInfo->normalTex = normalMaps[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the materials we processed and leave the
|
||||
// ones that remain for the next pass.
|
||||
for ( U32 i=0; i < matCount; i++ )
|
||||
{
|
||||
MaterialInfo *matInfo = materials->first();
|
||||
if ( baseOnly || pass->materials.find_next( matInfo ) == -1 )
|
||||
delete matInfo;
|
||||
materials->pop_front();
|
||||
}
|
||||
|
||||
// If we're doing prepass it requires some
|
||||
// special stencil settings for it to work.
|
||||
if ( prePassMat )
|
||||
desc.addDesc( RenderPrePassMgr::getOpaqueStenciWriteDesc( false ) );
|
||||
|
||||
// Flip the cull for reflection materials.
|
||||
if ( reflectMat )
|
||||
desc.setCullMode( GFXCullCW );
|
||||
|
||||
pass->stateBlock = GFX->createStateBlock( desc );
|
||||
|
||||
// Create the wireframe state blocks.
|
||||
GFXStateBlockDesc wireframe( desc );
|
||||
wireframe.fillMode = GFXFillWireframe;
|
||||
pass->wireframeStateBlock = GFX->createStateBlock( wireframe );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
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->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;
|
||||
}
|
||||
|
||||
BaseMatInstance* TerrainCellMaterial::getShadowMat()
|
||||
{
|
||||
// Find our material which has some settings
|
||||
// defined on it in script.
|
||||
Material *mat = MATMGR->getMaterialDefinitionByName( "AL_DefaultShadowMaterial" );
|
||||
|
||||
// Create the material instance adding the feature which
|
||||
// handles rendering terrain cut outs.
|
||||
FeatureSet features = MATMGR->getDefaultFeatures();
|
||||
BaseMatInstance *matInst = mat->createMatInstance();
|
||||
if ( !matInst->init( features, getGFXVertexFormat<TerrVertex>() ) )
|
||||
{
|
||||
delete matInst;
|
||||
matInst = NULL;
|
||||
}
|
||||
|
||||
return matInst;
|
||||
}
|
||||
|
||||
199
Engine/source/terrain/terrCellMaterial.h
Normal file
199
Engine/source/terrain/terrCellMaterial.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRCELLMATERIAL_H_
|
||||
#define _TERRCELLMATERIAL_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _MATTEXTURETARGET_H_
|
||||
#include "materials/matTextureTarget.h"
|
||||
#endif
|
||||
#ifndef _GFXTEXTUREHANDLE_H_
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#endif
|
||||
#ifndef _GFXSHADER_H_
|
||||
#include "gfx/gfxShader.h"
|
||||
#endif
|
||||
#ifndef _GFXSTATEBLOCK_H_
|
||||
#include "gfx/gfxStateBlock.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneRenderState;
|
||||
struct SceneData;
|
||||
class TerrainMaterial;
|
||||
class TerrainBlock;
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
/// This is a complex material which holds one or more
|
||||
/// optimized shaders for rendering a single cell.
|
||||
class TerrainCellMaterial
|
||||
{
|
||||
protected:
|
||||
|
||||
class MaterialInfo
|
||||
{
|
||||
public:
|
||||
|
||||
MaterialInfo()
|
||||
{
|
||||
}
|
||||
|
||||
~MaterialInfo()
|
||||
{
|
||||
}
|
||||
|
||||
TerrainMaterial *mat;
|
||||
U32 layerId;
|
||||
|
||||
GFXShaderConstHandle *detailTexConst;
|
||||
GFXTexHandle detailTex;
|
||||
|
||||
GFXShaderConstHandle *normalTexConst;
|
||||
GFXTexHandle normalTex;
|
||||
|
||||
GFXShaderConstHandle *detailInfoVConst;
|
||||
GFXShaderConstHandle *detailInfoPConst;
|
||||
};
|
||||
|
||||
class Pass
|
||||
{
|
||||
public:
|
||||
|
||||
Pass()
|
||||
: shader( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
~Pass()
|
||||
{
|
||||
for ( U32 i=0; i < materials.size(); i++ )
|
||||
delete materials[i];
|
||||
}
|
||||
|
||||
Vector<MaterialInfo*> materials;
|
||||
|
||||
///
|
||||
GFXShader *shader;
|
||||
|
||||
GFXShaderConstBufferRef consts;
|
||||
|
||||
GFXStateBlockRef stateBlock;
|
||||
GFXStateBlockRef wireframeStateBlock;
|
||||
|
||||
GFXShaderConstHandle *modelViewProjConst;
|
||||
GFXShaderConstHandle *worldViewOnly;
|
||||
GFXShaderConstHandle *viewToObj;
|
||||
|
||||
GFXShaderConstHandle *eyePosWorldConst;
|
||||
GFXShaderConstHandle *eyePosConst;
|
||||
|
||||
GFXShaderConstHandle *objTransConst;
|
||||
GFXShaderConstHandle *worldToObjConst;
|
||||
GFXShaderConstHandle *vEyeConst;
|
||||
|
||||
GFXShaderConstHandle *layerSizeConst;
|
||||
GFXShaderConstHandle *lightParamsConst;
|
||||
GFXShaderConstHandle *lightInfoBufferConst;
|
||||
|
||||
GFXShaderConstHandle *baseTexMapConst;
|
||||
GFXShaderConstHandle *layerTexConst;
|
||||
|
||||
GFXShaderConstHandle *lightMapTexConst;
|
||||
|
||||
GFXShaderConstHandle *squareSize;
|
||||
GFXShaderConstHandle *oneOverTerrainSize;
|
||||
|
||||
GFXShaderConstHandle *fogDataConst;
|
||||
GFXShaderConstHandle *fogColorConst;
|
||||
};
|
||||
|
||||
TerrainBlock *mTerrain;
|
||||
|
||||
U64 mMaterials;
|
||||
|
||||
Vector<Pass> mPasses;
|
||||
|
||||
U32 mCurrPass;
|
||||
|
||||
GFXTexHandle mBaseMapTexture;
|
||||
|
||||
GFXTexHandle mLayerMapTexture;
|
||||
|
||||
NamedTexTargetRef mLightInfoTarget;
|
||||
|
||||
/// The prepass material for this material.
|
||||
TerrainCellMaterial *mPrePassMat;
|
||||
|
||||
/// The reflection material for this material.
|
||||
TerrainCellMaterial *mReflectMat;
|
||||
|
||||
/// A vector of all terrain cell materials loaded in the system.
|
||||
static Vector<TerrainCellMaterial*> smAllMaterials;
|
||||
|
||||
bool _createPass( Vector<MaterialInfo*> *materials,
|
||||
Pass *pass,
|
||||
bool firstPass,
|
||||
bool prePassMat,
|
||||
bool reflectMat,
|
||||
bool baseOnly );
|
||||
|
||||
void _updateMaterialConsts( Pass *pass );
|
||||
|
||||
public:
|
||||
|
||||
TerrainCellMaterial();
|
||||
~TerrainCellMaterial();
|
||||
|
||||
void init( TerrainBlock *block,
|
||||
U64 activeMaterials,
|
||||
bool prePassMat = false,
|
||||
bool reflectMat = false,
|
||||
bool baseOnly = false );
|
||||
|
||||
/// Returns a prepass material from this material.
|
||||
TerrainCellMaterial* getPrePassMat();
|
||||
|
||||
/// Returns the reflection material from this material.
|
||||
TerrainCellMaterial* getReflectMat();
|
||||
|
||||
void setTransformAndEye( const MatrixF &modelXfm,
|
||||
const MatrixF &viewXfm,
|
||||
const MatrixF &projectXfm,
|
||||
F32 farPlane );
|
||||
|
||||
///
|
||||
bool setupPass( const SceneRenderState *state,
|
||||
const SceneData &sceneData );
|
||||
|
||||
///
|
||||
static BaseMatInstance* getShadowMat();
|
||||
|
||||
///
|
||||
static void _updateDefaultAnisotropy();
|
||||
};
|
||||
|
||||
#endif // _TERRCELLMATERIAL_H_
|
||||
|
||||
983
Engine/source/terrain/terrCollision.cpp
Normal file
983
Engine/source/terrain/terrCollision.cpp
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrCollision.h"
|
||||
|
||||
#include "terrain/terrData.h"
|
||||
#include "collision/abstractPolyList.h"
|
||||
#include "collision/collision.h"
|
||||
|
||||
|
||||
const F32 TerrainThickness = 0.5f;
|
||||
static const U32 MaxExtent = 256;
|
||||
#define MAX_FLOAT 1e20f
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Convex sTerrainConvexList;
|
||||
|
||||
// Number of vertices followed by point index
|
||||
S32 sVertexList[5][5] = {
|
||||
{ 3, 1,2,3 }, // 135 B
|
||||
{ 3, 0,1,3 }, // 135 A
|
||||
{ 3, 0,2,3 }, // 45 B
|
||||
{ 3, 0,1,2 }, // 45 A
|
||||
{ 4, 0,1,2,3 } // Convex square
|
||||
};
|
||||
|
||||
// Number of edges followed by edge index pairs
|
||||
S32 sEdgeList45[16][11] = {
|
||||
{ 0 }, //
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-1
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-2
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 3, 0,1,1,2,2,0 }, // 0-1,1-2,2-0
|
||||
{ 0 },
|
||||
{ 0,}, //
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-1,
|
||||
{ 0, }, //
|
||||
{ 1, 0,1 }, // 0-2,
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 3, 0,1,1,2,0,2 },
|
||||
};
|
||||
|
||||
S32 sEdgeList135[16][11] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-1
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 2, 0,1,1,2 }, // 0-1,1-2
|
||||
{ 0 },
|
||||
{ 0, }, //
|
||||
{ 1, 0,1 }, // 1-3
|
||||
{ 2, 0,1,1,2 }, // 0-1,1-3,
|
||||
{ 0 }, //
|
||||
{ 0 }, //
|
||||
{ 2, 0,1,2,0 }, // 1-2,3-1
|
||||
{ 3, 0,1,1,2,1,3 },
|
||||
};
|
||||
|
||||
// On split squares, the FaceA diagnal is also removed
|
||||
S32 sEdgeList45A[16][11] = {
|
||||
{ 0 }, //
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-1
|
||||
{ 0 },
|
||||
{ 0 }, //
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 2, 0,1,1,2 }, // 0-1,1-2
|
||||
{ 0 },
|
||||
{ 0,}, //
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-1
|
||||
{ 0, }, //
|
||||
{ 0, 0,1 }, //
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 3, 0,1,1,2 },
|
||||
};
|
||||
|
||||
S32 sEdgeList135A[16][11] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 0-1
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 2, 0,1,1,2 }, // 0-1,1-2
|
||||
{ 0 },
|
||||
{ 0 }, //
|
||||
{ 0 }, //
|
||||
{ 1, 0,1 }, // 0-1
|
||||
{ 0 }, //
|
||||
{ 0 }, //
|
||||
{ 1, 0,1 }, // 1-2
|
||||
{ 3, 0,1,1,2 },
|
||||
};
|
||||
|
||||
|
||||
// Number of faces followed by normal index and vertices
|
||||
S32 sFaceList45[16][9] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,0,1,2 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 1,0,1,2 },
|
||||
{ 0 },
|
||||
{ 2, 0,0,1,2, 1,0,2,3 },
|
||||
};
|
||||
|
||||
S32 sFaceList135[16][9] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 0,0,1,2 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 1, 1,0,1,2 },
|
||||
{ 2, 0,0,1,3, 1,1,2,3 },
|
||||
};
|
||||
|
||||
|
||||
TerrainConvex::TerrainConvex()
|
||||
{
|
||||
mType = TerrainConvexType;
|
||||
}
|
||||
|
||||
TerrainConvex::TerrainConvex( const TerrainConvex &cv )
|
||||
{
|
||||
mType = TerrainConvexType;
|
||||
|
||||
// Only a partial copy...
|
||||
mObject = cv.mObject;
|
||||
split45 = cv.split45;
|
||||
squareId = cv.squareId;
|
||||
material = cv.material;
|
||||
point[0] = cv.point[0];
|
||||
point[1] = cv.point[1];
|
||||
point[2] = cv.point[2];
|
||||
point[3] = cv.point[3];
|
||||
normal[0] = cv.normal[0];
|
||||
normal[1] = cv.normal[1];
|
||||
box = cv.box;
|
||||
}
|
||||
|
||||
Box3F TerrainConvex::getBoundingBox() const
|
||||
{
|
||||
return box;
|
||||
}
|
||||
|
||||
Box3F TerrainConvex::getBoundingBox(const MatrixF&, const Point3F& ) const
|
||||
{
|
||||
// Function should not be called....
|
||||
return box;
|
||||
}
|
||||
|
||||
Point3F TerrainConvex::support(const VectorF& v) const
|
||||
{
|
||||
S32 *vp;
|
||||
if (halfA)
|
||||
vp = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4];
|
||||
else
|
||||
vp = square ? sVertexList[(split45 << 1)] : sVertexList[4];
|
||||
|
||||
S32 *ve = vp + vp[0] + 1;
|
||||
const Point3F *bp = &point[vp[1]];
|
||||
F32 bd = mDot(*bp,v);
|
||||
for (vp += 2; vp < ve; vp++) {
|
||||
const Point3F* cp = &point[*vp];
|
||||
F32 dd = mDot(*cp,v);
|
||||
if (dd > bd) {
|
||||
bd = dd;
|
||||
bp = cp;
|
||||
}
|
||||
}
|
||||
return *bp;
|
||||
}
|
||||
|
||||
inline bool isOnPlane(Point3F& p,PlaneF& plane)
|
||||
{
|
||||
F32 dist = mDot(plane,p) + plane.d;
|
||||
return dist < 0.1 && dist > -0.1;
|
||||
}
|
||||
|
||||
void TerrainConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
|
||||
{
|
||||
U32 i;
|
||||
cf->material = 0;
|
||||
cf->object = mObject;
|
||||
|
||||
// Plane is normal n + support point
|
||||
PlaneF plane;
|
||||
plane.set(support(n),n);
|
||||
S32 vertexCount = cf->mVertexList.size();
|
||||
|
||||
// Emit vertices on the plane
|
||||
S32* vertexListPointer;
|
||||
if (halfA)
|
||||
vertexListPointer = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4];
|
||||
else
|
||||
vertexListPointer = square ? sVertexList[(split45 << 1)] : sVertexList[4];
|
||||
|
||||
S32 pm = 0;
|
||||
S32 numVerts = *vertexListPointer;
|
||||
vertexListPointer += 1;
|
||||
for (i = 0; i < numVerts; i++)
|
||||
{
|
||||
Point3F& cp = point[vertexListPointer[i]];
|
||||
cf->mVertexList.increment();
|
||||
mat.mulP(cp,&cf->mVertexList.last());
|
||||
pm |= 1 << vertexListPointer[i];
|
||||
}
|
||||
|
||||
// Emit Edges
|
||||
S32* ep = (square && halfA)?
|
||||
(split45 ? sEdgeList45A[pm]: sEdgeList135A[pm]):
|
||||
(split45 ? sEdgeList45[pm]: sEdgeList135[pm]);
|
||||
|
||||
S32 numEdges = *ep;
|
||||
S32 edgeListStart = cf->mEdgeList.size();
|
||||
cf->mEdgeList.increment(numEdges);
|
||||
ep += 1;
|
||||
for (i = 0; i < numEdges; i++)
|
||||
{
|
||||
cf->mEdgeList[edgeListStart + i].vertex[0] = vertexCount + ep[i * 2 + 0];
|
||||
cf->mEdgeList[edgeListStart + i].vertex[1] = vertexCount + ep[i * 2 + 1];
|
||||
}
|
||||
|
||||
// Emit faces
|
||||
S32* fp = split45 ? sFaceList45[pm]: sFaceList135[pm];
|
||||
S32 numFaces = *fp;
|
||||
fp += 1;
|
||||
S32 faceListStart = cf->mFaceList.size();
|
||||
cf->mFaceList.increment(numFaces);
|
||||
for (i = 0; i < numFaces; i++)
|
||||
{
|
||||
cf->mFaceList[faceListStart + i].normal = normal[fp[i * 4 + 0]];
|
||||
cf->mFaceList[faceListStart + i].vertex[0] = vertexCount + fp[i * 4 + 1];
|
||||
cf->mFaceList[faceListStart + i].vertex[1] = vertexCount + fp[i * 4 + 2];
|
||||
cf->mFaceList[faceListStart + i].vertex[2] = vertexCount + fp[i * 4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TerrainConvex::getPolyList(AbstractPolyList* list)
|
||||
{
|
||||
list->setTransform(&mObject->getTransform(), mObject->getScale());
|
||||
list->setObject(mObject);
|
||||
|
||||
// Emit vertices
|
||||
U32 array[4];
|
||||
U32 curr = 0;
|
||||
|
||||
S32 numVerts;
|
||||
S32* vertsStart;
|
||||
if (halfA)
|
||||
{
|
||||
numVerts = square ? sVertexList[(split45 << 1) | 1][0] : sVertexList[4][0];
|
||||
vertsStart = square ? &sVertexList[(split45 << 1) | 1][1] : &sVertexList[4][1];
|
||||
}
|
||||
else
|
||||
{
|
||||
numVerts = square ? sVertexList[(split45 << 1)][0] : sVertexList[4][0];
|
||||
vertsStart = square ? &sVertexList[(split45 << 1)][1] : &sVertexList[4][1];
|
||||
}
|
||||
|
||||
S32 pointMask = 0;
|
||||
for (U32 i = 0; i < numVerts; i++) {
|
||||
const Point3F& cp = point[vertsStart[i]];
|
||||
array[curr++] = list->addPoint(cp);
|
||||
pointMask |= (1 << vertsStart[i]);
|
||||
}
|
||||
|
||||
S32 numFaces = split45 ? sFaceList45[pointMask][0] : sFaceList135[pointMask][0];
|
||||
S32* faceStart = split45 ? &sFaceList45[pointMask][1] : &sFaceList135[pointMask][1];
|
||||
for (U32 j = 0; j < numFaces; j++) {
|
||||
S32 plane = faceStart[0];
|
||||
S32 v0 = faceStart[1];
|
||||
S32 v1 = faceStart[2];
|
||||
S32 v2 = faceStart[3];
|
||||
|
||||
list->begin(0, plane);
|
||||
list->vertex(array[v0]);
|
||||
list->vertex(array[v1]);
|
||||
list->vertex(array[v2]);
|
||||
list->plane(array[v0], array[v1], array[v2]);
|
||||
list->end();
|
||||
|
||||
faceStart += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void TerrainBlock::buildConvex(const Box3F& box,Convex* convex)
|
||||
{
|
||||
PROFILE_SCOPE( TerrainBlock_buildConvex );
|
||||
|
||||
sTerrainConvexList.collectGarbage();
|
||||
|
||||
// First check to see if the query misses the
|
||||
// terrain elevation range.
|
||||
const Point3F &terrainPos = getPosition();
|
||||
if ( box.maxExtents.z - terrainPos.z < -TerrainThickness ||
|
||||
box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) )
|
||||
return;
|
||||
|
||||
// Transform the bounding sphere into the object's coord space. Note that this
|
||||
// not really optimal.
|
||||
Box3F osBox = box;
|
||||
mWorldToObj.mul(osBox);
|
||||
AssertWarn(mObjScale == Point3F(1, 1, 1), "Error, handle the scale transform on the terrain");
|
||||
|
||||
S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize );
|
||||
S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize );
|
||||
S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize );
|
||||
S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize );
|
||||
S32 xExt = xEnd - xStart;
|
||||
if (xExt > MaxExtent)
|
||||
xExt = MaxExtent;
|
||||
|
||||
U16 heightMax = floatToFixed(osBox.maxExtents.z);
|
||||
U16 heightMin = (osBox.minExtents.z < 0)? 0: floatToFixed(osBox.minExtents.z);
|
||||
|
||||
const U32 BlockMask = mFile->mSize - 1;
|
||||
|
||||
for ( S32 y = yStart; y < yEnd; y++ )
|
||||
{
|
||||
S32 yi = y & BlockMask;
|
||||
|
||||
//
|
||||
for ( S32 x = xStart; x < xEnd; x++ )
|
||||
{
|
||||
S32 xi = x & BlockMask;
|
||||
|
||||
const TerrainSquare *sq = mFile->findSquare( 0, xi, yi );
|
||||
|
||||
if ( x != xi || y != yi )
|
||||
continue;
|
||||
|
||||
// holes only in the primary terrain block
|
||||
if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) ||
|
||||
sq->minHeight > heightMax ||
|
||||
sq->maxHeight < heightMin )
|
||||
continue;
|
||||
|
||||
U32 sid = (x << 16) + (y & ((1 << 16) - 1));
|
||||
Convex *cc = 0;
|
||||
|
||||
// See if the square already exists as part of the working set.
|
||||
CollisionWorkingList& wl = convex->getWorkingList();
|
||||
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
|
||||
if (itr->mConvex->getType() == TerrainConvexType &&
|
||||
static_cast<TerrainConvex*>(itr->mConvex)->squareId == sid) {
|
||||
cc = itr->mConvex;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cc)
|
||||
continue;
|
||||
|
||||
// Create a new convex.
|
||||
TerrainConvex* cp = new TerrainConvex;
|
||||
sTerrainConvexList.registerObject(cp);
|
||||
convex->addToWorkingList(cp);
|
||||
cp->halfA = true;
|
||||
cp->square = 0;
|
||||
cp->mObject = this;
|
||||
cp->squareId = sid;
|
||||
cp->material = mFile->getLayerIndex( xi, yi );
|
||||
cp->box.minExtents.set((F32)(x * mSquareSize), (F32)(y * mSquareSize), fixedToFloat( sq->minHeight ));
|
||||
cp->box.maxExtents.x = cp->box.minExtents.x + mSquareSize;
|
||||
cp->box.maxExtents.y = cp->box.minExtents.y + mSquareSize;
|
||||
cp->box.maxExtents.z = fixedToFloat( sq->maxHeight );
|
||||
mObjToWorld.mul(cp->box);
|
||||
|
||||
// Build points
|
||||
Point3F* pos = cp->point;
|
||||
for (int i = 0; i < 4 ; i++,pos++) {
|
||||
S32 dx = i >> 1;
|
||||
S32 dy = dx ^ (i & 1);
|
||||
pos->x = (F32)((x + dx) * mSquareSize);
|
||||
pos->y = (F32)((y + dy) * mSquareSize);
|
||||
pos->z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) );
|
||||
}
|
||||
|
||||
// Build normals, then split into two Convex objects if the
|
||||
// square is concave
|
||||
if ((cp->split45 = sq->flags & TerrainSquare::Split45) == true) {
|
||||
VectorF *vp = cp->point;
|
||||
mCross(vp[0] - vp[1],vp[2] - vp[1],&cp->normal[0]);
|
||||
cp->normal[0].normalize();
|
||||
mCross(vp[2] - vp[3],vp[0] - vp[3],&cp->normal[1]);
|
||||
cp->normal[1].normalize();
|
||||
if (mDot(vp[3] - vp[1],cp->normal[0]) > 0) {
|
||||
TerrainConvex* nc = new TerrainConvex(*cp);
|
||||
sTerrainConvexList.registerObject(nc);
|
||||
convex->addToWorkingList(nc);
|
||||
nc->halfA = false;
|
||||
nc->square = cp;
|
||||
cp->square = nc;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VectorF *vp = cp->point;
|
||||
mCross(vp[3] - vp[0],vp[1] - vp[0],&cp->normal[0]);
|
||||
cp->normal[0].normalize();
|
||||
mCross(vp[1] - vp[2],vp[3] - vp[2],&cp->normal[1]);
|
||||
cp->normal[1].normalize();
|
||||
if (mDot(vp[2] - vp[0],cp->normal[0]) > 0) {
|
||||
TerrainConvex* nc = new TerrainConvex(*cp);
|
||||
sTerrainConvexList.registerObject(nc);
|
||||
convex->addToWorkingList(nc);
|
||||
nc->halfA = false;
|
||||
nc->square = cp;
|
||||
cp->square = nc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void swap(U32*& a,U32*& b)
|
||||
{
|
||||
U32* t = b;
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
|
||||
static void clrbuf(U32* p, U32 s)
|
||||
{
|
||||
U32* e = p + s;
|
||||
while (p != e)
|
||||
*p++ = U32_MAX;
|
||||
}
|
||||
|
||||
bool TerrainBlock::buildPolyList(PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF&)
|
||||
{
|
||||
PROFILE_SCOPE( TerrainBlock_buildPolyList );
|
||||
|
||||
// First check to see if the query misses the
|
||||
// terrain elevation range.
|
||||
const Point3F &terrainPos = getPosition();
|
||||
if ( box.maxExtents.z - terrainPos.z < -TerrainThickness ||
|
||||
box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) )
|
||||
return false;
|
||||
|
||||
// Transform the bounding sphere into the object's coord
|
||||
// space. Note that this is really optimal.
|
||||
Box3F osBox = box;
|
||||
mWorldToObj.mul(osBox);
|
||||
AssertWarn(mObjScale == Point3F::One, "Error, handle the scale transform on the terrain");
|
||||
|
||||
// Setup collision state data
|
||||
polyList->setTransform(&getTransform(), getScale());
|
||||
polyList->setObject(this);
|
||||
|
||||
S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize );
|
||||
S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize );
|
||||
S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize );
|
||||
S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize );
|
||||
if ( xStart < 0 )
|
||||
xStart = 0;
|
||||
S32 xExt = xEnd - xStart;
|
||||
if ( xExt > MaxExtent )
|
||||
xExt = MaxExtent;
|
||||
xEnd = xStart + xExt;
|
||||
|
||||
U32 heightMax = floatToFixed(osBox.maxExtents.z);
|
||||
U32 heightMin = (osBox.minExtents.z < 0.0f)? 0.0f: floatToFixed(osBox.minExtents.z);
|
||||
|
||||
// Index of shared points
|
||||
U32 bp[(MaxExtent + 1) * 2],*vb[2];
|
||||
vb[0] = &bp[0];
|
||||
vb[1] = &bp[xExt + 1];
|
||||
clrbuf(vb[1],xExt + 1);
|
||||
|
||||
const U32 BlockMask = mFile->mSize - 1;
|
||||
|
||||
bool emitted = false;
|
||||
for (S32 y = yStart; y < yEnd; y++)
|
||||
{
|
||||
S32 yi = y & BlockMask;
|
||||
|
||||
swap(vb[0],vb[1]);
|
||||
clrbuf(vb[1],xExt + 1);
|
||||
//
|
||||
for (S32 x = xStart; x < xEnd; x++)
|
||||
{
|
||||
S32 xi = x & BlockMask;
|
||||
const TerrainSquare *sq = mFile->findSquare( 0, xi, yi );
|
||||
|
||||
if ( x != xi || y != yi )
|
||||
continue;
|
||||
|
||||
// holes only in the primary terrain block
|
||||
if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) ||
|
||||
sq->minHeight > heightMax ||
|
||||
sq->maxHeight < heightMin )
|
||||
continue;
|
||||
|
||||
emitted = true;
|
||||
|
||||
// Add the missing points
|
||||
U32 vi[5];
|
||||
for (int i = 0; i < 4 ; i++)
|
||||
{
|
||||
S32 dx = i >> 1;
|
||||
S32 dy = dx ^ (i & 1);
|
||||
U32* vp = &vb[dy][x - xStart + dx];
|
||||
if (*vp == U32_MAX)
|
||||
{
|
||||
Point3F pos;
|
||||
pos.x = (F32)((x + dx) * mSquareSize);
|
||||
pos.y = (F32)((y + dy) * mSquareSize);
|
||||
pos.z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) );
|
||||
*vp = polyList->addPoint(pos);
|
||||
}
|
||||
vi[i] = *vp;
|
||||
}
|
||||
|
||||
U32* vp = &vi[0];
|
||||
if ( !( sq->flags & TerrainSquare::Split45 ) )
|
||||
vi[4] = vi[0], vp++;
|
||||
|
||||
BaseMatInstance *material = NULL; //getMaterialInst( xi, yi );
|
||||
U32 surfaceKey = ((xi << 16) + yi) << 1;
|
||||
polyList->begin(material,surfaceKey);
|
||||
polyList->vertex(vp[0]);
|
||||
polyList->vertex(vp[1]);
|
||||
polyList->vertex(vp[2]);
|
||||
polyList->plane(vp[0],vp[1],vp[2]);
|
||||
polyList->end();
|
||||
polyList->begin(material,surfaceKey + 1);
|
||||
polyList->vertex(vp[0]);
|
||||
polyList->vertex(vp[2]);
|
||||
polyList->vertex(vp[3]);
|
||||
polyList->plane(vp[0],vp[2],vp[3]);
|
||||
polyList->end();
|
||||
}
|
||||
}
|
||||
|
||||
return emitted;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static F32 calcInterceptV(F32 vStart, F32 invDeltaV, F32 intercept)
|
||||
{
|
||||
return (intercept - vStart) * invDeltaV;
|
||||
}
|
||||
|
||||
static F32 calcInterceptNone(F32, F32, F32)
|
||||
{
|
||||
return MAX_FLOAT;
|
||||
}
|
||||
|
||||
static F32 (*calcInterceptX)(F32, F32, F32);
|
||||
static F32 (*calcInterceptY)(F32, F32, F32);
|
||||
|
||||
static U32 lineCount;
|
||||
static Point3F lineStart, lineEnd;
|
||||
|
||||
bool TerrainBlock::castRay(const Point3F &start, const Point3F &end, RayInfo *info)
|
||||
{
|
||||
PROFILE_SCOPE( TerrainBlock_castRay );
|
||||
|
||||
if ( !castRayI(start, end, info, false) )
|
||||
return false;
|
||||
|
||||
// Set intersection point.
|
||||
info->setContactPoint( start, end );
|
||||
|
||||
// Set material at contact point.
|
||||
Point2I gridPos = getGridPos( info->point );
|
||||
U8 layer = mFile->getLayerIndex( gridPos.x, gridPos.y );
|
||||
info->material = mFile->getMaterialMapping( layer );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *info, bool collideEmpty)
|
||||
{
|
||||
lineCount = 0;
|
||||
lineStart = start;
|
||||
lineEnd = end;
|
||||
|
||||
info->object = this;
|
||||
|
||||
if(start.x == end.x && start.y == end.y)
|
||||
{
|
||||
if (end.z == start.z)
|
||||
return false;
|
||||
|
||||
F32 height;
|
||||
if(!getNormalAndHeight(Point2F(start.x, start.y), &info->normal, &height, true))
|
||||
return false;
|
||||
|
||||
F32 t = (height - start.z) / (end.z - start.z);
|
||||
if(t < 0 || t > 1)
|
||||
return false;
|
||||
info->t = t;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
F32 invBlockWorldSize = 1 / getWorldBlockSize();
|
||||
|
||||
Point3F pStart(start.x * invBlockWorldSize, start.y * invBlockWorldSize, start.z);
|
||||
Point3F pEnd(end.x * invBlockWorldSize, end.y * invBlockWorldSize, end.z);
|
||||
|
||||
int blockX = (S32)mFloor(pStart.x);
|
||||
int blockY = (S32)mFloor(pStart.y);
|
||||
|
||||
int dx, dy;
|
||||
|
||||
F32 invDeltaX;
|
||||
if(pEnd.x == pStart.x)
|
||||
{
|
||||
calcInterceptX = calcInterceptNone;
|
||||
invDeltaX = 0;
|
||||
dx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
invDeltaX = 1 / (pEnd.x - pStart.x);
|
||||
calcInterceptX = calcInterceptV;
|
||||
if(pEnd.x < pStart.x)
|
||||
dx = -1;
|
||||
else
|
||||
dx = 1;
|
||||
}
|
||||
|
||||
F32 invDeltaY;
|
||||
if(pEnd.y == pStart.y)
|
||||
{
|
||||
calcInterceptY = calcInterceptNone;
|
||||
invDeltaY = 0;
|
||||
dy = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
invDeltaY = 1 / (pEnd.y - pStart.y);
|
||||
calcInterceptY = calcInterceptV;
|
||||
if(pEnd.y < pStart.y)
|
||||
dy = -1;
|
||||
else
|
||||
dy = 1;
|
||||
}
|
||||
|
||||
const U32 BlockSquareWidth = mFile->mSize;
|
||||
const U32 GridLevels = mFile->mGridLevels;
|
||||
|
||||
F32 startT = 0;
|
||||
for(;;)
|
||||
{
|
||||
F32 nextXInt = calcInterceptX(pStart.x, invDeltaX, (F32)(blockX + (dx == 1)));
|
||||
F32 nextYInt = calcInterceptY(pStart.y, invDeltaY, (F32)(blockY + (dy == 1)));
|
||||
|
||||
F32 intersectT = 1;
|
||||
|
||||
if(nextXInt < intersectT)
|
||||
intersectT = nextXInt;
|
||||
if(nextYInt < intersectT)
|
||||
intersectT = nextYInt;
|
||||
|
||||
if ( castRayBlock( pStart,
|
||||
pEnd,
|
||||
Point2I( blockX * BlockSquareWidth,
|
||||
blockY * BlockSquareWidth ),
|
||||
GridLevels,
|
||||
invDeltaX,
|
||||
invDeltaY,
|
||||
startT,
|
||||
intersectT,
|
||||
info,
|
||||
collideEmpty ) )
|
||||
{
|
||||
info->normal.z *= BlockSquareWidth * mSquareSize;
|
||||
info->normal.normalize();
|
||||
return true;
|
||||
}
|
||||
|
||||
startT = intersectT;
|
||||
if(intersectT >= 1)
|
||||
break;
|
||||
if(nextXInt < nextYInt)
|
||||
blockX += dx;
|
||||
else if(nextYInt < nextXInt)
|
||||
blockY += dy;
|
||||
else
|
||||
{
|
||||
blockX += dx;
|
||||
blockY += dy;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct TerrLOSStackNode
|
||||
{
|
||||
F32 startT;
|
||||
F32 endT;
|
||||
Point2I blockPos;
|
||||
U32 level;
|
||||
};
|
||||
|
||||
bool TerrainBlock::castRayBlock( const Point3F &pStart,
|
||||
const Point3F &pEnd,
|
||||
const Point2I &aBlockPos,
|
||||
U32 aLevel,
|
||||
F32 invDeltaX,
|
||||
F32 invDeltaY,
|
||||
F32 aStartT,
|
||||
F32 aEndT,
|
||||
RayInfo *info,
|
||||
bool collideEmpty )
|
||||
{
|
||||
const U32 BlockSquareWidth = mFile->mSize;
|
||||
const U32 GridLevels = mFile->mGridLevels;
|
||||
const U32 BlockMask = mFile->mSize - 1;
|
||||
|
||||
F32 invBlockSize = 1 / F32( BlockSquareWidth );
|
||||
|
||||
static Vector<TerrLOSStackNode> stack;
|
||||
stack.setSize( GridLevels * 3 + 1 );
|
||||
U32 stackSize = 1;
|
||||
|
||||
stack[0].startT = aStartT;
|
||||
stack[0].endT = aEndT;
|
||||
stack[0].blockPos = aBlockPos;
|
||||
stack[0].level = aLevel;
|
||||
|
||||
if( !aBlockPos.isZero() )
|
||||
return false;
|
||||
|
||||
while(stackSize--)
|
||||
{
|
||||
TerrLOSStackNode *sn = stack.address() + stackSize;
|
||||
U32 level = sn->level;
|
||||
F32 startT = sn->startT;
|
||||
F32 endT = sn->endT;
|
||||
Point2I blockPos = sn->blockPos;
|
||||
|
||||
const TerrainSquare *sq = mFile->findSquare( level, blockPos.x, blockPos.y );
|
||||
|
||||
F32 startZ = startT * (pEnd.z - pStart.z) + pStart.z;
|
||||
F32 endZ = endT * (pEnd.z - pStart.z) + pStart.z;
|
||||
|
||||
F32 minHeight = fixedToFloat(sq->minHeight);
|
||||
if(startZ <= minHeight && endZ <= minHeight)
|
||||
continue;
|
||||
|
||||
F32 maxHeight = fixedToFloat(sq->maxHeight);
|
||||
if(startZ >= maxHeight && endZ >= maxHeight)
|
||||
continue;
|
||||
|
||||
if ( !collideEmpty && ( sq->flags & TerrainSquare::Empty ) &&
|
||||
blockPos.x == ( blockPos.x & BlockMask ) && blockPos.y == ( blockPos.y & BlockMask ))
|
||||
continue;
|
||||
|
||||
if(level == 0)
|
||||
{
|
||||
F32 xs = blockPos.x * invBlockSize;
|
||||
F32 ys = blockPos.y * invBlockSize;
|
||||
|
||||
F32 zBottomLeft = fixedToFloat( mFile->getHeight(blockPos.x, blockPos.y) );
|
||||
F32 zBottomRight= fixedToFloat( mFile->getHeight(blockPos.x + 1, blockPos.y) );
|
||||
F32 zTopLeft = fixedToFloat( mFile->getHeight(blockPos.x, blockPos.y + 1) );
|
||||
F32 zTopRight = fixedToFloat( mFile->getHeight(blockPos.x + 1, blockPos.y + 1) );
|
||||
|
||||
PlaneF p1, p2;
|
||||
PlaneF divider;
|
||||
Point3F planePoint;
|
||||
|
||||
if(sq->flags & TerrainSquare::Split45)
|
||||
{
|
||||
p1.set(zBottomLeft - zBottomRight, zBottomRight - zTopRight, invBlockSize);
|
||||
p2.set(zTopLeft - zTopRight, zBottomLeft - zTopLeft, invBlockSize);
|
||||
planePoint.set(xs, ys, zBottomLeft);
|
||||
divider.x = 1;
|
||||
divider.y = -1;
|
||||
divider.z = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p1.set(zTopLeft - zTopRight, zBottomRight - zTopRight, invBlockSize);
|
||||
p2.set(zBottomLeft - zBottomRight, zBottomLeft - zTopLeft, invBlockSize);
|
||||
planePoint.set(xs + invBlockSize, ys, zBottomRight);
|
||||
divider.x = 1;
|
||||
divider.y = 1;
|
||||
divider.z = 0;
|
||||
}
|
||||
p1.setPoint(planePoint);
|
||||
p2.setPoint(planePoint);
|
||||
divider.setPoint(planePoint);
|
||||
|
||||
F32 t1 = p1.intersect(pStart, pEnd);
|
||||
F32 t2 = p2.intersect(pStart, pEnd);
|
||||
F32 td = divider.intersect(pStart, pEnd);
|
||||
|
||||
F32 dStart = divider.distToPlane(pStart);
|
||||
F32 dEnd = divider.distToPlane(pEnd);
|
||||
|
||||
// see if the line crosses the divider
|
||||
if((dStart >= 0 && dEnd < 0) || (dStart < 0 && dEnd >= 0))
|
||||
{
|
||||
if(dStart < 0)
|
||||
{
|
||||
F32 temp = t1;
|
||||
t1 = t2;
|
||||
t2 = temp;
|
||||
}
|
||||
if(t1 >= startT && t1 && t1 <= td && t1 <= endT)
|
||||
{
|
||||
info->t = t1;
|
||||
info->normal = p1;
|
||||
return true;
|
||||
}
|
||||
if(t2 >= td && t2 >= startT && t2 <= endT)
|
||||
{
|
||||
info->t = t2;
|
||||
info->normal = p2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 t;
|
||||
if(dStart >= 0) {
|
||||
t = t1;
|
||||
info->normal = p1;
|
||||
}
|
||||
else {
|
||||
t = t2;
|
||||
info->normal = p2;
|
||||
}
|
||||
if(t >= startT && t <= endT)
|
||||
{
|
||||
info->t = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
int subSqWidth = 1 << (level - 1);
|
||||
F32 xIntercept = (blockPos.x + subSqWidth) * invBlockSize;
|
||||
F32 xInt = calcInterceptX(pStart.x, invDeltaX, xIntercept);
|
||||
F32 yIntercept = (blockPos.y + subSqWidth) * invBlockSize;
|
||||
F32 yInt = calcInterceptY(pStart.y, invDeltaY, yIntercept);
|
||||
|
||||
F32 startX = startT * (pEnd.x - pStart.x) + pStart.x;
|
||||
F32 startY = startT * (pEnd.y - pStart.y) + pStart.y;
|
||||
|
||||
if(xInt < startT)
|
||||
xInt = MAX_FLOAT;
|
||||
if(yInt < startT)
|
||||
yInt = MAX_FLOAT;
|
||||
|
||||
U32 x0 = (startX > xIntercept) * subSqWidth;
|
||||
U32 y0 = (startY > yIntercept) * subSqWidth;
|
||||
U32 x1 = subSqWidth - x0;
|
||||
U32 y1 = subSqWidth - y0;
|
||||
U32 nextLevel = level - 1;
|
||||
|
||||
// push the items on the stack in reverse order of processing
|
||||
if(xInt > endT && yInt > endT)
|
||||
{
|
||||
// only test the square the point started in:
|
||||
stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y0);
|
||||
stack[stackSize].level = nextLevel;
|
||||
stackSize++;
|
||||
}
|
||||
else if(xInt < yInt)
|
||||
{
|
||||
F32 nextIntersect = endT;
|
||||
if(yInt <= endT)
|
||||
{
|
||||
stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1);
|
||||
stack[stackSize].startT = yInt;
|
||||
stack[stackSize].endT = endT;
|
||||
stack[stackSize].level = nextLevel;
|
||||
nextIntersect = yInt;
|
||||
stackSize++;
|
||||
}
|
||||
stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y0);
|
||||
stack[stackSize].startT = xInt;
|
||||
stack[stackSize].endT = nextIntersect;
|
||||
stack[stackSize].level = nextLevel;
|
||||
|
||||
stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0);
|
||||
stack[stackSize+1].startT = startT;
|
||||
stack[stackSize+1].endT = xInt;
|
||||
stack[stackSize+1].level = nextLevel;
|
||||
stackSize += 2;
|
||||
}
|
||||
else if(yInt < xInt)
|
||||
{
|
||||
F32 nextIntersect = endT;
|
||||
if(xInt <= endT)
|
||||
{
|
||||
stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1);
|
||||
stack[stackSize].startT = xInt;
|
||||
stack[stackSize].endT = endT;
|
||||
stack[stackSize].level = nextLevel;
|
||||
nextIntersect = xInt;
|
||||
stackSize++;
|
||||
}
|
||||
stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y1);
|
||||
stack[stackSize].startT = yInt;
|
||||
stack[stackSize].endT = nextIntersect;
|
||||
stack[stackSize].level = nextLevel;
|
||||
|
||||
stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0);
|
||||
stack[stackSize+1].startT = startT;
|
||||
stack[stackSize+1].endT = yInt;
|
||||
stack[stackSize+1].level = nextLevel;
|
||||
stackSize += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1);
|
||||
stack[stackSize].startT = xInt;
|
||||
stack[stackSize].endT = endT;
|
||||
stack[stackSize].level = nextLevel;
|
||||
|
||||
stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0);
|
||||
stack[stackSize+1].startT = startT;
|
||||
stack[stackSize+1].endT = xInt;
|
||||
stack[stackSize+1].level = nextLevel;
|
||||
stackSize += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
57
Engine/source/terrain/terrCollision.h
Normal file
57
Engine/source/terrain/terrCollision.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRCOLL_H_
|
||||
#define _TERRCOLL_H_
|
||||
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
|
||||
|
||||
class TerrainConvex : public Convex
|
||||
{
|
||||
friend class TerrainBlock;
|
||||
TerrainConvex *square; ///< Alternate convex if square is concave
|
||||
bool halfA; ///< Which half of square
|
||||
bool split45; ///< Square split pattern
|
||||
U32 squareId; ///< Used to match squares
|
||||
U32 material;
|
||||
Point3F point[4]; ///< 3-4 vertices
|
||||
VectorF normal[2];
|
||||
Box3F box; ///< Bounding box
|
||||
|
||||
public:
|
||||
|
||||
TerrainConvex();
|
||||
|
||||
TerrainConvex( const TerrainConvex& cv );
|
||||
|
||||
// Convex
|
||||
Box3F getBoundingBox() const;
|
||||
Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
|
||||
Point3F support(const VectorF& v) const;
|
||||
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
|
||||
void getPolyList(AbstractPolyList* list);
|
||||
};
|
||||
|
||||
#endif // _TERRCOLL_H_
|
||||
1365
Engine/source/terrain/terrData.cpp
Normal file
1365
Engine/source/terrain/terrData.cpp
Normal file
File diff suppressed because it is too large
Load diff
436
Engine/source/terrain/terrData.h
Normal file
436
Engine/source/terrain/terrData.h
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRDATA_H_
|
||||
#define _TERRDATA_H_
|
||||
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _RENDERPASSMANAGER_H_
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#endif
|
||||
#ifndef _TSIGNAL_H_
|
||||
#include "core/util/tSignal.h"
|
||||
#endif
|
||||
#ifndef _TERRFILE_H_
|
||||
#include "terrain/terrFile.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class GBitmap;
|
||||
class TerrainBlock;
|
||||
class TerrCell;
|
||||
class PhysicsBody;
|
||||
class TerrainCellMaterial;
|
||||
|
||||
class TerrainBlock : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
friend class TerrainEditor;
|
||||
friend class TerrainCellMaterial;
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask,
|
||||
FileMask = Parent::NextFreeMask << 1,
|
||||
SizeMask = Parent::NextFreeMask << 2,
|
||||
MaterialMask = Parent::NextFreeMask << 3,
|
||||
HeightMapChangeMask = Parent::NextFreeMask << 4,
|
||||
MiscMask = Parent::NextFreeMask << 5,
|
||||
|
||||
NextFreeMask = Parent::NextFreeMask << 6,
|
||||
};
|
||||
|
||||
Box3F mBounds;
|
||||
|
||||
///
|
||||
GBitmap *mLightMap;
|
||||
|
||||
/// The lightmap dimensions in pixels.
|
||||
U32 mLightMapSize;
|
||||
|
||||
/// The lightmap texture.
|
||||
GFXTexHandle mLightMapTex;
|
||||
|
||||
/// The terrain data file.
|
||||
Resource<TerrainFile> mFile;
|
||||
|
||||
/// The TerrainFile CRC sent from the server.
|
||||
U32 mCRC;
|
||||
|
||||
///
|
||||
FileName mTerrFileName;
|
||||
|
||||
/// The maximum detail distance found in the material list.
|
||||
F32 mMaxDetailDistance;
|
||||
|
||||
///
|
||||
Vector<GFXTexHandle> mBaseTextures;
|
||||
|
||||
///
|
||||
GFXTexHandle mLayerTex;
|
||||
|
||||
/// The shader used to generate the base texture map.
|
||||
GFXShaderRef mBaseShader;
|
||||
|
||||
///
|
||||
GFXStateBlockRef mBaseShaderSB;
|
||||
|
||||
///
|
||||
GFXShaderConstBufferRef mBaseShaderConsts;
|
||||
|
||||
///
|
||||
GFXShaderConstHandle *mBaseTexScaleConst;
|
||||
GFXShaderConstHandle *mBaseTexIdConst;
|
||||
GFXShaderConstHandle *mBaseLayerSizeConst;
|
||||
|
||||
///
|
||||
GFXTextureTargetRef mBaseTarget;
|
||||
|
||||
/// The base texture.
|
||||
GFXTexHandle mBaseTex;
|
||||
|
||||
///
|
||||
bool mDetailsDirty;
|
||||
|
||||
///
|
||||
bool mLayerTexDirty;
|
||||
|
||||
/// The desired size for the base texture.
|
||||
U32 mBaseTexSize;
|
||||
|
||||
///
|
||||
TerrCell *mCell;
|
||||
|
||||
/// The shared base material which is used to render
|
||||
/// cells that are outside the detail map range.
|
||||
TerrainCellMaterial *mBaseMaterial;
|
||||
|
||||
/// A dummy material only used for shadow
|
||||
/// material generation.
|
||||
BaseMatInstance *mDefaultMatInst;
|
||||
|
||||
F32 mSquareSize;
|
||||
|
||||
PhysicsBody *mPhysicsRep;
|
||||
|
||||
U32 mScreenError;
|
||||
|
||||
/// The shared primitive buffer used in rendering.
|
||||
GFXPrimitiveBufferHandle mPrimBuffer;
|
||||
|
||||
/// The cells used in the last render pass
|
||||
/// when doing debug rendering.
|
||||
/// @see _renderDebug
|
||||
Vector<TerrCell*> mDebugCells;
|
||||
|
||||
/// Set to enable debug rendering of the terrain. It
|
||||
/// is exposed to the console via $terrain::debugRender.
|
||||
static bool smDebugRender;
|
||||
|
||||
/// Allows the terrain to cast shadows onto itself and other objects.
|
||||
bool mCastShadows;
|
||||
|
||||
/// A global LOD scale used to tweak the default
|
||||
/// terrain screen error value.
|
||||
static F32 smLODScale;
|
||||
|
||||
/// A global detail scale used to tweak the
|
||||
/// material detail distances.
|
||||
static F32 smDetailScale;
|
||||
|
||||
/// True if the zoning needs to be recalculated for the terrain.
|
||||
bool mZoningDirty;
|
||||
|
||||
String _getBaseTexCacheFileName() const;
|
||||
|
||||
void _rebuildQuadtree();
|
||||
|
||||
void _updatePhysics();
|
||||
|
||||
void _renderBlock( SceneRenderState *state );
|
||||
void _renderDebug( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
|
||||
|
||||
/// The callback used to get texture events.
|
||||
/// @see GFXTextureManager::addEventDelegate
|
||||
void _onTextureEvent( GFXTexCallbackCode code );
|
||||
|
||||
/// Used to release terrain materials when
|
||||
/// the material manager flushes them.
|
||||
/// @see MaterialManager::getFlushSignal
|
||||
void _onFlushMaterials();
|
||||
|
||||
///
|
||||
bool _initBaseShader();
|
||||
|
||||
///
|
||||
void _updateMaterials();
|
||||
|
||||
///
|
||||
void _updateBaseTexture( bool writeToCache );
|
||||
|
||||
void _updateLayerTexture();
|
||||
|
||||
void _updateBounds();
|
||||
|
||||
void _onZoningChanged( SceneZoneSpaceManager *zoneManager );
|
||||
|
||||
void _updateZoning();
|
||||
|
||||
// Protected fields
|
||||
static bool _setTerrainFile( void *obj, const char *index, const char *data );
|
||||
static bool _setSquareSize( void *obj, const char *index, const char *data );
|
||||
static bool _setBaseTexSize( void *obj, const char *index, const char *data );
|
||||
static bool _setLightMapSize( void *obj, const char *index, const char *data );
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
LightmapUpdate = BIT(0),
|
||||
HeightmapUpdate = BIT(1),
|
||||
LayersUpdate = BIT(2),
|
||||
EmptyUpdate = BIT(3)
|
||||
};
|
||||
|
||||
static Signal<void(U32,TerrainBlock*,const Point2I& ,const Point2I&)> smUpdateSignal;
|
||||
|
||||
///
|
||||
bool import( const GBitmap &heightMap,
|
||||
F32 heightScale,
|
||||
F32 metersPerPixel,
|
||||
const Vector<U8> &layerMap,
|
||||
const Vector<String> &materials );
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
bool exportHeightMap( const UTF8 *filePath, const String &format ) const;
|
||||
bool exportLayerMaps( const UTF8 *filePrefix, const String &format ) const;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
TerrainBlock();
|
||||
virtual ~TerrainBlock();
|
||||
|
||||
U32 getCRC() const { return(mCRC); }
|
||||
|
||||
Resource<TerrainFile> getFile() const { return mFile; };
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
|
||||
/// Adds a new material as the top layer or
|
||||
/// inserts it at the specified index.
|
||||
void addMaterial( const String &name, U32 insertAt = -1 );
|
||||
|
||||
/// Removes the material at the index.
|
||||
void removeMaterial( U32 index );
|
||||
|
||||
/// Updates the material at the index.
|
||||
void updateMaterial( U32 index, const String &name );
|
||||
|
||||
/// Deletes all the materials on the terrain.
|
||||
void deleteAllMaterials();
|
||||
|
||||
//void setMaterialName( U32 index, const String &name );
|
||||
|
||||
/// Accessors and mutators for TerrainMaterialUndoAction.
|
||||
/// @{
|
||||
const Vector<TerrainMaterial*>& getMaterials() const { return mFile->mMaterials; }
|
||||
const Vector<U8>& getLayerMap() const { return mFile->mLayerMap; }
|
||||
void setMaterials( const Vector<TerrainMaterial*> &materials ) { mFile->mMaterials = materials; }
|
||||
void setLayerMap( const Vector<U8> &layers ) { mFile->mLayerMap = layers; }
|
||||
/// @}
|
||||
|
||||
TerrainMaterial* getMaterial( U32 index ) const;
|
||||
|
||||
const char* getMaterialName( U32 index ) const;
|
||||
|
||||
U32 getMaterialCount() const;
|
||||
|
||||
//BaseMatInstance* getMaterialInst( U32 x, U32 y );
|
||||
|
||||
void setHeight( const Point2I &pos, F32 height );
|
||||
F32 getHeight( const Point2I &pos );
|
||||
|
||||
// Performs an update to the selected range of the terrain
|
||||
// grid including the collision and rendering structures.
|
||||
void updateGrid( const Point2I &minPt,
|
||||
const Point2I &maxPt,
|
||||
bool updateClient = false );
|
||||
|
||||
void updateGridMaterials( const Point2I &minPt, const Point2I &maxPt );
|
||||
|
||||
Point2I getGridPos( const Point3F &worldPos ) const;
|
||||
|
||||
/// This returns true and the terrain z height for
|
||||
/// a 2d position in the terrains object space.
|
||||
///
|
||||
/// If the terrain at that point is within an empty block
|
||||
/// or the 2d position is outside of the terrain area then
|
||||
/// it returns false.
|
||||
///
|
||||
bool getHeight( const Point2F &pos, F32 *height ) const;
|
||||
|
||||
void getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const;
|
||||
|
||||
/// This returns true and the terrain normal for a
|
||||
/// 2d position in the terrains object space.
|
||||
///
|
||||
/// If the terrain at that point is within an empty block
|
||||
/// or the 2d position is outside of the terrain area then
|
||||
/// it returns false.
|
||||
///
|
||||
bool getNormal( const Point2F &pos,
|
||||
Point3F *normal,
|
||||
bool normalize = true,
|
||||
bool skipEmpty = true ) const;
|
||||
|
||||
/// This returns true and the smoothed terrain normal
|
||||
// for a 2d position in the terrains object space.
|
||||
///
|
||||
/// If the terrain at that point is within an empty block
|
||||
/// or the 2d position is outside of the terrain area then
|
||||
/// it returns false.
|
||||
///
|
||||
bool getSmoothNormal( const Point2F &pos,
|
||||
Point3F *normal,
|
||||
bool normalize = true,
|
||||
bool skipEmpty = true ) const;
|
||||
|
||||
/// This returns true and the terrain normal and z height
|
||||
/// for a 2d position in the terrains object space.
|
||||
///
|
||||
/// If the terrain at that point is within an empty block
|
||||
/// or the 2d position is outside of the terrain area then
|
||||
/// it returns false.
|
||||
///
|
||||
bool getNormalAndHeight( const Point2F &pos,
|
||||
Point3F *normal,
|
||||
F32 *height,
|
||||
bool normalize = true ) const;
|
||||
|
||||
/// This returns true and the terrain normal, z height, and
|
||||
/// material name for a 2d position in the terrains object
|
||||
/// space.
|
||||
///
|
||||
/// If the terrain at that point is within an empty block
|
||||
/// or the 2d position is outside of the terrain area then
|
||||
/// it returns false.
|
||||
///
|
||||
bool getNormalHeightMaterial( const Point2F &pos,
|
||||
Point3F *normal,
|
||||
F32 *height,
|
||||
StringTableEntry &matName ) const;
|
||||
|
||||
// only the editor currently uses this method - should always be using a ray to collide with
|
||||
bool collideBox( const Point3F &start, const Point3F &end, RayInfo* info )
|
||||
{
|
||||
return castRay( start, end, info );
|
||||
}
|
||||
|
||||
///
|
||||
void setLightMap( GBitmap *newLightMap );
|
||||
|
||||
/// Fills the lightmap with white.
|
||||
void clearLightMap();
|
||||
|
||||
/// Retuns the dimensions of the light map.
|
||||
U32 getLightMapSize() const { return mLightMapSize; }
|
||||
|
||||
const GBitmap* getLightMap() const { return mLightMap; }
|
||||
|
||||
GBitmap* getLightMap() { return mLightMap; }
|
||||
|
||||
///
|
||||
GFXTextureObject* getLightMapTex();
|
||||
|
||||
public:
|
||||
|
||||
bool setFile( const FileName& terrFileName );
|
||||
|
||||
void setFile( Resource<TerrainFile> file );
|
||||
|
||||
bool save(const char* filename);
|
||||
|
||||
F32 getSquareSize() const { return mSquareSize; }
|
||||
|
||||
/// Returns the dimensions of the terrain in world space.
|
||||
F32 getWorldBlockSize() const { return mSquareSize * (F32)mFile->mSize; }
|
||||
|
||||
/// Retuns the dimensions of the terrain in samples.
|
||||
U32 getBlockSize() const { return mFile->mSize; }
|
||||
|
||||
U32 getScreenError() const { return smLODScale * mScreenError; }
|
||||
|
||||
// SceneObject
|
||||
void setTransform( const MatrixF &mat );
|
||||
void setScale( const VectorF &scale );
|
||||
|
||||
void prepRenderImage ( SceneRenderState* state );
|
||||
|
||||
void buildConvex(const Box3F& box,Convex* convex);
|
||||
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
|
||||
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||
bool castRayI(const Point3F &start, const Point3F &end, RayInfo* info, bool emptyCollide);
|
||||
|
||||
bool castRayBlock( const Point3F &pStart,
|
||||
const Point3F &pEnd,
|
||||
const Point2I &blockPos,
|
||||
U32 level,
|
||||
F32 invDeltaX,
|
||||
F32 invDeltaY,
|
||||
F32 startT,
|
||||
F32 endT,
|
||||
RayInfo *info,
|
||||
bool collideEmpty );
|
||||
|
||||
const FileName& getTerrainFile() const { return mTerrFileName; }
|
||||
|
||||
void postLight(Vector<TerrainBlock *> &terrBlocks) {};
|
||||
|
||||
|
||||
DECLARE_CONOBJECT(TerrainBlock);
|
||||
static void initPersistFields();
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
void inspectPostApply();
|
||||
};
|
||||
|
||||
#endif // _TERRDATA_H_
|
||||
162
Engine/source/terrain/terrExport.cpp
Normal file
162
Engine/source/terrain/terrExport.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "terrain/terrMaterial.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
|
||||
bool TerrainBlock::exportHeightMap( const UTF8 *filePath, const String &format ) const
|
||||
{
|
||||
|
||||
GBitmap output( mFile->mSize,
|
||||
mFile->mSize,
|
||||
false,
|
||||
GFXFormatR5G6B5 );
|
||||
|
||||
// First capture the max height... we'll normalize
|
||||
// everything to this value.
|
||||
U16 maxHeight = 0;
|
||||
|
||||
Vector<const U16>::iterator iBits = mFile->mHeightMap.begin();
|
||||
for ( S32 y = 0; y < mFile->mSize; y++ )
|
||||
{
|
||||
for ( S32 x = 0; x < mFile->mSize; x++ )
|
||||
{
|
||||
if ( *iBits > maxHeight )
|
||||
maxHeight = *iBits;
|
||||
++iBits;
|
||||
}
|
||||
}
|
||||
|
||||
// Now write out the map.
|
||||
iBits = mFile->mHeightMap.begin();
|
||||
U16 *oBits = (U16*)output.getWritableBits();
|
||||
for ( S32 y = 0; y < mFile->mSize; y++ )
|
||||
{
|
||||
for ( S32 x = 0; x < mFile->mSize; x++ )
|
||||
{
|
||||
// PNG expects big endian.
|
||||
U16 height = (U16)( ( (F32)(*iBits) / (F32)maxHeight ) * (F32)U16_MAX );
|
||||
*oBits = convertHostToBEndian( height );
|
||||
++oBits;
|
||||
++iBits;
|
||||
}
|
||||
}
|
||||
|
||||
FileStream stream;
|
||||
if ( !stream.open( filePath, Torque::FS::File::Write ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportHeightMap() - Error opening file for writing: %s !", filePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !output.writeBitmap( format, stream ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportHeightMap() - Error writing %s: %s !", format.c_str(), filePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Print out the map size in meters, so that the user
|
||||
// knows what values to use when importing it into
|
||||
// another terrain tool.
|
||||
S32 dim = mSquareSize * mFile->mSize;
|
||||
S32 height = fixedToFloat( maxHeight );
|
||||
Con::printf( "Saved heightmap with dimensions %d x %d x %d.", dim, dim, height );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerrainBlock::exportLayerMaps( const UTF8 *filePrefix, const String &format ) const
|
||||
{
|
||||
for(S32 i = 0; i < mFile->mMaterials.size(); i++)
|
||||
{
|
||||
Vector<const U8>::iterator iBits = mFile->mLayerMap.begin();
|
||||
|
||||
GBitmap output( mFile->mSize,
|
||||
mFile->mSize,
|
||||
false,
|
||||
GFXFormatA8 );
|
||||
|
||||
// Copy the layer data.
|
||||
U8 *oBits = (U8*)output.getWritableBits();
|
||||
dMemset( oBits, 0, mFile->mSize * mFile->mSize );
|
||||
|
||||
for ( S32 y = 0; y < mFile->mSize; y++ )
|
||||
{
|
||||
for ( S32 x = 0; x < mFile->mSize; x++ )
|
||||
{
|
||||
if(*iBits == i)
|
||||
*oBits = 0xFF;
|
||||
++iBits;
|
||||
++oBits;
|
||||
}
|
||||
}
|
||||
|
||||
// Whats the full file name for this layer.
|
||||
UTF8 filePath[1024];
|
||||
dSprintf( filePath, 1024, "%s_%d_%s.%s", filePrefix, i, mFile->mMaterials[i]->getInternalName(), format.c_str() );
|
||||
|
||||
FileStream stream;
|
||||
if ( !stream.open( filePath, Torque::FS::File::Write ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportLayerMaps() - Error opening file for writing: %s !", filePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !output.writeBitmap( format, stream ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportLayerMaps() - Error writing %s: %s !", format.c_str(), filePath );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConsoleMethod( TerrainBlock, exportHeightMap, bool, 3, 4, "(string filename, [string format]) - export the terrain block's heightmap to a bitmap file (default: png)" )
|
||||
{
|
||||
UTF8 fileName[1024];
|
||||
String format = "png";
|
||||
if( argc > 3 )
|
||||
format = argv[ 3 ];
|
||||
|
||||
Con::expandScriptFilename( fileName, sizeof( fileName ), argv[2] );
|
||||
|
||||
return object->exportHeightMap( fileName, format );
|
||||
}
|
||||
|
||||
ConsoleMethod( TerrainBlock, exportLayerMaps, bool, 3, 4, "(string filePrefix, [string format]) - export the terrain block's layer maps to bitmap files (default: png)" )
|
||||
{
|
||||
UTF8 filePrefix[1024];
|
||||
String format = "png";
|
||||
if( argc > 3 )
|
||||
format = argv[3];
|
||||
|
||||
Con::expandScriptFilename( filePrefix, sizeof( filePrefix ), argv[2] );
|
||||
|
||||
return object->exportLayerMaps( filePrefix, format );
|
||||
}
|
||||
#endif
|
||||
36
Engine/source/terrain/terrFeatureTypes.cpp
Normal file
36
Engine/source/terrain/terrFeatureTypes.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrFeatureTypes.h"
|
||||
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
|
||||
|
||||
ImplementFeatureType( MFT_TerrainBaseMap, MFG_Texture, 100.0f, false );
|
||||
ImplementFeatureType( MFT_TerrainParallaxMap, MFG_Texture, 101.0f, false );
|
||||
ImplementFeatureType( MFT_TerrainDetailMap, MFG_Texture, 102.0f, false );
|
||||
ImplementFeatureType( MFT_TerrainNormalMap, MFG_Texture, 103.0f, false );
|
||||
ImplementFeatureType( MFT_TerrainLightMap, MFG_Texture, 104.0f, false );
|
||||
ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 105.0f, false );
|
||||
ImplementFeatureType( MFT_TerrainAdditive, MFG_PostProcess, 999.0f, false );
|
||||
|
||||
39
Engine/source/terrain/terrFeatureTypes.h
Normal file
39
Engine/source/terrain/terrFeatureTypes.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRFEATURETYPES_H_
|
||||
#define _TERRFEATURETYPES_H_
|
||||
|
||||
#ifndef _FEATURETYPE_H_
|
||||
#include "shaderGen/featureType.h"
|
||||
#endif
|
||||
|
||||
DeclareFeatureType( MFT_TerrainBaseMap );
|
||||
DeclareFeatureType( MFT_TerrainDetailMap );
|
||||
DeclareFeatureType( MFT_TerrainNormalMap );
|
||||
DeclareFeatureType( MFT_TerrainParallaxMap );
|
||||
DeclareFeatureType( MFT_TerrainLightMap );
|
||||
DeclareFeatureType( MFT_TerrainSideProject );
|
||||
DeclareFeatureType( MFT_TerrainAdditive );
|
||||
|
||||
#endif // _TERRFEATURETYPES_H_
|
||||
|
||||
852
Engine/source/terrain/terrFile.cpp
Normal file
852
Engine/source/terrain/terrFile.cpp
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrFile.h"
|
||||
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "terrain/terrMaterial.h"
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "math/mPlane.h"
|
||||
|
||||
|
||||
template<>
|
||||
void* Resource<TerrainFile>::create( const Torque::Path &path )
|
||||
{
|
||||
return TerrainFile::load( path );
|
||||
}
|
||||
|
||||
template<> ResourceBase::Signature Resource<TerrainFile>::signature()
|
||||
{
|
||||
return MakeFourCC('t','e','r','d');
|
||||
}
|
||||
|
||||
|
||||
TerrainFile::TerrainFile()
|
||||
: mNeedsResaving( false ),
|
||||
mFileVersion( FILE_VERSION ),
|
||||
mSize( 256 )
|
||||
{
|
||||
mLayerMap.setSize( mSize * mSize );
|
||||
dMemset( mLayerMap.address(), 0, mLayerMap.memSize() );
|
||||
|
||||
mHeightMap.setSize( mSize * mSize );
|
||||
dMemset( mHeightMap.address(), 0, mHeightMap.memSize() );
|
||||
}
|
||||
|
||||
TerrainFile::~TerrainFile()
|
||||
{
|
||||
}
|
||||
|
||||
static U16 calcDev( const PlaneF &pl, const Point3F &pt )
|
||||
{
|
||||
F32 z = (pl.d + pl.x * pt.x + pl.y * pt.y) / -pl.z;
|
||||
F32 diff = z - pt.z;
|
||||
if(diff < 0.0f)
|
||||
diff = -diff;
|
||||
|
||||
if(diff > 0xFFFF)
|
||||
return 0xFFFF;
|
||||
else
|
||||
return U16(diff);
|
||||
}
|
||||
|
||||
static U16 Umax( U16 u1, U16 u2 )
|
||||
{
|
||||
return u1 > u2 ? u1 : u2;
|
||||
}
|
||||
|
||||
|
||||
inline U32 getMostSignificantBit( U32 v )
|
||||
{
|
||||
U32 bit = 0;
|
||||
|
||||
while ( v >>= 1 )
|
||||
bit++;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
void TerrainFile::_buildGridMap()
|
||||
{
|
||||
// The grid level count is the same as the
|
||||
// most significant bit of the size. While
|
||||
// we loop we take the time to calculate the
|
||||
// grid memory pool size.
|
||||
mGridLevels = 0;
|
||||
U32 size = mSize;
|
||||
U32 poolSize = size * size;
|
||||
while ( size >>= 1 )
|
||||
{
|
||||
poolSize += size * size;
|
||||
mGridLevels++;
|
||||
}
|
||||
|
||||
mGridMapPool.setSize( poolSize );
|
||||
mGridMapPool.compact();
|
||||
mGridMap.setSize( mGridLevels + 1 );
|
||||
mGridMap.compact();
|
||||
|
||||
// Assign memory from the pool to each grid level.
|
||||
TerrainSquare *sq = mGridMapPool.address();
|
||||
for ( S32 i = mGridLevels; i >= 0; i-- )
|
||||
{
|
||||
mGridMap[i] = sq;
|
||||
sq += 1 << ( 2 * ( mGridLevels - i ) );
|
||||
}
|
||||
|
||||
for( S32 i = mGridLevels; i >= 0; i-- )
|
||||
{
|
||||
S32 squareCount = 1 << ( mGridLevels - i );
|
||||
S32 squareSize = mSize / squareCount;
|
||||
|
||||
for ( S32 squareX = 0; squareX < squareCount; squareX++ )
|
||||
{
|
||||
for ( S32 squareY = 0; squareY < squareCount; squareY++ )
|
||||
{
|
||||
U16 min = 0xFFFF;
|
||||
U16 max = 0;
|
||||
U16 mindev45 = 0;
|
||||
U16 mindev135 = 0;
|
||||
|
||||
// determine max error for both possible splits.
|
||||
|
||||
const Point3F p1(0, 0, getHeight(squareX * squareSize, squareY * squareSize));
|
||||
const Point3F p2(0, (F32)squareSize, getHeight(squareX * squareSize, squareY * squareSize + squareSize));
|
||||
const Point3F p3((F32)squareSize, (F32)squareSize, getHeight(squareX * squareSize + squareSize, squareY * squareSize + squareSize));
|
||||
const Point3F p4((F32)squareSize, 0, getHeight(squareX * squareSize + squareSize, squareY * squareSize));
|
||||
|
||||
// pl1, pl2 = split45, pl3, pl4 = split135
|
||||
const PlaneF pl1(p1, p2, p3);
|
||||
const PlaneF pl2(p1, p3, p4);
|
||||
const PlaneF pl3(p1, p2, p4);
|
||||
const PlaneF pl4(p2, p3, p4);
|
||||
|
||||
bool parentSplit45 = false;
|
||||
TerrainSquare *parent = NULL;
|
||||
if ( i < mGridLevels )
|
||||
{
|
||||
parent = findSquare( i+1, squareX * squareSize, squareY * squareSize );
|
||||
parentSplit45 = parent->flags & TerrainSquare::Split45;
|
||||
}
|
||||
|
||||
bool empty = true;
|
||||
bool hasEmpty = false;
|
||||
|
||||
for ( S32 sizeX = 0; sizeX <= squareSize; sizeX++ )
|
||||
{
|
||||
for ( S32 sizeY = 0; sizeY <= squareSize; sizeY++ )
|
||||
{
|
||||
S32 x = squareX * squareSize + sizeX;
|
||||
S32 y = squareY * squareSize + sizeY;
|
||||
|
||||
if(sizeX != squareSize && sizeY != squareSize)
|
||||
{
|
||||
if ( !isEmptyAt( x, y ) )
|
||||
empty = false;
|
||||
else
|
||||
hasEmpty = true;
|
||||
}
|
||||
|
||||
U16 ht = getHeight( x, y );
|
||||
if ( ht < min )
|
||||
min = ht;
|
||||
if( ht > max )
|
||||
max = ht;
|
||||
|
||||
Point3F pt( (F32)sizeX, (F32)sizeY, (F32)ht );
|
||||
U16 dev;
|
||||
|
||||
if(sizeX < sizeY)
|
||||
dev = calcDev(pl1, pt);
|
||||
else if(sizeX > sizeY)
|
||||
dev = calcDev(pl2, pt);
|
||||
else
|
||||
dev = Umax(calcDev(pl1, pt), calcDev(pl2, pt));
|
||||
|
||||
if(dev > mindev45)
|
||||
mindev45 = dev;
|
||||
|
||||
if(sizeX + sizeY < squareSize)
|
||||
dev = calcDev(pl3, pt);
|
||||
else if(sizeX + sizeY > squareSize)
|
||||
dev = calcDev(pl4, pt);
|
||||
else
|
||||
dev = Umax(calcDev(pl3, pt), calcDev(pl4, pt));
|
||||
|
||||
if(dev > mindev135)
|
||||
mindev135 = dev;
|
||||
}
|
||||
}
|
||||
|
||||
TerrainSquare *sq = findSquare( i, squareX * squareSize, squareY * squareSize );
|
||||
sq->minHeight = min;
|
||||
sq->maxHeight = max;
|
||||
|
||||
sq->flags = empty ? TerrainSquare::Empty : 0;
|
||||
if ( hasEmpty )
|
||||
sq->flags |= TerrainSquare::HasEmpty;
|
||||
|
||||
bool shouldSplit45 = ((squareX ^ squareY) & 1) == 0;
|
||||
bool split45;
|
||||
|
||||
//split45 = shouldSplit45;
|
||||
if ( i == 0 )
|
||||
split45 = shouldSplit45;
|
||||
else if( i < 4 && shouldSplit45 == parentSplit45 )
|
||||
split45 = shouldSplit45;
|
||||
else
|
||||
split45 = mindev45 < mindev135;
|
||||
|
||||
//split45 = shouldSplit45;
|
||||
if(split45)
|
||||
{
|
||||
sq->flags |= TerrainSquare::Split45;
|
||||
sq->heightDeviance = mindev45;
|
||||
}
|
||||
else
|
||||
sq->heightDeviance = mindev135;
|
||||
|
||||
if( parent )
|
||||
if ( parent->heightDeviance < sq->heightDeviance )
|
||||
parent->heightDeviance = sq->heightDeviance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for ( S32 y = 0; y < mSize; y += 2 )
|
||||
{
|
||||
for ( S32 x=0; x < mSize; x += 2 )
|
||||
{
|
||||
GridSquare *sq = findSquare(1, Point2I(x, y));
|
||||
GridSquare *s1 = findSquare(0, Point2I(x, y));
|
||||
GridSquare *s2 = findSquare(0, Point2I(x+1, y));
|
||||
GridSquare *s3 = findSquare(0, Point2I(x, y+1));
|
||||
GridSquare *s4 = findSquare(0, Point2I(x+1, y+1));
|
||||
sq->flags |= (s1->flags | s2->flags | s3->flags | s4->flags) & ~(GridSquare::MaterialStart -1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void TerrainFile::_initMaterialInstMapping()
|
||||
{
|
||||
mMaterialInstMapping.clearMatInstList();
|
||||
|
||||
for( U32 i = 0; i < mMaterials.size(); ++ i )
|
||||
{
|
||||
Torque::Path path( mMaterials[ i ]->getDiffuseMap() );
|
||||
mMaterialInstMapping.push_back( path.getFileName() );
|
||||
}
|
||||
|
||||
mMaterialInstMapping.mapMaterials();
|
||||
}
|
||||
|
||||
bool TerrainFile::save( const char *filename )
|
||||
{
|
||||
FileStream stream;
|
||||
stream.open( filename, Torque::FS::File::Write );
|
||||
if ( stream.getStatus() != Stream::Ok )
|
||||
return false;
|
||||
|
||||
stream.write( (U8)FILE_VERSION );
|
||||
|
||||
stream.write( mSize );
|
||||
|
||||
// Write out the height map.
|
||||
for ( U32 i=0; i < mHeightMap.size(); i++)
|
||||
stream.write( mHeightMap[i] );
|
||||
|
||||
// Write out the layer map.
|
||||
for ( U32 i=0; i < mLayerMap.size(); i++)
|
||||
stream.write( mLayerMap[i] );
|
||||
|
||||
// Write out the material names.
|
||||
stream.write( (U32)mMaterials.size() );
|
||||
for ( U32 i=0; i < mMaterials.size(); i++ )
|
||||
stream.write( String( mMaterials[i]->getInternalName() ) );
|
||||
|
||||
return stream.getStatus() == FileStream::Ok;
|
||||
}
|
||||
|
||||
TerrainFile* TerrainFile::load( const Torque::Path &path )
|
||||
{
|
||||
FileStream stream;
|
||||
|
||||
stream.open( path.getFullPath(), Torque::FS::File::Read );
|
||||
if ( stream.getStatus() != Stream::Ok )
|
||||
{
|
||||
Con::errorf( "Resource<TerrainFile>::create - could not open '%s'", path.getFullPath().c_str() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U8 version;
|
||||
stream.read(&version);
|
||||
if (version > TerrainFile::FILE_VERSION)
|
||||
{
|
||||
Con::errorf( "Resource<TerrainFile>::create - file version '%i' is newer than engine version '%i'", version, TerrainFile::FILE_VERSION );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TerrainFile *ret = new TerrainFile;
|
||||
ret->mFileVersion = version;
|
||||
ret->mFilePath = path;
|
||||
|
||||
if ( version >= 7 )
|
||||
ret->_load( stream );
|
||||
else
|
||||
ret->_loadLegacy( stream );
|
||||
|
||||
// Update the collision structures.
|
||||
ret->_buildGridMap();
|
||||
|
||||
// Do the material mapping.
|
||||
ret->_initMaterialInstMapping();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TerrainFile::_load( FileStream &stream )
|
||||
{
|
||||
// NOTE: We read using a loop instad of in one large chunk
|
||||
// because the stream will do endian conversions for us when
|
||||
// reading one type at a time.
|
||||
|
||||
stream.read( &mSize );
|
||||
|
||||
// Load the heightmap.
|
||||
mHeightMap.setSize( mSize * mSize );
|
||||
for ( U32 i=0; i < mHeightMap.size(); i++ )
|
||||
stream.read( &mHeightMap[i] );
|
||||
|
||||
// Load the layer index map.
|
||||
mLayerMap.setSize( mSize * mSize );
|
||||
for ( U32 i=0; i < mLayerMap.size(); i++ )
|
||||
stream.read( &mLayerMap[i] );
|
||||
|
||||
// Get the material name count.
|
||||
U32 materialCount;
|
||||
stream.read( &materialCount );
|
||||
Vector<String> materials;
|
||||
materials.setSize( materialCount );
|
||||
|
||||
// Load the material names.
|
||||
for ( U32 i=0; i < materialCount; i++ )
|
||||
stream.read( &materials[i] );
|
||||
|
||||
// Resolve the TerrainMaterial objects from the names.
|
||||
_resolveMaterials( materials );
|
||||
}
|
||||
|
||||
void TerrainFile::_loadLegacy( FileStream &stream )
|
||||
{
|
||||
// Some legacy constants.
|
||||
enum
|
||||
{
|
||||
MaterialGroups = 8,
|
||||
BlockSquareWidth = 256,
|
||||
};
|
||||
|
||||
const U32 sampleCount = BlockSquareWidth * BlockSquareWidth;
|
||||
mSize = BlockSquareWidth;
|
||||
|
||||
// Load the heightmap.
|
||||
mHeightMap.setSize( sampleCount );
|
||||
for ( U32 i=0; i < mHeightMap.size(); i++ )
|
||||
stream.read( &mHeightMap[i] );
|
||||
|
||||
// Prior to version 7 we stored this weird material struct.
|
||||
const U32 MATERIAL_GROUP_MASK = 0x7;
|
||||
struct Material
|
||||
{
|
||||
enum Flags
|
||||
{
|
||||
Plain = 0,
|
||||
Rotate = 1,
|
||||
FlipX = 2,
|
||||
FlipXRotate = 3,
|
||||
FlipY = 4,
|
||||
FlipYRotate = 5,
|
||||
FlipXY = 6,
|
||||
FlipXYRotate = 7,
|
||||
RotateMask = 7,
|
||||
Empty = 8,
|
||||
Modified = BIT(7),
|
||||
|
||||
// must not clobber TerrainFile::MATERIAL_GROUP_MASK bits!
|
||||
PersistMask = BIT(7)
|
||||
};
|
||||
|
||||
U8 flags;
|
||||
U8 index;
|
||||
};
|
||||
|
||||
// Temp locals for loading before we convert to the new
|
||||
// version 7+ format.
|
||||
U8 baseMaterialMap[sampleCount] = { 0 };
|
||||
U8 *materialAlphaMap[MaterialGroups] = { 0 };
|
||||
Material materialMap[BlockSquareWidth * BlockSquareWidth];
|
||||
|
||||
// read the material group map and flags...
|
||||
dMemset(materialMap, 0, sizeof(materialMap));
|
||||
|
||||
AssertFatal(!(Material::PersistMask & MATERIAL_GROUP_MASK),
|
||||
"Doh! We have flag clobberage...");
|
||||
|
||||
for (S32 j=0; j < sampleCount; j++)
|
||||
{
|
||||
U8 val;
|
||||
stream.read(&val);
|
||||
|
||||
//
|
||||
baseMaterialMap[j] = val & MATERIAL_GROUP_MASK;
|
||||
materialMap[j].flags = val & Material::PersistMask;
|
||||
}
|
||||
|
||||
// Load the material names.
|
||||
Vector<String> materials;
|
||||
for ( U32 i=0; i < MaterialGroups; i++ )
|
||||
{
|
||||
String matName;
|
||||
stream.read( &matName );
|
||||
if ( matName.isEmpty() )
|
||||
continue;
|
||||
|
||||
if ( mFileVersion > 3 && mFileVersion < 6 )
|
||||
{
|
||||
// Between version 3 and 5 we store the texture file names
|
||||
// relative to the terrain file. We restore the full path
|
||||
// here so that we can create a TerrainMaterial from it.
|
||||
materials.push_back( Torque::Path::CompressPath( mFilePath.getRoot() + mFilePath.getPath() + '/' + matName ) );
|
||||
}
|
||||
else
|
||||
materials.push_back( matName );
|
||||
}
|
||||
|
||||
if ( mFileVersion <= 3 )
|
||||
{
|
||||
GFXTexHandle terrainMat;
|
||||
Torque::Path matRelPath;
|
||||
|
||||
// Try to automatically fix up our material file names
|
||||
for (U32 i = 0; i < materials.size(); i++)
|
||||
{
|
||||
if ( materials[i].isEmpty() )
|
||||
continue;
|
||||
|
||||
terrainMat.set( materials[i], &GFXDefaultPersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
|
||||
if ( terrainMat )
|
||||
continue;
|
||||
|
||||
matRelPath = materials[i];
|
||||
|
||||
String path = matRelPath.getPath();
|
||||
|
||||
String::SizeType n = path.find( '/', 0, String::NoCase );
|
||||
if ( n != String::NPos )
|
||||
{
|
||||
matRelPath.setPath( String(Con::getVariable( "$defaultGame" )) + path.substr( n, path.length() - n ) );
|
||||
|
||||
terrainMat.set( matRelPath, &GFXDefaultPersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
|
||||
if ( terrainMat )
|
||||
{
|
||||
materials[i] = matRelPath.getFullPath();
|
||||
mNeedsResaving = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // for (U32 i = 0; i < TerrainBlock::MaterialGroups; i++)
|
||||
|
||||
} // if ( mFileVersion <= 3 )
|
||||
|
||||
if ( mFileVersion == 1 )
|
||||
{
|
||||
for( S32 j = 0; j < sampleCount; j++ )
|
||||
{
|
||||
if ( materialAlphaMap[baseMaterialMap[j]] == NULL )
|
||||
{
|
||||
materialAlphaMap[baseMaterialMap[j]] = new U8[sampleCount];
|
||||
dMemset(materialAlphaMap[baseMaterialMap[j]], 0, sampleCount);
|
||||
}
|
||||
|
||||
materialAlphaMap[baseMaterialMap[j]][j] = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( S32 k=0; k < materials.size(); k++ )
|
||||
{
|
||||
AssertFatal(materialAlphaMap[k] == NULL, "Bad assumption. There should be no alpha map at this point...");
|
||||
materialAlphaMap[k] = new U8[sampleCount];
|
||||
stream.read(sampleCount, materialAlphaMap[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// Throw away the old texture and heightfield scripts.
|
||||
if ( mFileVersion >= 3 )
|
||||
{
|
||||
U32 len;
|
||||
stream.read(&len);
|
||||
char *textureScript = (char *)dMalloc(len + 1);
|
||||
stream.read(len, textureScript);
|
||||
dFree( textureScript );
|
||||
|
||||
stream.read(&len);
|
||||
char *heightfieldScript = (char *)dMalloc(len + 1);
|
||||
stream.read(len, heightfieldScript);
|
||||
dFree( heightfieldScript );
|
||||
}
|
||||
|
||||
// Load and throw away the old edge terrain paths.
|
||||
if ( mFileVersion >= 5 )
|
||||
{
|
||||
stream.readSTString(true);
|
||||
stream.readSTString(true);
|
||||
}
|
||||
|
||||
U32 layerCount = materials.size() - 1;
|
||||
|
||||
// Ok... time to convert all this mess to the layer index map!
|
||||
for ( U32 i=0; i < sampleCount; i++ )
|
||||
{
|
||||
// Find the greatest layer.
|
||||
U32 layer = 0;
|
||||
U32 lastValue = 0;
|
||||
for ( U32 k=0; k < MaterialGroups; k++ )
|
||||
{
|
||||
if ( materialAlphaMap[k] && materialAlphaMap[k][i] > lastValue )
|
||||
{
|
||||
layer = k;
|
||||
lastValue = materialAlphaMap[k][i];
|
||||
}
|
||||
}
|
||||
|
||||
// Set the layer index.
|
||||
mLayerMap[i] = getMin( layer, layerCount );
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
for ( U32 i=0; i < MaterialGroups; i++ )
|
||||
delete [] materialAlphaMap[i];
|
||||
|
||||
// Force resaving on these old file versions.
|
||||
//mNeedsResaving = false;
|
||||
|
||||
// Resolve the TerrainMaterial objects from the names.
|
||||
_resolveMaterials( materials );
|
||||
}
|
||||
|
||||
void TerrainFile::_resolveMaterials( const Vector<String> &materials )
|
||||
{
|
||||
mMaterials.clear();
|
||||
|
||||
for ( U32 i=0; i < materials.size(); i++ )
|
||||
mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) );
|
||||
|
||||
// If we didn't get any materials then at least
|
||||
// add a warning material so we will render.
|
||||
if ( mMaterials.empty() )
|
||||
mMaterials.push_back( TerrainMaterial::getWarningMaterial() );
|
||||
}
|
||||
|
||||
void TerrainFile::setSize( U32 newSize, bool clear )
|
||||
{
|
||||
// Make sure the resolution is a power of two.
|
||||
newSize = getNextPow2( newSize );
|
||||
|
||||
//
|
||||
if ( clear )
|
||||
{
|
||||
mLayerMap.setSize( newSize * newSize );
|
||||
mLayerMap.compact();
|
||||
dMemset( mLayerMap.address(), 0, mLayerMap.memSize() );
|
||||
|
||||
// Initialize the elevation to something above
|
||||
// zero so that we have room to excavate by default.
|
||||
U16 elev = floatToFixed( 512.0f );
|
||||
|
||||
mHeightMap.setSize( newSize * newSize );
|
||||
mHeightMap.compact();
|
||||
for ( U32 i = 0; i < mHeightMap.size(); i++ )
|
||||
mHeightMap[i] = elev;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're resizing here!
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
mSize = newSize;
|
||||
|
||||
_buildGridMap();
|
||||
}
|
||||
|
||||
void TerrainFile::smooth( F32 factor, U32 steps, bool updateCollision )
|
||||
{
|
||||
const U32 blockSize = mSize * mSize;
|
||||
|
||||
// Grab some temp buffers for our smoothing results.
|
||||
Vector<F32> h1, h2;
|
||||
h1.setSize( blockSize );
|
||||
h2.setSize( blockSize );
|
||||
|
||||
// Fill the first buffer with the current heights.
|
||||
for ( U32 i=0; i < blockSize; i++ )
|
||||
h1[i] = (F32)mHeightMap[i];
|
||||
|
||||
// factor of 0.0 = NO Smoothing
|
||||
// factor of 1.0 = MAX Smoothing
|
||||
const F32 matrixM = 1.0f - getMax(0.0f, getMin(1.0f, factor));
|
||||
const F32 matrixE = (1.0f-matrixM) * (1.0f/12.0f) * 2.0f;
|
||||
const F32 matrixC = matrixE * 0.5f;
|
||||
|
||||
// Now loop for our interations.
|
||||
F32 *src = h1.address();
|
||||
F32 *dst = h2.address();
|
||||
for ( U32 s=0; s < steps; s++ )
|
||||
{
|
||||
for ( S32 y=0; y < mSize; y++ )
|
||||
{
|
||||
for ( S32 x=0; x < mSize; x++ )
|
||||
{
|
||||
F32 samples[9];
|
||||
|
||||
S32 c = 0;
|
||||
for (S32 i = y-1; i < y+2; i++)
|
||||
for (S32 j = x-1; j < x+2; j++)
|
||||
{
|
||||
if ( i < 0 || j < 0 || i >= mSize || j >= mSize )
|
||||
samples[c++] = src[ x + ( y * mSize ) ];
|
||||
else
|
||||
samples[c++] = src[ j + ( i * mSize ) ];
|
||||
}
|
||||
|
||||
// 0 1 2
|
||||
// 3 x,y 5
|
||||
// 6 7 8
|
||||
|
||||
dst[ x + ( y * mSize ) ] =
|
||||
((samples[0]+samples[2]+samples[6]+samples[8]) * matrixC) +
|
||||
((samples[1]+samples[3]+samples[5]+samples[7]) * matrixE) +
|
||||
(samples[4] * matrixM);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap!
|
||||
F32 *tmp = dst;
|
||||
dst = src;
|
||||
src = tmp;
|
||||
}
|
||||
|
||||
// Copy the results back to the height map.
|
||||
for ( U32 i=0; i < blockSize; i++ )
|
||||
mHeightMap[i] = (U16)mCeil( (F32)src[i] );
|
||||
|
||||
if ( updateCollision )
|
||||
_buildGridMap();
|
||||
}
|
||||
|
||||
void TerrainFile::setHeightMap( const Vector<U16> &heightmap, bool updateCollision )
|
||||
{
|
||||
AssertFatal( mHeightMap.size() == heightmap.size(), "TerrainFile::setHeightMap - Incorrect heightmap size!" );
|
||||
dMemcpy( mHeightMap.address(), heightmap.address(), mHeightMap.size() );
|
||||
|
||||
if ( updateCollision )
|
||||
_buildGridMap();
|
||||
}
|
||||
|
||||
void TerrainFile::import( const GBitmap &heightMap,
|
||||
F32 heightScale,
|
||||
const Vector<U8> &layerMap,
|
||||
const Vector<String> &materials )
|
||||
{
|
||||
AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" );
|
||||
AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" );
|
||||
|
||||
const U32 newSize = heightMap.getWidth();
|
||||
if ( newSize != mSize )
|
||||
{
|
||||
mHeightMap.setSize( newSize * newSize );
|
||||
mHeightMap.compact();
|
||||
mSize = newSize;
|
||||
}
|
||||
|
||||
// Convert the height map to heights.
|
||||
U16 *oBits = mHeightMap.address();
|
||||
if ( heightMap.getFormat() == GFXFormatR5G6B5 )
|
||||
{
|
||||
const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale );
|
||||
const U16 *iBits = (const U16*)heightMap.getBits();
|
||||
for ( U32 i = 0; i < mSize * mSize; i++ )
|
||||
{
|
||||
U16 height = convertBEndianToHost( *iBits );
|
||||
*oBits = (U16)mCeil( (F32)height * toFixedPoint );
|
||||
++oBits;
|
||||
++iBits;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale );
|
||||
const U8 *iBits = heightMap.getBits();
|
||||
for ( U32 i = 0; i < mSize * mSize; i++ )
|
||||
{
|
||||
*oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
|
||||
++oBits;
|
||||
iBits += heightMap.getBytesPerPixel();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over the layer map.
|
||||
AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" );
|
||||
mLayerMap = layerMap;
|
||||
mLayerMap.compact();
|
||||
|
||||
// Resolve the materials.
|
||||
_resolveMaterials( materials );
|
||||
|
||||
// Rebuild the collision grid map.
|
||||
_buildGridMap();
|
||||
}
|
||||
|
||||
|
||||
void TerrainFile::create( String *inOutFilename,
|
||||
U32 newSize,
|
||||
const Vector<String> &materials )
|
||||
{
|
||||
// Determine the path and basename - first try using the input filename (mission name)
|
||||
Torque::Path basePath( *inOutFilename );
|
||||
if ( !basePath.getExtension().equal("mis") )
|
||||
{
|
||||
// Use the default path and filename
|
||||
basePath.setPath( "art/terrains" );
|
||||
basePath.setFileName( "terrain" );
|
||||
}
|
||||
|
||||
// Construct a default file name
|
||||
(*inOutFilename) = Torque::FS::MakeUniquePath( basePath.getRootAndPath(), basePath.getFileName(), "ter" );
|
||||
|
||||
// Create the file
|
||||
TerrainFile *file = new TerrainFile;
|
||||
|
||||
for ( U32 i=0; i < materials.size(); i++ )
|
||||
file->mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) );
|
||||
|
||||
file->setSize( newSize, true );
|
||||
file->save( *inOutFilename );
|
||||
|
||||
delete file;
|
||||
}
|
||||
|
||||
inline void getMinMax( U16 &inMin, U16 &inMax, U16 height )
|
||||
{
|
||||
if ( height < inMin )
|
||||
inMin = height;
|
||||
if ( height > inMax )
|
||||
inMax = height;
|
||||
}
|
||||
|
||||
inline void checkSquare( TerrainSquare *parent, const TerrainSquare *child )
|
||||
{
|
||||
if(parent->minHeight > child->minHeight)
|
||||
parent->minHeight = child->minHeight;
|
||||
if(parent->maxHeight < child->maxHeight)
|
||||
parent->maxHeight = child->maxHeight;
|
||||
|
||||
if ( child->flags & (TerrainSquare::Empty | TerrainSquare::HasEmpty) )
|
||||
parent->flags |= TerrainSquare::HasEmpty;
|
||||
}
|
||||
|
||||
void TerrainFile::updateGrid( const Point2I &minPt, const Point2I &maxPt )
|
||||
{
|
||||
// here's how it works:
|
||||
// for the current terrain renderer we only care about
|
||||
// the minHeight and maxHeight on the GridSquare
|
||||
// so we do one pass through, updating minHeight and maxHeight
|
||||
// on the level 0 squares, then we loop up the grid map from 1 to
|
||||
// the top, expanding the bounding boxes as necessary.
|
||||
// this should end up being way, way, way, way faster for the terrain
|
||||
// editor
|
||||
|
||||
PROFILE_SCOPE( TerrainFile_UpdateGrid );
|
||||
|
||||
for ( S32 y = minPt.y - 1; y < maxPt.y + 1; y++ )
|
||||
{
|
||||
for ( S32 x = minPt.x - 1; x < maxPt.x + 1; x++ )
|
||||
{
|
||||
S32 px = x;
|
||||
S32 py = y;
|
||||
if ( px < 0 )
|
||||
px += mSize;
|
||||
if ( py < 0 )
|
||||
py += mSize;
|
||||
|
||||
TerrainSquare *sq = findSquare( 0, px, py );
|
||||
sq->minHeight = 0xFFFF;
|
||||
sq->maxHeight = 0;
|
||||
|
||||
// Update the empty state.
|
||||
if ( isEmptyAt( x, y ) )
|
||||
sq->flags |= TerrainSquare::Empty;
|
||||
else
|
||||
sq->flags &= ~TerrainSquare::Empty;
|
||||
|
||||
getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y ) );
|
||||
getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y ) );
|
||||
getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y+1 ) );
|
||||
getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y+1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// ok, all the level 0 grid squares are updated:
|
||||
// now update all the parent grid squares that need to be updated:
|
||||
for( S32 level = 1; level <= mGridLevels; level++ )
|
||||
{
|
||||
S32 size = 1 << level;
|
||||
S32 halfSize = size >> 1;
|
||||
|
||||
for( S32 y = (minPt.y - 1) >> level; y < (maxPt.y + size) >> level; y++ )
|
||||
{
|
||||
for ( S32 x = (minPt.x - 1) >> level; x < (maxPt.x + size) >> level; x++ )
|
||||
{
|
||||
S32 px = x << level;
|
||||
S32 py = y << level;
|
||||
|
||||
TerrainSquare *sq = findSquare(level, px, py);
|
||||
sq->minHeight = 0xFFFF;
|
||||
sq->maxHeight = 0;
|
||||
sq->flags &= ~( TerrainSquare::Empty | TerrainSquare::HasEmpty );
|
||||
|
||||
checkSquare( sq, findSquare( level - 1, px, py ) );
|
||||
checkSquare( sq, findSquare( level - 1, px + halfSize, py ) );
|
||||
checkSquare( sq, findSquare( level - 1, px, py + halfSize ) );
|
||||
checkSquare( sq, findSquare( level - 1, px + halfSize, py + halfSize ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
282
Engine/source/terrain/terrFile.h
Normal file
282
Engine/source/terrain/terrFile.h
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRFILE_H_
|
||||
#define _TERRFILE_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include <core/util/tVector.h>
|
||||
#endif
|
||||
#ifndef _PATH_H_
|
||||
#include <core/util/path.h>
|
||||
#endif
|
||||
#ifndef _MATERIALLIST_H_
|
||||
#include "materials/materialList.h"
|
||||
#endif
|
||||
#ifndef _TERRMATERIAL_H_
|
||||
#include "terrain/terrMaterial.h"
|
||||
#endif
|
||||
|
||||
class TerrainMaterial;
|
||||
class FileStream;
|
||||
class GBitmap;
|
||||
|
||||
|
||||
///
|
||||
struct TerrainSquare
|
||||
{
|
||||
U16 minHeight;
|
||||
|
||||
U16 maxHeight;
|
||||
|
||||
U16 heightDeviance;
|
||||
|
||||
U16 flags;
|
||||
|
||||
enum
|
||||
{
|
||||
Split45 = BIT(0),
|
||||
|
||||
Empty = BIT(1),
|
||||
|
||||
HasEmpty = BIT(2),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/// NOTE: The terrain uses 11.5 fixed point which gives
|
||||
/// us a height range from 0->2048 in 1/32 increments.
|
||||
typedef U16 TerrainHeight;
|
||||
|
||||
|
||||
///
|
||||
class TerrainFile
|
||||
{
|
||||
protected:
|
||||
|
||||
friend class TerrainBlock;
|
||||
|
||||
/// The materials used to render the terrain.
|
||||
Vector<TerrainMaterial*> mMaterials;
|
||||
|
||||
/// The dimensions of the layer and height maps.
|
||||
U32 mSize;
|
||||
|
||||
/// The layer index at each height map sample.
|
||||
Vector<U8> mLayerMap;
|
||||
|
||||
/// The fixed point height map.
|
||||
/// @see fixedToFloat
|
||||
Vector<U16> mHeightMap;
|
||||
|
||||
/// The memory pool used by the grid map layers.
|
||||
Vector<TerrainSquare> mGridMapPool;
|
||||
|
||||
///
|
||||
U32 mGridLevels;
|
||||
|
||||
/// The grid map layers used to accelerate collision
|
||||
/// queries for the height map data.
|
||||
Vector<TerrainSquare*> mGridMap;
|
||||
|
||||
/// MaterialList used to map terrain materials to material instances for the
|
||||
/// sake of collision (physics, etc.).
|
||||
MaterialList mMaterialInstMapping;
|
||||
|
||||
/// The file version.
|
||||
U32 mFileVersion;
|
||||
|
||||
/// The dirty flag.
|
||||
bool mNeedsResaving;
|
||||
|
||||
/// The full path and name of the TerrainFile
|
||||
Torque::Path mFilePath;
|
||||
|
||||
/// The internal loading function.
|
||||
void _load( FileStream &stream );
|
||||
|
||||
/// The legacy file loading code.
|
||||
void _loadLegacy( FileStream &stream );
|
||||
|
||||
/// Used to populate the materail vector by finding the
|
||||
/// TerrainMaterial objects by name.
|
||||
void _resolveMaterials( const Vector<String> &materials );
|
||||
|
||||
///
|
||||
void _buildGridMap();
|
||||
|
||||
///
|
||||
void _initMaterialInstMapping();
|
||||
|
||||
public:
|
||||
|
||||
enum Constants
|
||||
{
|
||||
FILE_VERSION = 7
|
||||
};
|
||||
|
||||
TerrainFile();
|
||||
|
||||
virtual ~TerrainFile();
|
||||
|
||||
///
|
||||
static void create( String *inOutFilename,
|
||||
U32 newSize,
|
||||
const Vector<String> &materials );
|
||||
|
||||
///
|
||||
static TerrainFile* load( const Torque::Path &path );
|
||||
|
||||
bool save( const char *filename );
|
||||
|
||||
///
|
||||
void import( const GBitmap &heightMap,
|
||||
F32 heightScale,
|
||||
const Vector<U8> &layerMap,
|
||||
const Vector<String> &materials );
|
||||
|
||||
/// Updates the terrain grid for the specified area.
|
||||
void updateGrid( const Point2I &minPt, const Point2I &maxPt );
|
||||
|
||||
/// Performs multiple smoothing steps on the heightmap.
|
||||
void smooth( F32 factor, U32 steps, bool updateCollision );
|
||||
|
||||
void setSize( U32 newResolution, bool clear );
|
||||
|
||||
TerrainSquare* findSquare( U32 level, U32 x, U32 y ) const;
|
||||
|
||||
BaseMatInstance* getMaterialMapping( U32 index ) const;
|
||||
|
||||
StringTableEntry getMaterialName( U32 x, U32 y) const;
|
||||
|
||||
void setLayerIndex( U32 x, U32 y, U8 index );
|
||||
|
||||
U8 getLayerIndex( U32 x, U32 y ) const;
|
||||
|
||||
bool isEmptyAt( U32 x, U32 y ) const { return getLayerIndex( x, y ) == U8_MAX; }
|
||||
|
||||
void setHeight( U32 x, U32 y, U16 height );
|
||||
|
||||
const U16* getHeightAddress( U32 x, U32 y ) const;
|
||||
|
||||
U16 getHeight( U32 x, U32 y ) const;
|
||||
|
||||
U16 getMaxHeight() const { return mGridMap[mGridLevels]->maxHeight; }
|
||||
|
||||
/// Returns the constant heightmap vector.
|
||||
const Vector<U16>& getHeightMap() const { return mHeightMap; }
|
||||
|
||||
/// Sets a new heightmap state.
|
||||
void setHeightMap( const Vector<U16> &heightmap, bool updateCollision );
|
||||
|
||||
/// Check if the given point is valid within the (non-tiled) terrain file.
|
||||
bool isPointInTerrain( U32 x, U32 y ) const;
|
||||
};
|
||||
|
||||
|
||||
inline TerrainSquare* TerrainFile::findSquare( U32 level, U32 x, U32 y ) const
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
x >>= level;
|
||||
y >>= level;
|
||||
|
||||
return mGridMap[level] + x + ( y << ( mGridLevels - level ) );
|
||||
}
|
||||
|
||||
inline void TerrainFile::setHeight( U32 x, U32 y, U16 height )
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
mHeightMap[ x + ( y * mSize ) ] = height;
|
||||
}
|
||||
|
||||
inline const U16* TerrainFile::getHeightAddress( U32 x, U32 y ) const
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
return &mHeightMap[ x + ( y * mSize ) ];
|
||||
}
|
||||
|
||||
inline U16 TerrainFile::getHeight( U32 x, U32 y ) const
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
return mHeightMap[ x + ( y * mSize ) ];
|
||||
}
|
||||
|
||||
inline U8 TerrainFile::getLayerIndex( U32 x, U32 y ) const
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
return mLayerMap[ x + ( y * mSize ) ];
|
||||
}
|
||||
|
||||
inline void TerrainFile::setLayerIndex( U32 x, U32 y, U8 index )
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
mLayerMap[ x + ( y * mSize ) ] = index;
|
||||
}
|
||||
|
||||
inline BaseMatInstance* TerrainFile::getMaterialMapping( U32 index ) const
|
||||
{
|
||||
if ( index < mMaterialInstMapping.size() )
|
||||
return mMaterialInstMapping.getMaterialInst( index );
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline StringTableEntry TerrainFile::getMaterialName( U32 x, U32 y) const
|
||||
{
|
||||
x %= mSize;
|
||||
y %= mSize;
|
||||
const U8 &index = mLayerMap[ x + ( y * mSize ) ];
|
||||
|
||||
if ( index < mMaterials.size() )
|
||||
return mMaterials[ index ]->getInternalName();
|
||||
|
||||
return StringTable->EmptyString();
|
||||
}
|
||||
|
||||
|
||||
/// Conversion from 11.5 fixed point to floating point.
|
||||
inline F32 fixedToFloat( U16 val )
|
||||
{
|
||||
return F32(val) * 0.03125f;
|
||||
}
|
||||
|
||||
/// Conversion from floating point to 11.5 fixed point.
|
||||
inline U16 floatToFixed( F32 val )
|
||||
{
|
||||
return U16(val * 32.0);
|
||||
}
|
||||
|
||||
inline bool TerrainFile::isPointInTerrain( U32 x, U32 y ) const
|
||||
{
|
||||
if ( x < mSize && y < mSize)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // _TERRFILE_H_
|
||||
313
Engine/source/terrain/terrImport.cpp
Normal file
313
Engine/source/terrain/terrImport.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "terrain/terrData.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "core/strings/stringUnit.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "gui/worldEditor/terrainEditor.h"
|
||||
#include "util/noise2d.h"
|
||||
#include "core/volume.h"
|
||||
|
||||
|
||||
ConsoleStaticMethod( TerrainBlock, createNew, S32, 5, 5,
|
||||
"TerrainBlock.create( String terrainName, U32 resolution, String materialName, bool genNoise )\n"
|
||||
"" )
|
||||
{
|
||||
const UTF8 *terrainName = argv[1];
|
||||
U32 resolution = dAtoi( argv[2] );
|
||||
const UTF8 *materialName = argv[3];
|
||||
bool genNoise = dAtob( argv[4] );
|
||||
|
||||
Vector<String> materials;
|
||||
materials.push_back( materialName );
|
||||
|
||||
TerrainBlock *terrain = new TerrainBlock();
|
||||
|
||||
// We create terrains based on level name. If the user wants to rename the terrain names; they have to
|
||||
// rename it themselves in their file browser. The main reason for this is so we can easily increment for ourselves;
|
||||
// and because its too easy to rename the terrain object and forget to take care of the terrain filename afterwards.
|
||||
FileName terrFileName( Con::getVariable("$Client::MissionFile") );
|
||||
terrFileName.replace("tools/levels/", "art/terrains/");
|
||||
terrFileName.replace("levels/", "art/terrains/");
|
||||
|
||||
TerrainFile::create( &terrFileName, resolution, materials );
|
||||
|
||||
if( !terrain->setFile( terrFileName ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::createNew - error creating '%s'", terrFileName.c_str() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
terrain->setPosition( Point3F( 0, 0, 0 ) );
|
||||
|
||||
const U32 blockSize = terrain->getBlockSize();
|
||||
|
||||
if ( genNoise )
|
||||
{
|
||||
TerrainFile *file = terrain->getFile();
|
||||
|
||||
Vector<F32> floatHeights;
|
||||
floatHeights.setSize( blockSize * blockSize );
|
||||
|
||||
Noise2D noise;
|
||||
noise.setSeed( 134208587 );
|
||||
|
||||
// Set up some defaults.
|
||||
F32 octaves = 3.0f;
|
||||
U32 freq = 4;
|
||||
F32 roughness = 0.0f;
|
||||
noise.fBm( &floatHeights, blockSize, freq, 1.0f - roughness, octaves );
|
||||
|
||||
F32 height = 0;
|
||||
|
||||
F32 omax, omin;
|
||||
noise.getMinMax( &floatHeights, &omin, &omax, blockSize );
|
||||
|
||||
F32 terrscale = 300.0f / (omax - omin);
|
||||
for ( S32 y = 0; y < blockSize; y++ )
|
||||
{
|
||||
for ( S32 x = 0; x < blockSize; x++ )
|
||||
{
|
||||
// Very important to subtract the min
|
||||
// noise value when using the noise functions
|
||||
// for terrain, otherwise floatToFixed() will
|
||||
// wrap negative values to U16_MAX, creating
|
||||
// a very ugly terrain.
|
||||
height = (floatHeights[ x + (y * blockSize) ] - omin) * terrscale + 30.0f;
|
||||
file->setHeight( x, y, floatToFixed( height ) );
|
||||
}
|
||||
}
|
||||
|
||||
terrain->updateGrid( Point2I::Zero, Point2I( blockSize, blockSize ) );
|
||||
terrain->updateGridMaterials( Point2I::Zero, Point2I( blockSize, blockSize ) );
|
||||
}
|
||||
|
||||
terrain->registerObject( terrainName );
|
||||
|
||||
// Add to mission group!
|
||||
SimGroup *missionGroup;
|
||||
if( Sim::findObject( "MissionGroup", missionGroup ) )
|
||||
missionGroup->addObject( terrain );
|
||||
|
||||
return terrain->getId();
|
||||
}
|
||||
|
||||
ConsoleStaticMethod( TerrainBlock, import, S32, 7, 7,
|
||||
"( String terrainName, String heightMap, F32 metersPerPixel, F32 heightScale, String materials, String opacityLayers )\n"
|
||||
"" )
|
||||
{
|
||||
// Get the parameters.
|
||||
const UTF8 *terrainName = argv[1];
|
||||
const UTF8 *hmap = argv[2];
|
||||
F32 metersPerPixel = dAtof(argv[3]);
|
||||
F32 heightScale = dAtof(argv[4]);
|
||||
const UTF8 *opacityFiles = argv[5];
|
||||
const UTF8 *materialsStr = argv[6];
|
||||
|
||||
// First load the height map and validate it.
|
||||
Resource<GBitmap> heightmap = GBitmap::load( hmap );
|
||||
if ( !heightmap )
|
||||
{
|
||||
Con::errorf( "Heightmap failed to load!" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
U32 terrSize = heightmap->getWidth();
|
||||
U32 hheight = heightmap->getHeight();
|
||||
if ( terrSize != hheight || !isPow2( terrSize ) )
|
||||
{
|
||||
Con::errorf( "Height map must be square and power of two in size!" );
|
||||
return 0;
|
||||
}
|
||||
else if ( terrSize < 128 || terrSize > 4096 )
|
||||
{
|
||||
Con::errorf( "Height map must be between 128 and 4096 in size!" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
U32 fileCount = StringUnit::getUnitCount( opacityFiles, "\n" );
|
||||
Vector<U8> layerMap;
|
||||
layerMap.setSize( terrSize * terrSize );
|
||||
{
|
||||
Vector<GBitmap*> bitmaps;
|
||||
|
||||
for ( U32 i = 0; i < fileCount; i++ )
|
||||
{
|
||||
String fileNameWithChannel = StringUnit::getUnit( opacityFiles, i, "\n" );
|
||||
String fileName = StringUnit::getUnit( fileNameWithChannel, 0, "\t" );
|
||||
String channel = StringUnit::getUnit( fileNameWithChannel, 1, "\t" );
|
||||
|
||||
if ( fileName.isEmpty() )
|
||||
continue;
|
||||
|
||||
if ( !channel.isEmpty() )
|
||||
{
|
||||
// Load and push back the bitmap here.
|
||||
Resource<GBitmap> opacityMap = ResourceManager::get().load( fileName );
|
||||
if ( terrSize != opacityMap->getWidth() || terrSize != opacityMap->getHeight() )
|
||||
{
|
||||
Con::errorf( "The opacity map '%s' doesn't match height map size!", fileName.c_str() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Always going to be one channel.
|
||||
GBitmap *opacityMapChannel = new GBitmap( terrSize,
|
||||
terrSize,
|
||||
false,
|
||||
GFXFormatA8 );
|
||||
|
||||
if ( opacityMap->getBytesPerPixel() > 1 )
|
||||
{
|
||||
if ( channel.equal( "R", 1 ) )
|
||||
opacityMap->copyChannel( 0, opacityMapChannel );
|
||||
else if ( channel.equal( "G", 1 ) )
|
||||
opacityMap->copyChannel( 1, opacityMapChannel );
|
||||
else if ( channel.equal( "B", 1 ) )
|
||||
opacityMap->copyChannel( 2, opacityMapChannel );
|
||||
else if ( channel.equal( "A", 1 ) )
|
||||
opacityMap->copyChannel( 3, opacityMapChannel );
|
||||
|
||||
bitmaps.push_back( opacityMapChannel );
|
||||
}
|
||||
else
|
||||
{
|
||||
opacityMapChannel->copyRect( opacityMap, RectI( 0, 0, terrSize, terrSize ), Point2I( 0, 0 ) );
|
||||
bitmaps.push_back( opacityMapChannel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ok... time to convert all this opacity layer
|
||||
// mess to the layer index map!
|
||||
U32 layerCount = bitmaps.size() - 1;
|
||||
U32 layer, lastValue;
|
||||
U8 value;
|
||||
for ( U32 i = 0; i < terrSize * terrSize; i++ )
|
||||
{
|
||||
// Find the greatest layer.
|
||||
layer = lastValue = 0;
|
||||
for ( U32 k=0; k < bitmaps.size(); k++ )
|
||||
{
|
||||
value = bitmaps[k]->getBits()[i];
|
||||
if ( value >= lastValue )
|
||||
{
|
||||
layer = k;
|
||||
lastValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the layer index.
|
||||
layerMap[i] = getMin( layer, layerCount );
|
||||
}
|
||||
|
||||
// Cleanup the bitmaps.
|
||||
for ( U32 i=0; i < bitmaps.size(); i++ )
|
||||
delete bitmaps[i];
|
||||
}
|
||||
|
||||
U32 matCount = StringUnit::getUnitCount( materialsStr, "\t\n" );
|
||||
if( matCount != fileCount)
|
||||
{
|
||||
Con::errorf("Number of Materials and Layer maps must be equal.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vector<String> materials;
|
||||
for ( U32 i = 0; i < matCount; i++ )
|
||||
{
|
||||
String matStr = StringUnit::getUnit( materialsStr, i, "\t\n" );
|
||||
// even if matStr is empty, insert it as a placeholder (will be replaced with warning material later)
|
||||
materials.push_back( matStr );
|
||||
}
|
||||
|
||||
// Do we have an existing terrain with that name... then update it!
|
||||
TerrainBlock *terrain = dynamic_cast<TerrainBlock*>( Sim::findObject( terrainName ) );
|
||||
if ( terrain )
|
||||
terrain->import( (*heightmap), heightScale, metersPerPixel, layerMap, materials );
|
||||
else
|
||||
{
|
||||
terrain = new TerrainBlock();
|
||||
terrain->assignName( terrainName );
|
||||
terrain->import( (*heightmap), heightScale, metersPerPixel, layerMap, materials );
|
||||
terrain->registerObject();
|
||||
|
||||
// Add to mission group!
|
||||
SimGroup *missionGroup;
|
||||
if ( Sim::findObject( "MissionGroup", missionGroup ) )
|
||||
missionGroup->addObject( terrain );
|
||||
}
|
||||
|
||||
return terrain->getId();
|
||||
}
|
||||
|
||||
bool TerrainBlock::import( const GBitmap &heightMap,
|
||||
F32 heightScale,
|
||||
F32 metersPerPixel,
|
||||
const Vector<U8> &layerMap,
|
||||
const Vector<String> &materials )
|
||||
{
|
||||
AssertFatal( isServerObject(), "TerrainBlock::import - This should only be called on the server terrain!" );
|
||||
|
||||
AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainBlock::import - Height map is not square!" );
|
||||
AssertFatal( isPow2( heightMap.getWidth() ), "TerrainBlock::import - Height map is not power of two!" );
|
||||
|
||||
// If we don't have a terrain file then add one.
|
||||
if ( !mFile )
|
||||
{
|
||||
// Get a unique file name for the terrain.
|
||||
String fileName( getName() );
|
||||
if ( fileName.isEmpty() )
|
||||
fileName = "terrain";
|
||||
mTerrFileName = FS::MakeUniquePath( "levels", fileName, "ter" );
|
||||
|
||||
// TODO: We have to save and reload the file to get
|
||||
// it into the resource system. This creates lots
|
||||
// of temporary unused files when the terrain is
|
||||
// discarded because of undo or quit.
|
||||
TerrainFile *file = new TerrainFile;
|
||||
file->save( mTerrFileName );
|
||||
delete file;
|
||||
mFile = ResourceManager::get().load( mTerrFileName );
|
||||
}
|
||||
|
||||
// The file does a bunch of the work.
|
||||
mFile->import( heightMap, heightScale, layerMap, materials );
|
||||
|
||||
// Set the square size.
|
||||
mSquareSize = metersPerPixel;
|
||||
|
||||
if ( isProperlyAdded() )
|
||||
{
|
||||
// Update the server bounds.
|
||||
_updateBounds();
|
||||
|
||||
// Make sure the client gets updated.
|
||||
setMaskBits( HeightMapChangeMask | SizeMask );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
92
Engine/source/terrain/terrLighting.cpp
Normal file
92
Engine/source/terrain/terrLighting.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "terrain/terrRender.h"
|
||||
#include "lighting/lightInfo.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
/*
|
||||
|
||||
U32 TerrainRender::testSquareLights(GridSquare *sq, S32 level, const Point2I &pos, U32 lightMask)
|
||||
{
|
||||
|
||||
// Calculate our Box3F for this GridSquare
|
||||
Point3F boxMin(pos.x * mSquareSize + mBlockPos.x, pos.y * mSquareSize + mBlockPos.y, fixedToFloat(sq->minHeight));
|
||||
F32 blockSize = F32(mSquareSize * (1 << level));
|
||||
F32 blockHeight = fixedToFloat(sq->maxHeight - sq->minHeight);
|
||||
Point3F boxMax(boxMin);
|
||||
boxMax += Point3F(blockSize, blockSize, blockHeight);
|
||||
Box3F gridBox(boxMin, boxMax);
|
||||
|
||||
U32 retMask = 0;
|
||||
|
||||
for(S32 i = 0; (lightMask >> i) != 0; i++)
|
||||
{
|
||||
if(lightMask & (1 << i))
|
||||
{
|
||||
if (mTerrainLights[i].light->mType != LightInfo::Vector)
|
||||
{
|
||||
// test the visibility of this light to box
|
||||
F32 dist = gridBox.getDistanceFromPoint(mTerrainLights[i].pos);
|
||||
static F32 minDist = 1e14f;
|
||||
minDist = getMin(minDist, dist);
|
||||
if(dist < mTerrainLights[i].radius)
|
||||
retMask |= (1 << i);
|
||||
} else {
|
||||
retMask |= (1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void TerrainRender::buildLightArray(SceneState * state)
|
||||
{
|
||||
PROFILE_SCOPE(TerrainRender_buildLightArray);
|
||||
|
||||
mDynamicLightCount = 0;
|
||||
if ((mTerrainLighting == NULL) || (!TerrainRender::mEnableTerrainDynLights))
|
||||
return;
|
||||
|
||||
static LightInfoList lights;
|
||||
lights.clear();
|
||||
|
||||
LIGHTMGR->getBestLights(lights);
|
||||
// create terrain lights from these...
|
||||
U32 curIndex = 0;
|
||||
for(U32 i = 0; i < lights.size(); i++)
|
||||
{
|
||||
LightInfo* light = lights[i];
|
||||
if((light->mType != LightInfo::Point) && (light->mType != LightInfo::Spot))
|
||||
continue;
|
||||
|
||||
// set the 'fo
|
||||
TerrLightInfo & info = mTerrainLights[curIndex++];
|
||||
mCurrentBlock->getWorldTransform().mulP(light->mPos, &info.pos);
|
||||
info.radius = light->getRadius();
|
||||
info.light = light;
|
||||
}
|
||||
|
||||
mDynamicLightCount = curIndex;
|
||||
}
|
||||
|
||||
*/
|
||||
165
Engine/source/terrain/terrMaterial.cpp
Normal file
165
Engine/source/terrain/terrMaterial.cpp
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrMaterial.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT( TerrainMaterial );
|
||||
|
||||
ConsoleDocClass( TerrainMaterial,
|
||||
"@brief The TerrainMaterial class orginizes the material settings "
|
||||
"for a single terrain material layer.\n\n"
|
||||
|
||||
"@note You should not be creating TerrainMaterials by hand in code. "
|
||||
"All TerrainMaterials should be created in the editors, as intended "
|
||||
"by the system.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Created by the Terrain Painter tool in the World Editor\n"
|
||||
"new TerrainMaterial()\n"
|
||||
"{\n"
|
||||
" internalName = \"grass1\";\n"
|
||||
" diffuseMap = \"art/terrains/Test/grass1\";\n"
|
||||
" detailMap = \"art/terrains/Test/grass1_d\";\n"
|
||||
" detailSize = \"10\";\n"
|
||||
" isManaged = \"1\";\n"
|
||||
" detailBrightness = \"1\";\n"
|
||||
" Enabled = \"1\";\n"
|
||||
" diffuseSize = \"200\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see Materials\n"
|
||||
|
||||
"@ingroup enviroMisc\n");
|
||||
|
||||
TerrainMaterial::TerrainMaterial()
|
||||
: mSideProjection( false ),
|
||||
mDiffuseSize( 500.0f ),
|
||||
mDetailSize( 5.0f ),
|
||||
mDetailStrength( 1.0f ),
|
||||
mDetailDistance( 50.0f ),
|
||||
mParallaxScale( 0.0f )
|
||||
{
|
||||
}
|
||||
|
||||
TerrainMaterial::~TerrainMaterial()
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainMaterial::initPersistFields()
|
||||
{
|
||||
addField( "diffuseMap", TypeStringFilename, Offset( mDiffuseMap, TerrainMaterial ), "Base texture for the material" );
|
||||
addField( "diffuseSize", TypeF32, Offset( mDiffuseSize, TerrainMaterial ), "Used to scale the diffuse map to the material square" );
|
||||
|
||||
addField( "normalMap", TypeStringFilename, Offset( mNormalMap, TerrainMaterial ), "Bump map for the material" );
|
||||
|
||||
addField( "detailMap", TypeStringFilename, Offset( mDetailMap, TerrainMaterial ), "Detail map for the material" );
|
||||
addField( "detailSize", TypeF32, Offset( mDetailSize, TerrainMaterial ), "Used to scale the detail map to the material square" );
|
||||
|
||||
addField( "detailStrength", TypeF32, Offset( mDetailStrength, TerrainMaterial ), "Exponentially sharpens or lightens the detail map rendering on the material" );
|
||||
addField( "detailDistance", TypeF32, Offset( mDetailDistance, TerrainMaterial ), "Changes how far camera can see the detail map rendering on the material" );
|
||||
addField( "useSideProjection", TypeBool, Offset( mSideProjection, TerrainMaterial ),"Makes that terrain material project along the sides of steep "
|
||||
"slopes instead of projected downwards");
|
||||
addField( "parallaxScale", TypeF32, Offset( mParallaxScale, TerrainMaterial ), "Used to scale the height from the normal map to give some self "
|
||||
"occlusion effect (aka parallax) to the terrain material" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
|
||||
// Gotta call this at least once or it won't get created!
|
||||
Sim::getTerrainMaterialSet();
|
||||
}
|
||||
|
||||
bool TerrainMaterial::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
SimSet *set = Sim::getTerrainMaterialSet();
|
||||
|
||||
// Make sure we have an internal name set.
|
||||
if ( !mInternalName || !mInternalName[0] )
|
||||
Con::warnf( "TerrainMaterial::onAdd() - No internal name set!" );
|
||||
else
|
||||
{
|
||||
SimObject *object = set->findObjectByInternalName( mInternalName );
|
||||
if ( object )
|
||||
Con::warnf( "TerrainMaterial::onAdd() - Internal name collision; '%s' already exists!", mInternalName );
|
||||
}
|
||||
|
||||
set->addObject( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TerrainMaterial* TerrainMaterial::getWarningMaterial()
|
||||
{
|
||||
return findOrCreate( NULL );
|
||||
}
|
||||
|
||||
TerrainMaterial* TerrainMaterial::findOrCreate( const char *nameOrPath )
|
||||
{
|
||||
SimSet *set = Sim::getTerrainMaterialSet();
|
||||
|
||||
if ( !nameOrPath || !nameOrPath[0] )
|
||||
nameOrPath = "warning_material";
|
||||
|
||||
// See if we can just find it.
|
||||
TerrainMaterial *mat = dynamic_cast<TerrainMaterial*>( set->findObjectByInternalName( StringTable->insert( nameOrPath ) ) );
|
||||
if ( mat )
|
||||
return mat;
|
||||
|
||||
// We didn't find it... so see if its a path to a
|
||||
// file. If it is lets assume its the texture.
|
||||
if ( GBitmap::sFindFiles( nameOrPath, NULL ) )
|
||||
{
|
||||
mat = new TerrainMaterial();
|
||||
mat->setInternalName( nameOrPath );
|
||||
mat->mDiffuseMap = nameOrPath;
|
||||
mat->registerObject();
|
||||
Sim::getRootGroup()->addObject( mat );
|
||||
return mat;
|
||||
}
|
||||
|
||||
// Ok... return a debug material then.
|
||||
mat = dynamic_cast<TerrainMaterial*>( set->findObjectByInternalName( StringTable->insert( "warning_material" ) ) );
|
||||
if ( !mat )
|
||||
{
|
||||
// This shouldn't happen.... the warning_texture should
|
||||
// have already been defined in script, but we put this
|
||||
// fallback here just in case it gets "lost".
|
||||
mat = new TerrainMaterial();
|
||||
mat->setInternalName( "warning_material" );
|
||||
mat->mDiffuseMap = "core/art/warnMat.png";
|
||||
mat->mDiffuseSize = 500;
|
||||
mat->mDetailMap = "core/art/warnMat.png";
|
||||
mat->mDetailSize = 5;
|
||||
mat->registerObject();
|
||||
|
||||
Sim::getRootGroup()->addObject( mat );
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
111
Engine/source/terrain/terrMaterial.h
Normal file
111
Engine/source/terrain/terrMaterial.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRMATERIAL_H_
|
||||
#define _TERRMATERIAL_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// The TerrainMaterial class orginizes the material settings
|
||||
/// for a single terrain material layer.
|
||||
class TerrainMaterial : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
FileName mDiffuseMap;
|
||||
|
||||
/// The size of the diffuse base map in meters
|
||||
/// used to generate its texture coordinates.
|
||||
F32 mDiffuseSize;
|
||||
|
||||
///
|
||||
FileName mNormalMap;
|
||||
|
||||
///
|
||||
FileName mDetailMap;
|
||||
|
||||
/// The size of the detail map in meters used
|
||||
/// to generate the texture coordinates for the
|
||||
/// detail and normal maps.
|
||||
F32 mDetailSize;
|
||||
|
||||
///
|
||||
F32 mDetailStrength;
|
||||
|
||||
///
|
||||
F32 mDetailDistance;
|
||||
|
||||
/// Normally the detail is projected on to the xy
|
||||
/// coordinates of the terrain. If this flag is true
|
||||
/// then this detail is projected along the xz and yz
|
||||
/// planes.
|
||||
bool mSideProjection;
|
||||
|
||||
///
|
||||
F32 mParallaxScale;
|
||||
|
||||
public:
|
||||
|
||||
TerrainMaterial();
|
||||
virtual ~TerrainMaterial();
|
||||
|
||||
bool onAdd();
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT( TerrainMaterial );
|
||||
|
||||
/// This method locates the TerrainMaterial if it exists, tries
|
||||
/// to create a new one if a valid texture path was passed, or
|
||||
/// returns a debug material if all else fails.
|
||||
static TerrainMaterial* findOrCreate( const char *nameOrPath );
|
||||
|
||||
/// Returns the default warning terrain material used when
|
||||
/// a material is not found or defined.
|
||||
static TerrainMaterial* getWarningMaterial();
|
||||
|
||||
const String& getDiffuseMap() const { return mDiffuseMap; }
|
||||
|
||||
F32 getDiffuseSize() const { return mDiffuseSize; }
|
||||
|
||||
const String& getNormalMap() const { return mNormalMap; }
|
||||
|
||||
const String& getDetailMap() const { return mDetailMap; }
|
||||
|
||||
F32 getDetailSize() const { return mDetailSize; }
|
||||
|
||||
F32 getDetailStrength() const { return mDetailStrength; }
|
||||
|
||||
F32 getDetailDistance() const { return mDetailDistance; }
|
||||
|
||||
bool useSideProjection() const { return mSideProjection; }
|
||||
|
||||
F32 getParallaxScale() const { return mParallaxScale; }
|
||||
|
||||
};
|
||||
|
||||
#endif // _TERRMATERIAL_H_
|
||||
493
Engine/source/terrain/terrRender.cpp
Normal file
493
Engine/source/terrain/terrRender.cpp
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "terrain/terrRender.h"
|
||||
|
||||
#include "terrain/terrData.h"
|
||||
#include "terrain/terrCell.h"
|
||||
#include "terrain/terrMaterial.h"
|
||||
#include "terrain/terrCellMaterial.h"
|
||||
#include "materials/shaderData.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "math/util/frustum.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "renderInstance/renderTerrainMgr.h"
|
||||
|
||||
#include "lighting/lightInfo.h"
|
||||
#include "lighting/lightManager.h"
|
||||
|
||||
#include "materials/matInstance.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/matTextureTarget.h"
|
||||
#include "shaderGen/conditionerFeature.h"
|
||||
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "gfx/bitmap/ddsFile.h"
|
||||
#include "gfx/bitmap/ddsUtils.h"
|
||||
#include "terrain/terrMaterial.h"
|
||||
#include "gfx/gfxDebugEvent.h"
|
||||
#include "gfx/gfxCardProfile.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
|
||||
|
||||
bool TerrainBlock::smDebugRender = false;
|
||||
|
||||
|
||||
GFX_ImplementTextureProfile( TerrainLayerTexProfile,
|
||||
GFXTextureProfile::DiffuseMap,
|
||||
GFXTextureProfile::PreserveSize |
|
||||
GFXTextureProfile::Dynamic,
|
||||
GFXTextureProfile::None );
|
||||
|
||||
|
||||
void TerrainBlock::_onFlushMaterials()
|
||||
{
|
||||
if ( mCell )
|
||||
mCell->deleteMaterials();
|
||||
|
||||
SAFE_DELETE( mBaseMaterial );
|
||||
}
|
||||
|
||||
void TerrainBlock::_updateMaterials()
|
||||
{
|
||||
mBaseTextures.setSize( mFile->mMaterials.size() );
|
||||
|
||||
mMaxDetailDistance = 0.0f;
|
||||
|
||||
for ( U32 i=0; i < mFile->mMaterials.size(); i++ )
|
||||
{
|
||||
TerrainMaterial *mat = mFile->mMaterials[i];
|
||||
|
||||
if( !mat->getDiffuseMap().isEmpty() )
|
||||
mBaseTextures[i].set( mat->getDiffuseMap(),
|
||||
&GFXDefaultStaticDiffuseProfile,
|
||||
"TerrainBlock::_updateMaterials() - DiffuseMap" );
|
||||
else
|
||||
mBaseTextures[ i ] = GFXTexHandle();
|
||||
|
||||
// Find the maximum detail distance.
|
||||
if ( mat->getDetailMap().isNotEmpty() &&
|
||||
mat->getDetailDistance() > mMaxDetailDistance )
|
||||
mMaxDetailDistance = mat->getDetailDistance();
|
||||
}
|
||||
|
||||
if ( mCell )
|
||||
mCell->deleteMaterials();
|
||||
}
|
||||
|
||||
void TerrainBlock::_updateLayerTexture()
|
||||
{
|
||||
const U32 layerSize = mFile->mSize;
|
||||
const Vector<U8> &layerMap = mFile->mLayerMap;
|
||||
const U32 pixelCount = layerMap.size();
|
||||
|
||||
if ( mLayerTex.isNull() ||
|
||||
mLayerTex.getWidth() != layerSize ||
|
||||
mLayerTex.getHeight() != layerSize )
|
||||
mLayerTex.set( layerSize, layerSize, GFXFormatR8G8B8A8, &TerrainLayerTexProfile, "" );
|
||||
|
||||
AssertFatal( mLayerTex.getWidth() == layerSize &&
|
||||
mLayerTex.getHeight() == layerSize,
|
||||
"TerrainBlock::_updateLayerTexture - The texture size doesn't match the requested size!" );
|
||||
|
||||
// Update the layer texture.
|
||||
GFXLockedRect *lock = mLayerTex.lock();
|
||||
|
||||
for ( U32 i=0; i < pixelCount; i++ )
|
||||
{
|
||||
lock->bits[0] = layerMap[i];
|
||||
|
||||
if ( i + 1 >= pixelCount )
|
||||
lock->bits[1] = lock->bits[0];
|
||||
else
|
||||
lock->bits[1] = layerMap[i+1];
|
||||
|
||||
if ( i + layerSize >= pixelCount )
|
||||
lock->bits[2] = lock->bits[0];
|
||||
else
|
||||
lock->bits[2] = layerMap[i + layerSize];
|
||||
|
||||
if ( i + layerSize + 1 >= pixelCount )
|
||||
lock->bits[3] = lock->bits[0];
|
||||
else
|
||||
lock->bits[3] = layerMap[i + layerSize + 1];
|
||||
|
||||
lock->bits += 4;
|
||||
}
|
||||
|
||||
mLayerTex.unlock();
|
||||
//mLayerTex->dumpToDisk( "png", "./layerTex.png" );
|
||||
}
|
||||
|
||||
bool TerrainBlock::_initBaseShader()
|
||||
{
|
||||
ShaderData *shaderData = NULL;
|
||||
if ( !Sim::findObject( "TerrainBlendShader", shaderData ) || !shaderData )
|
||||
return false;
|
||||
|
||||
mBaseShader = shaderData->getShader();
|
||||
|
||||
mBaseShaderConsts = mBaseShader->allocConstBuffer();
|
||||
mBaseTexScaleConst = mBaseShader->getShaderConstHandle( "$texScale" );
|
||||
mBaseTexIdConst = mBaseShader->getShaderConstHandle( "$texId" );
|
||||
mBaseLayerSizeConst = mBaseShader->getShaderConstHandle( "$layerSize" );
|
||||
|
||||
mBaseTarget = GFX->allocRenderToTextureTarget();
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.samplersDefined = true;
|
||||
desc.samplers[0] = GFXSamplerStateDesc::getClampPoint();
|
||||
desc.samplers[1] = GFXSamplerStateDesc::getWrapLinear();
|
||||
desc.zDefined = true;
|
||||
desc.zWriteEnable = false;
|
||||
desc.zEnable = false;
|
||||
desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha );
|
||||
desc.cullDefined = true;
|
||||
desc.cullMode = GFXCullNone;
|
||||
mBaseShaderSB = GFX->createStateBlock( desc );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TerrainBlock::_updateBaseTexture( bool writeToCache )
|
||||
{
|
||||
if ( !mBaseShader && !_initBaseShader() )
|
||||
return;
|
||||
|
||||
// This can sometimes occur outside a begin/end scene.
|
||||
const bool sceneBegun = GFX->canCurrentlyRender();
|
||||
if ( !sceneBegun )
|
||||
GFX->beginScene();
|
||||
|
||||
GFXDEBUGEVENT_SCOPE( TerrainBlock_UpdateBaseTexture, ColorI::GREEN );
|
||||
|
||||
PROFILE_SCOPE( TerrainBlock_UpdateBaseTexture );
|
||||
|
||||
GFXTransformSaver saver;
|
||||
|
||||
const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 );
|
||||
|
||||
U32 baseTexSize = getNextPow2( mBaseTexSize );
|
||||
baseTexSize = getMin( maxTextureSize, baseTexSize );
|
||||
Point2I destSize( baseTexSize, baseTexSize );
|
||||
|
||||
// Setup geometry
|
||||
GFXVertexBufferHandle<GFXVertexPT> vb;
|
||||
{
|
||||
F32 copyOffsetX = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.x;
|
||||
F32 copyOffsetY = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.y;
|
||||
|
||||
const bool needsYFlip = GFX->getAdapterType() == OpenGL;
|
||||
|
||||
GFXVertexPT points[4];
|
||||
points[0].point = Point3F( -1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0 );
|
||||
points[0].texCoord = Point2F( 0.0, needsYFlip ? 0.0f : 1.0f );
|
||||
points[1].point = Point3F( -1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0 );
|
||||
points[1].texCoord = Point2F( 0.0, needsYFlip ? 1.0f : 0.0f );
|
||||
points[2].point = Point3F( 1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0 );
|
||||
points[2].texCoord = Point2F( 1.0, needsYFlip ? 1.0f : 0.0f );
|
||||
points[3].point = Point3F( 1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0 );
|
||||
points[3].texCoord = Point2F( 1.0, needsYFlip ? 0.0f : 1.0f );
|
||||
|
||||
vb.set( GFX, 4, GFXBufferTypeVolatile );
|
||||
dMemcpy( vb.lock(), points, sizeof(GFXVertexPT) * 4 );
|
||||
vb.unlock();
|
||||
}
|
||||
|
||||
GFXTexHandle blendTex;
|
||||
|
||||
// If the base texture is already a valid render target then
|
||||
// use it to render to else we create one.
|
||||
if ( mBaseTex.isValid() &&
|
||||
mBaseTex->isRenderTarget() &&
|
||||
mBaseTex->getFormat() == GFXFormatR8G8B8A8 &&
|
||||
mBaseTex->getWidth() == destSize.x &&
|
||||
mBaseTex->getHeight() == destSize.y )
|
||||
blendTex = mBaseTex;
|
||||
else
|
||||
blendTex.set( destSize.x, destSize.y, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, "" );
|
||||
|
||||
GFX->pushActiveRenderTarget();
|
||||
|
||||
// Set our shader stuff
|
||||
GFX->setShader( mBaseShader );
|
||||
GFX->setShaderConstBuffer( mBaseShaderConsts );
|
||||
GFX->setStateBlock( mBaseShaderSB );
|
||||
GFX->setVertexBuffer( vb );
|
||||
|
||||
mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex );
|
||||
GFX->setActiveRenderTarget( mBaseTarget );
|
||||
|
||||
GFX->setTexture( 0, mLayerTex );
|
||||
mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() );
|
||||
|
||||
for ( U32 i=0; i < mBaseTextures.size(); i++ )
|
||||
{
|
||||
GFXTextureObject *tex = mBaseTextures[i];
|
||||
if ( !tex )
|
||||
continue;
|
||||
|
||||
GFX->setTexture( 1, tex );
|
||||
|
||||
F32 baseSize = mFile->mMaterials[i]->getDiffuseSize();
|
||||
F32 scale = 1.0f;
|
||||
if ( !mIsZero( baseSize ) )
|
||||
scale = getWorldBlockSize() / baseSize;
|
||||
|
||||
// A mistake early in development means that texture
|
||||
// coords are not flipped correctly. To compensate
|
||||
// we flip the y scale here.
|
||||
mBaseShaderConsts->setSafe( mBaseTexScaleConst, Point2F( scale, -scale ) );
|
||||
mBaseShaderConsts->setSafe( mBaseTexIdConst, (F32)i );
|
||||
|
||||
GFX->drawPrimitive( GFXTriangleFan, 0, 2 );
|
||||
}
|
||||
|
||||
mBaseTarget->resolve();
|
||||
|
||||
GFX->setShader( NULL );
|
||||
//GFX->setStateBlock( NULL ); // WHY NOT?
|
||||
GFX->setShaderConstBuffer( NULL );
|
||||
GFX->setVertexBuffer( NULL );
|
||||
|
||||
GFX->popActiveRenderTarget();
|
||||
|
||||
// End it if we begun it... Yeehaw!
|
||||
if ( !sceneBegun )
|
||||
GFX->endScene();
|
||||
|
||||
/// Do we cache this sucker?
|
||||
if ( writeToCache )
|
||||
{
|
||||
String cachePath = _getBaseTexCacheFileName();
|
||||
|
||||
FileStream fs;
|
||||
if ( fs.open( _getBaseTexCacheFileName(), Torque::FS::File::Write ) )
|
||||
{
|
||||
// Read back the render target, dxt compress it, and write it to disk.
|
||||
GBitmap blendBmp( destSize.x, destSize.y, false, GFXFormatR8G8B8A8 );
|
||||
blendTex.copyToBmp( &blendBmp );
|
||||
|
||||
/*
|
||||
// Test code for dumping uncompressed bitmap to disk.
|
||||
{
|
||||
FileStream fs;
|
||||
if ( fs.open( "./basetex.png", Torque::FS::File::Write ) )
|
||||
{
|
||||
blendBmp.writeBitmap( "png", fs );
|
||||
fs.close();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
blendBmp.extrudeMipLevels();
|
||||
|
||||
DDSFile *blendDDS = DDSFile::createDDSFileFromGBitmap( &blendBmp );
|
||||
DDSUtil::squishDDS( blendDDS, GFXFormatDXT1 );
|
||||
|
||||
// Write result to file stream
|
||||
blendDDS->write( fs );
|
||||
|
||||
delete blendDDS;
|
||||
}
|
||||
fs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't cache the result, so set the base texture
|
||||
// to the render target we updated. This should be good
|
||||
// for realtime painting cases.
|
||||
mBaseTex = blendTex;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainBlock::_renderBlock( SceneRenderState *state )
|
||||
{
|
||||
PROFILE_SCOPE( TerrainBlock_RenderBlock );
|
||||
|
||||
// Prevent rendering shadows if feature is disabled
|
||||
if ( !mCastShadows && state->isShadowPass() )
|
||||
return;
|
||||
|
||||
MatrixF worldViewXfm = state->getWorldViewMatrix();
|
||||
worldViewXfm.mul( getRenderTransform() );
|
||||
|
||||
MatrixF worldViewProjXfm = state->getProjectionMatrix();
|
||||
worldViewProjXfm.mul( worldViewXfm );
|
||||
|
||||
const MatrixF &objectXfm = getRenderWorldTransform();
|
||||
|
||||
Point3F objCamPos = state->getDiffuseCameraPosition();
|
||||
objectXfm.mulP( objCamPos );
|
||||
|
||||
// Get the shadow material.
|
||||
if ( !mDefaultMatInst )
|
||||
mDefaultMatInst = TerrainCellMaterial::getShadowMat();
|
||||
|
||||
// Make sure we have a base material.
|
||||
if ( !mBaseMaterial )
|
||||
{
|
||||
mBaseMaterial = new TerrainCellMaterial();
|
||||
mBaseMaterial->init( this, 0, false, false, true );
|
||||
}
|
||||
|
||||
// Did the detail layers change?
|
||||
if ( mDetailsDirty )
|
||||
{
|
||||
_updateMaterials();
|
||||
mDetailsDirty = false;
|
||||
}
|
||||
|
||||
// If the layer texture has been cleared or is
|
||||
// dirty then update it.
|
||||
if ( mLayerTex.isNull() || mLayerTexDirty )
|
||||
_updateLayerTexture();
|
||||
|
||||
// If the layer texture is dirty or we lost the base
|
||||
// texture then regenerate it.
|
||||
if ( mLayerTexDirty || mBaseTex.isNull() )
|
||||
{
|
||||
_updateBaseTexture( false );
|
||||
mLayerTexDirty = false;
|
||||
}
|
||||
|
||||
static Vector<TerrCell*> renderCells;
|
||||
renderCells.clear();
|
||||
|
||||
mCell->cullCells( state,
|
||||
objCamPos,
|
||||
&renderCells );
|
||||
|
||||
RenderPassManager *renderPass = state->getRenderPass();
|
||||
|
||||
MatrixF *riObjectToWorldXfm = renderPass->allocUniqueXform( getRenderTransform() );
|
||||
|
||||
const bool isColorDrawPass = state->isDiffusePass() || state->isReflectPass();
|
||||
|
||||
// This is here for shadows mostly... it allows the
|
||||
// proper shadow material to be generated.
|
||||
BaseMatInstance *defaultMatInst = state->getOverrideMaterial( mDefaultMatInst );
|
||||
|
||||
// Only pass and use the light manager if this is not a shadow pass.
|
||||
LightManager *lm = NULL;
|
||||
if ( isColorDrawPass )
|
||||
lm = LIGHTMGR;
|
||||
|
||||
for ( U32 i=0; i < renderCells.size(); i++ )
|
||||
{
|
||||
TerrCell *cell = renderCells[i];
|
||||
|
||||
// Ok this cell is fit to render.
|
||||
TerrainRenderInst *inst = renderPass->allocInst<TerrainRenderInst>();
|
||||
|
||||
// Setup lights for this cell.
|
||||
if ( lm )
|
||||
{
|
||||
SphereF bounds = cell->getSphereBounds();
|
||||
getRenderTransform().mulP( bounds.center );
|
||||
|
||||
LightQuery query;
|
||||
query.init( bounds );
|
||||
query.getLights( inst->lights, 8 );
|
||||
}
|
||||
|
||||
GFXVertexBufferHandleBase vertBuff;
|
||||
GFXPrimitiveBufferHandle primBuff;
|
||||
|
||||
cell->getRenderPrimitive( &inst->prim, &vertBuff, &primBuff );
|
||||
|
||||
inst->mat = defaultMatInst;
|
||||
inst->vertBuff = vertBuff.getPointer();
|
||||
|
||||
if ( primBuff.isValid() )
|
||||
{
|
||||
// Use the cell's custom primitive buffer
|
||||
inst->primBuff = primBuff.getPointer();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the standard primitive buffer for this cell
|
||||
inst->primBuff = mPrimBuffer.getPointer();
|
||||
}
|
||||
|
||||
inst->objectToWorldXfm = riObjectToWorldXfm;
|
||||
|
||||
// If we're not drawing to the shadow map then we need
|
||||
// to include the normal rendering materials.
|
||||
if ( isColorDrawPass )
|
||||
{
|
||||
const SphereF &bounds = cell->getSphereBounds();
|
||||
|
||||
F32 sqDist = ( bounds.center - objCamPos ).lenSquared();
|
||||
|
||||
F32 radiusSq = mSquared( ( mMaxDetailDistance + bounds.radius ) * smDetailScale );
|
||||
|
||||
// If this cell is near enough to get detail textures then
|
||||
// use the full detail mapping material. Else we use the
|
||||
// simple base only material.
|
||||
if ( !state->isReflectPass() && sqDist < radiusSq )
|
||||
inst->cellMat = cell->getMaterial();
|
||||
else if ( state->isReflectPass() )
|
||||
inst->cellMat = mBaseMaterial->getReflectMat();
|
||||
else
|
||||
inst->cellMat = mBaseMaterial;
|
||||
}
|
||||
|
||||
inst->defaultKey = (U32)cell->getMaterials();
|
||||
|
||||
// Submit it for rendering.
|
||||
renderPass->addInst( inst );
|
||||
}
|
||||
|
||||
// Trigger the debug rendering.
|
||||
if ( state->isDiffusePass() &&
|
||||
!renderCells.empty() &&
|
||||
smDebugRender )
|
||||
{
|
||||
// Store the render cells for later.
|
||||
mDebugCells = renderCells;
|
||||
|
||||
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||
ri->renderDelegate.bind( this, &TerrainBlock::_renderDebug );
|
||||
ri->type = RenderPassManager::RIT_Editor;
|
||||
state->getRenderPass()->addInst( ri );
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainBlock::_renderDebug( ObjectRenderInst *ri,
|
||||
SceneRenderState *state,
|
||||
BaseMatInstance *overrideMat )
|
||||
{
|
||||
GFXTransformSaver saver;
|
||||
GFX->multWorld( getRenderTransform() );
|
||||
|
||||
for ( U32 i=0; i < mDebugCells.size(); i++ )
|
||||
mDebugCells[i]->renderBounds();
|
||||
|
||||
mDebugCells.clear();
|
||||
}
|
||||
59
Engine/source/terrain/terrRender.h
Normal file
59
Engine/source/terrain/terrRender.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TERRRENDER_H_
|
||||
#define _TERRRENDER_H_
|
||||
|
||||
#ifndef _TERRDATA_H_
|
||||
#include "terrain/terrData.h"
|
||||
#endif
|
||||
|
||||
enum TerrConstants
|
||||
{
|
||||
MaxClipPlanes = 8, ///< left, right, top, bottom - don't need far tho...
|
||||
//MaxTerrainMaterials = 256,
|
||||
|
||||
MaxTerrainLights = 64,
|
||||
MaxVisibleLights = 31,
|
||||
ClipPlaneMask = (1 << MaxClipPlanes) - 1,
|
||||
FarSphereMask = 0x80000000,
|
||||
FogPlaneBoxMask = 0x40000000,
|
||||
};
|
||||
|
||||
class SceneRenderState;
|
||||
|
||||
|
||||
// Allows a lighting system to plug into terrain rendering
|
||||
class TerrainLightingPlugin
|
||||
{
|
||||
public:
|
||||
virtual ~TerrainLightingPlugin() {}
|
||||
|
||||
virtual void setupLightStage(LightManager * lm, LightInfo* light, SceneData& sgData, BaseMatInstance* basemat, BaseMatInstance** dmat) = 0;
|
||||
virtual void cleanupLights(LightManager * lm) {}
|
||||
};
|
||||
|
||||
|
||||
/// A special texture profile used for the terrain layer id map.
|
||||
GFX_DeclareTextureProfile( TerrainLayerTexProfile );
|
||||
|
||||
#endif // _TERRRENDER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue