2019-12-12 17:32:41 +01:00
// Copyright (C) 2002-2012 Thomas Alten / Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
# include "IrrCompileConfig.h"
# ifdef _IRR_COMPILE_WITH_BSP_LOADER_
# include "CQuake3ShaderSceneNode.h"
# include "ISceneManager.h"
# include "IVideoDriver.h"
# include "ICameraSceneNode.h"
# include "SViewFrustum.h"
# include "IMeshManipulator.h"
# include "SMesh.h"
# include "IMaterialRenderer.h"
# ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
# include "CShadowVolumeSceneNode.h"
# else
# include "IShadowVolumeSceneNode.h"
# endif
namespace irr
{
namespace scene
{
// who, if not you..
using namespace quake3 ;
/*!
*/
CQuake3ShaderSceneNode : : CQuake3ShaderSceneNode (
scene : : ISceneNode * parent , scene : : ISceneManager * mgr , s32 id ,
io : : IFileSystem * fileSystem , const scene : : IMeshBuffer * original ,
const IShader * shader )
: scene : : IMeshSceneNode ( parent , mgr , id ,
core : : vector3df ( 0.f , 0.f , 0.f ) ,
core : : vector3df ( 0.f , 0.f , 0.f ) ,
core : : vector3df ( 1.f , 1.f , 1.f ) ) ,
Shader ( shader ) , Mesh ( 0 ) , Shadow ( 0 ) , Original ( 0 ) , MeshBuffer ( 0 ) , TimeAbs ( 0.f )
{
# ifdef _DEBUG
core : : stringc dName = " CQuake3ShaderSceneNode " ;
dName + = Shader - > name ;
setDebugName ( dName . c_str ( ) ) ;
# endif
// name the Scene Node
this - > Name = Shader - > name ;
// take lightmap vertex type
MeshBuffer = new SMeshBuffer ( ) ;
Mesh = new SMesh ( ) ;
Mesh - > addMeshBuffer ( MeshBuffer ) ;
MeshBuffer - > drop ( ) ;
//Original = new SMeshBufferLightMap();
Original = ( const scene : : SMeshBufferLightMap * ) original ;
Original - > grab ( ) ;
// clone meshbuffer to modifiable buffer
cloneBuffer ( MeshBuffer , Original ,
Original - > getMaterial ( ) . ColorMask ! = 0 ) ;
// load all Textures in all stages
loadTextures ( fileSystem ) ;
setAutomaticCulling ( scene : : EAC_OFF ) ;
}
/*!
*/
CQuake3ShaderSceneNode : : ~ CQuake3ShaderSceneNode ( )
{
if ( Shadow )
Shadow - > drop ( ) ;
if ( Mesh )
Mesh - > drop ( ) ;
if ( Original )
Original - > drop ( ) ;
}
/*
create single copies
*/
void CQuake3ShaderSceneNode : : cloneBuffer ( scene : : SMeshBuffer * dest , const scene : : SMeshBufferLightMap * buffer , bool translateCenter )
{
dest - > Material = buffer - > Material ;
dest - > Indices = buffer - > Indices ;
const u32 vsize = buffer - > Vertices . size ( ) ;
dest - > Vertices . set_used ( vsize ) ;
for ( u32 i = 0 ; i ! = vsize ; + + i )
{
const video : : S3DVertex2TCoords & src = buffer - > Vertices [ i ] ;
video : : S3DVertex & dst = dest - > Vertices [ i ] ;
dst . Pos = src . Pos ;
dst . Normal = src . Normal ;
dst . Color = 0xFFFFFFFF ;
dst . TCoords = src . TCoords ;
if ( i = = 0 )
dest - > BoundingBox . reset ( src . Pos ) ;
else
dest - > BoundingBox . addInternalPoint ( src . Pos ) ;
}
// move the (temp) Mesh to a ScenePosititon
// set Scene Node Position
if ( translateCenter )
{
MeshOffset = dest - > BoundingBox . getCenter ( ) ;
setPosition ( MeshOffset ) ;
core : : matrix4 m ;
m . setTranslation ( - MeshOffset ) ;
SceneManager - > getMeshManipulator ( ) - > transform ( dest , m ) ;
}
// No Texture!. Use Shader-Pointer for sorting
dest - > Material . setTexture ( 0 , ( video : : ITexture * ) Shader ) ;
}
/*
load the textures for all stages
*/
void CQuake3ShaderSceneNode : : loadTextures ( io : : IFileSystem * fileSystem )
{
const SVarGroup * group ;
u32 i ;
video : : IVideoDriver * driver = SceneManager - > getVideoDriver ( ) ;
// generic stage
u32 mipmap = 0 ;
group = Shader - > getGroup ( 1 ) ;
if ( group - > isDefined ( " nomipmaps " ) )
{
mipmap = 2 | ( driver - > getTextureCreationFlag ( video : : ETCF_CREATE_MIP_MAPS ) ? 1 : 0 ) ;
driver - > setTextureCreationFlag ( video : : ETCF_CREATE_MIP_MAPS , false ) ;
}
// clear all stages and prefill empty
Q3Texture . setAllocStrategy ( core : : ALLOC_STRATEGY_SAFE ) ;
Q3Texture . clear ( ) ;
for ( i = 0 ; i ! = Shader - > VarGroup - > VariableGroup . size ( ) ; + + i )
{
Q3Texture . push_back ( SQ3Texture ( ) ) ;
}
u32 pos ;
// get texture map
for ( i = 0 ; i < Shader - > VarGroup - > VariableGroup . size ( ) ; + + i )
{
group = Shader - > getGroup ( i ) ;
const core : : stringc & mapname = group - > get ( " map " ) ;
if ( 0 = = mapname . size ( ) )
continue ;
// our lightmap is passed in material.Texture[2]
if ( mapname = = " $lightmap " )
{
Q3Texture [ i ] . Texture . push_back ( Original - > getMaterial ( ) . getTexture ( 1 ) ) ;
}
else
{
pos = 0 ;
getTextures ( Q3Texture [ i ] . Texture , mapname , pos , fileSystem , driver ) ;
}
}
// get anim map
for ( i = 0 ; i < Shader - > VarGroup - > VariableGroup . size ( ) ; + + i )
{
if ( Q3Texture [ i ] . Texture . size ( ) )
continue ;
group = Shader - > getGroup ( i ) ;
const core : : stringc & animmap = group - > get ( " animmap " ) ;
if ( 0 = = animmap . size ( ) )
continue ;
// first parameter is frequency
pos = 0 ;
Q3Texture [ i ] . TextureFrequency = core : : max_ ( 0.0001f , getAsFloat ( animmap , pos ) ) ;
getTextures ( Q3Texture [ i ] . Texture , animmap , pos , fileSystem , driver ) ;
}
// get clamp map
for ( i = 0 ; i < Shader - > VarGroup - > VariableGroup . size ( ) ; + + i )
{
if ( Q3Texture [ i ] . Texture . size ( ) )
continue ;
group = Shader - > getGroup ( i ) ;
const core : : stringc & clampmap = group - > get ( " clampmap " ) ;
if ( 0 = = clampmap . size ( ) )
continue ;
Q3Texture [ i ] . TextureAddressMode = video : : ETC_CLAMP_TO_EDGE ;
pos = 0 ;
getTextures ( Q3Texture [ i ] . Texture , clampmap , pos , fileSystem , driver ) ;
}
if ( mipmap & 2 )
driver - > setTextureCreationFlag ( video : : ETCF_CREATE_MIP_MAPS , mipmap & 1 ) ;
}
/*
Register each texture stage , if first is visible
*/
void CQuake3ShaderSceneNode : : OnRegisterSceneNode ( )
{
if ( isVisible ( ) )
{
SceneManager - > registerNodeForRendering ( this , getRenderStage ( ) ) ;
}
ISceneNode : : OnRegisterSceneNode ( ) ;
}
/*
is this a transparent node ?
*/
E_SCENE_NODE_RENDER_PASS CQuake3ShaderSceneNode : : getRenderStage ( ) const
{
E_SCENE_NODE_RENDER_PASS ret = ESNRP_SOLID ;
// generic stage
const SVarGroup * group ;
group = Shader - > getGroup ( 1 ) ;
/*
else
if ( group - > getIndex ( " portal " ) > = 0 )
{
ret = ESNRP_TRANSPARENT_EFFECT ;
}
else
*/
if ( group - > isDefined ( " sort " , " opaque " ) )
{
ret = ESNRP_SOLID ;
}
else
if ( group - > isDefined ( " sort " , " additive " ) )
{
ret = ESNRP_TRANSPARENT ;
}
else
if ( strstr ( Shader - > name . c_str ( ) , " flame " ) | |
group - > isDefined ( " surfaceparm " , " water " ) | |
group - > isDefined ( " sort " , " underwater " )
)
{
ret = ESNRP_TRANSPARENT_EFFECT ;
}
else
{
// Look if first drawing stage needs graphical underlay
for ( u32 stage = 2 ; stage < Shader - > VarGroup - > VariableGroup . size ( ) ; + + stage )
{
if ( 0 = = Q3Texture [ stage ] . Texture . size ( ) )
continue ;
group = Shader - > getGroup ( stage ) ;
SBlendFunc blendfunc ( video : : EMFN_MODULATE_1X ) ;
getBlendFunc ( group - > get ( " blendfunc " ) , blendfunc ) ;
getBlendFunc ( group - > get ( " alphafunc " ) , blendfunc ) ;
//ret = blendfunc.isTransparent ? ESNRP_TRANSPARENT : ESNRP_SOLID;
if ( blendfunc . isTransparent )
{
ret = ESNRP_TRANSPARENT ;
}
break ;
}
}
return ret ;
}
/*
render in multipass technique
*/
void CQuake3ShaderSceneNode : : render ( )
{
video : : IVideoDriver * driver = SceneManager - > getVideoDriver ( ) ;
E_SCENE_NODE_RENDER_PASS pass = SceneManager - > getSceneNodeRenderPass ( ) ;
video : : SMaterial material ;
const SVarGroup * group ;
material . Lighting = false ;
material . setTexture ( 1 , 0 ) ;
material . NormalizeNormals = false ;
// generic stage
group = Shader - > getGroup ( 1 ) ;
material . BackfaceCulling = getCullingFunction ( group - > get ( " cull " ) ) ;
u32 pushProjection = 0 ;
core : : matrix4 projection ( core : : matrix4 : : EM4CONST_NOTHING ) ;
// decal ( solve z-fighting )
if ( group - > isDefined ( " polygonoffset " ) )
{
projection = driver - > getTransform ( video : : ETS_PROJECTION ) ;
core : : matrix4 decalProjection ( projection ) ;
/*
f32 n = SceneManager - > getActiveCamera ( ) - > getNearValue ( ) ;
f32 f = SceneManager - > getActiveCamera ( ) - > getFarValue ( ) ;
f32 delta = 0.01f ;
f32 pz = 0.2f ;
f32 epsilon = - 2.f * f * n * delta / ( ( f + n ) * pz * ( pz + delta ) ) ;
decalProjection [ 10 ] * = 1.f + epsilon ;
*/
// TODO: involve camera
decalProjection [ 10 ] - = 0.0002f ;
driver - > setTransform ( video : : ETS_PROJECTION , decalProjection ) ;
pushProjection | = 1 ;
}
driver - > setTransform ( video : : ETS_WORLD , AbsoluteTransformation ) ;
if ( Shadow )
Shadow - > updateShadowVolumes ( ) ;
//! render all stages
u32 drawCount = ( pass = = ESNRP_TRANSPARENT_EFFECT ) ? 1 : 0 ;
core : : matrix4 textureMatrix ( core : : matrix4 : : EM4CONST_NOTHING ) ;
for ( u32 stage = 1 ; stage < Shader - > VarGroup - > VariableGroup . size ( ) ; + + stage )
{
SQ3Texture & q = Q3Texture [ stage ] ;
// advance current stage
textureMatrix . makeIdentity ( ) ;
animate ( stage , textureMatrix ) ;
// stage finished, no drawing stage ( vertex transform only )
video : : ITexture * tex = q . Texture . size ( ) ? q . Texture [ q . TextureIndex ] : 0 ;
if ( 0 = = tex )
continue ;
// current stage
group = Shader - > getGroup ( stage ) ;
material . setTexture ( 0 , tex ) ;
material . ZBuffer = getDepthFunction ( group - > get ( " depthfunc " ) ) ;
2020-01-02 16:34:52 +01:00
// TODO: maybe should be video::EZW_ON instead of EZW_AUTO now (we didn't have that before and I just kept old values here when introducing it to not break anything)
2019-12-12 17:32:41 +01:00
if ( group - > isDefined ( " depthwrite " ) )
{
2020-01-02 16:34:52 +01:00
material . ZWriteEnable = video : : EZW_AUTO ;
2019-12-12 17:32:41 +01:00
}
else
{
2020-01-02 16:34:52 +01:00
material . ZWriteEnable = drawCount = = 0 ? video : : EZW_AUTO : video : : EZW_OFF ;
2019-12-12 17:32:41 +01:00
}
//resolve quake3 blendfunction to irrlicht Material Type
SBlendFunc blendfunc ( video : : EMFN_MODULATE_1X ) ;
getBlendFunc ( group - > get ( " blendfunc " ) , blendfunc ) ;
getBlendFunc ( group - > get ( " alphafunc " ) , blendfunc ) ;
material . MaterialType = blendfunc . type ;
material . MaterialTypeParam = blendfunc . param0 ;
material . TextureLayer [ 0 ] . TextureWrapU = q . TextureAddressMode ;
material . TextureLayer [ 0 ] . TextureWrapV = q . TextureAddressMode ;
material . TextureLayer [ 0 ] . TextureWrapW = q . TextureAddressMode ;
//material.TextureLayer[0].TrilinearFilter = 1;
//material.TextureLayer[0].AnisotropicFilter = 0xFF;
material . setTextureMatrix ( 0 , textureMatrix ) ;
driver - > setMaterial ( material ) ;
driver - > drawMeshBuffer ( MeshBuffer ) ;
drawCount + = 1 ;
}
if ( DebugDataVisible & scene : : EDS_MESH_WIRE_OVERLAY )
{
video : : SMaterial deb_m ;
deb_m . Wireframe = true ;
deb_m . Lighting = false ;
deb_m . BackfaceCulling = material . BackfaceCulling ;
driver - > setMaterial ( deb_m ) ;
driver - > drawMeshBuffer ( MeshBuffer ) ;
}
// show normals
if ( DebugDataVisible & scene : : EDS_NORMALS )
{
video : : SMaterial deb_m ;
IAnimatedMesh * arrow = SceneManager - > addArrowMesh (
" __debugnormalq3 " ,
0xFFECEC00 , 0xFF999900 ,
4 , 8 ,
8.f , 6.f ,
0.5f , 1.f
) ;
if ( 0 = = arrow )
{
arrow = SceneManager - > getMesh ( " __debugnormalq3 " ) ;
}
const IMesh * mesh = arrow - > getMesh ( 0 ) ;
// find a good scaling factor
core : : matrix4 m2 ;
// draw normals
const scene : : IMeshBuffer * mb = MeshBuffer ;
const u32 vSize = video : : getVertexPitchFromType ( mb - > getVertexType ( ) ) ;
const video : : S3DVertex * v = ( const video : : S3DVertex * ) mb - > getVertices ( ) ;
//f32 colCycle = 270.f / (f32) core::s32_max ( mb->getVertexCount() - 1, 1 );
for ( u32 i = 0 ; i ! = mb - > getVertexCount ( ) ; + + i )
{
// Align to v->normal
m2 . buildRotateFromTo ( core : : vector3df ( 0.f , 1.f , 0 ) , v - > Normal ) ;
m2 . setTranslation ( v - > Pos + AbsoluteTransformation . getTranslation ( ) ) ;
/*
core : : quaternion quatRot ( v - > Normal . Z , 0.f , - v - > Normal . X , 1 + v - > Normal . Y ) ;
quatRot . normalize ( ) ;
quatRot . getMatrix ( m2 , v - > Pos ) ;
m2 [ 12 ] + = AbsoluteTransformation [ 12 ] ;
m2 [ 13 ] + = AbsoluteTransformation [ 13 ] ;
m2 [ 14 ] + = AbsoluteTransformation [ 14 ] ;
*/
driver - > setTransform ( video : : ETS_WORLD , m2 ) ;
deb_m . Lighting = true ;
/*
irr : : video : : SColorHSL color ;
irr : : video : : SColor rgb ( 0 ) ;
color . Hue = i * colCycle * core : : DEGTORAD ;
color . Saturation = 1.f ;
color . Luminance = 0.5f ;
color . toRGB ( deb_m . EmissiveColor ) ;
*/
switch ( i )
{
case 0 : deb_m . EmissiveColor . set ( 0xFFFFFFFF ) ; break ;
case 1 : deb_m . EmissiveColor . set ( 0xFFFF0000 ) ; break ;
case 2 : deb_m . EmissiveColor . set ( 0xFF00FF00 ) ; break ;
case 3 : deb_m . EmissiveColor . set ( 0xFF0000FF ) ; break ;
default :
deb_m . EmissiveColor = v - > Color ; break ;
}
driver - > setMaterial ( deb_m ) ;
for ( u32 a = 0 ; a ! = mesh - > getMeshBufferCount ( ) ; + + a )
driver - > drawMeshBuffer ( mesh - > getMeshBuffer ( a ) ) ;
v = ( const video : : S3DVertex * ) ( ( u8 * ) v + vSize ) ;
}
driver - > setTransform ( video : : ETS_WORLD , AbsoluteTransformation ) ;
}
if ( pushProjection & 1 )
{
driver - > setTransform ( video : : ETS_PROJECTION , projection ) ;
}
if ( DebugDataVisible & scene : : EDS_BBOX )
{
video : : SMaterial deb_m ;
deb_m . Lighting = false ;
driver - > setMaterial ( deb_m ) ;
driver - > setTransform ( video : : ETS_WORLD , AbsoluteTransformation ) ;
driver - > draw3DBox ( getBoundingBox ( ) , video : : SColor ( 255 , 255 , 0 , 0 ) ) ;
}
}
//! Removes a child from this scene node.
//! Implemented here, to be able to remove the shadow properly, if there is one,
//! or to remove attached childs.
bool CQuake3ShaderSceneNode : : removeChild ( ISceneNode * child )
{
if ( child & & Shadow = = child )
{
Shadow - > drop ( ) ;
Shadow = 0 ;
}
return ISceneNode : : removeChild ( child ) ;
}
//! Creates shadow volume scene node as child of this node
//! and returns a pointer to it.
IShadowVolumeSceneNode * CQuake3ShaderSceneNode : : addShadowVolumeSceneNode (
const IMesh * shadowMesh , s32 id , bool zfailmethod , f32 infinity )
{
# ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
if ( ! SceneManager - > getVideoDriver ( ) - > queryFeature ( video : : EVDF_STENCIL_BUFFER ) )
return 0 ;
if ( ! shadowMesh )
shadowMesh = Mesh ; // if null is given, use the mesh of node
if ( Shadow )
Shadow - > drop ( ) ;
Shadow = new CShadowVolumeSceneNode ( shadowMesh , this , SceneManager , id , zfailmethod , infinity ) ;
return Shadow ;
# else
return 0 ;
# endif
}
/*!
3.3 .1 deformVertexes wave < div > < func > < base > < amplitude > < phase > < freq >
Designed for water surfaces , modifying the values differently at each point .
It accepts the standard wave functions of the type sin , triangle , square , sawtooth
or inversesawtooth . The " div " parameter is used to control the wave " spread "
- a value equal to the tessSize of the surface is a good default value
( tessSize is subdivision size , in game units , used for the shader when seen in the game world ) .
*/
void CQuake3ShaderSceneNode : : deformvertexes_wave ( f32 dt , SModifierFunction & function )
{
function . wave = core : : reciprocal ( function . wave ) ;
const f32 phase = function . phase ;
const u32 vsize = Original - > Vertices . size ( ) ;
for ( u32 i = 0 ; i ! = vsize ; + + i )
{
const video : : S3DVertex2TCoords & src = Original - > Vertices [ i ] ;
video : : S3DVertex & dst = MeshBuffer - > Vertices [ i ] ;
if ( 0 = = function . count )
dst . Pos = src . Pos - MeshOffset ;
const f32 wavephase = ( dst . Pos . X + dst . Pos . Y + dst . Pos . Z ) * function . wave ;
function . phase = phase + wavephase ;
const f32 f = function . evaluate ( dt ) ;
dst . Pos . X + = f * src . Normal . X ;
dst . Pos . Y + = f * src . Normal . Y ;
dst . Pos . Z + = f * src . Normal . Z ;
if ( i = = 0 )
MeshBuffer - > BoundingBox . reset ( dst . Pos ) ;
else
MeshBuffer - > BoundingBox . addInternalPoint ( dst . Pos ) ;
}
function . count = 1 ;
}
/*!
deformVertexes move x y z func base amplitude phase freq
The move parameter is used to make a brush , curve patch or model
appear to move together as a unit . The x y z values are the distance
and direction in game units the object appears to move relative to
it ' s point of origin in the map . The func base amplitude phase freq values are
the same as found in other waveform manipulations .
The product of the function modifies the values x , y , and z .
Therefore , if you have an amplitude of 5 and an x value of 2 ,
the object will travel 10 units from its point of origin along the x axis .
This results in a total of 20 units of motion along the x axis , since the
amplitude is the variation both above and below the base .
It must be noted that an object made with this shader does not actually
change position , it only appears to .
Design Notes :
If an object is made up of surfaces with different shaders , all must have
matching deformVertexes move values or the object will appear to tear itself apart .
*/
void CQuake3ShaderSceneNode : : deformvertexes_move ( f32 dt , SModifierFunction & function )
{
function . wave = core : : reciprocal ( function . wave ) ;
const f32 f = function . evaluate ( dt ) ;
const u32 vsize = Original - > Vertices . size ( ) ;
for ( u32 i = 0 ; i ! = vsize ; + + i )
{
const video : : S3DVertex2TCoords & src = Original - > Vertices [ i ] ;
video : : S3DVertex & dst = MeshBuffer - > Vertices [ i ] ;
if ( 0 = = function . count )
dst . Pos = src . Pos - MeshOffset ;
dst . Pos . X + = f * function . x ;
dst . Pos . Y + = f * function . y ;
dst . Pos . Z + = f * function . z ;
if ( i = = 0 )
MeshBuffer - > BoundingBox . reset ( dst . Pos ) ;
else
MeshBuffer - > BoundingBox . addInternalPoint ( dst . Pos ) ;
}
function . count = 1 ;
}
/*!
3.3 .2 deformVertexes normal < div > < func > < base > < amplitude ~ 0.1 - ~ 0.5 > < frequency ~ 1.0 - ~ 4.0 >
This deformation affects the normals of a vertex without actually moving it ,
which will effect later shader options like lighting and especially environment mapping .
If the shader stages don ' t use normals in any of their calculations , there will
be no visible effect .
Design Notes : Putting values of 0.1 t o 0.5 in Amplitude and 1.0 to 4.0 in the
Frequency can produce some satisfying results . Some things that have been
done with it : A small fluttering bat , falling leaves , rain , flags .
*/
void CQuake3ShaderSceneNode : : deformvertexes_normal ( f32 dt , SModifierFunction & function )
{
function . func = SINUS ;
const u32 vsize = Original - > Vertices . size ( ) ;
for ( u32 i = 0 ; i ! = vsize ; + + i )
{
const video : : S3DVertex2TCoords & src = Original - > Vertices [ i ] ;
video : : S3DVertex & dst = MeshBuffer - > Vertices [ i ] ;
function . base = atan2f ( src . Pos . X , src . Pos . Y ) ;
function . phase = src . Pos . X + src . Pos . Z ;
const f32 lat = function . evaluate ( dt ) ;
function . base = src . Normal . Y ;
function . phase = src . Normal . Z + src . Normal . X ;
const f32 lng = function . evaluate ( dt ) ;
dst . Normal . X = cosf ( lat ) * sinf ( lng ) ;
dst . Normal . Y = sinf ( lat ) * sinf ( lng ) ;
dst . Normal . Z = cosf ( lng ) ;
}
}
/*!
3.3 .3 deformVertexes bulge < bulgeWidth > < bulgeHeight > < bulgeSpeed >
This forces a bulge to move along the given s and t directions . Designed for use
on curved pipes .
Specific parameter definitions for deform keywords :
< div > This is roughly defined as the size of the waves that occur .
It is measured in game units . Smaller values create a greater
density of smaller wave forms occurring in a given area .
Larger values create a lesser density of waves , or otherwise put ,
the appearance of larger waves . To look correct this value should
closely correspond to the value ( in pixels ) set for tessSize ( tessellation size )
of the texture . A value of 100.0 is a good default value
( which means your tessSize should be close to that for things to look " wavelike " ) .
< func > This is the type of wave form being created . Sin stands for sine wave ,
a regular smoothly flowing wave . Triangle is a wave with a sharp ascent
and a sharp decay . It will make a choppy looking wave forms .
A square wave is simply on or off for the period of the
frequency with no in between . The sawtooth wave has the ascent of a
triangle wave , but has the decay cut off sharply like a square wave .
An inversesawtooth wave reverses this .
< base > This is the distance , in game units that the apparent surface of the
texture is displaced from the actual surface of the brush as placed
in the editor . A positive value appears above the brush surface .
A negative value appears below the brush surface .
An example of this is the Quad effect , which essentially is a
shell with a positive base value to stand it away from the model
surface and a 0 ( zero ) value for amplitude .
< amplitude > The distance that the deformation moves away from the base value .
See Wave Forms in the introduction for a description of amplitude .
< phase > See Wave Forms in the introduction for a description of phase )
< frequency > See Wave Forms in the introduction for a description of frequency )
Design Note : The div and amplitude parameters , when used in conjunction with
liquid volumes like water should take into consideration how much the water
will be moving . A large ocean area would have have massive swells ( big div values )
that rose and fell dramatically ( big amplitude values ) . While a small , quiet pool
may move very little .
*/
void CQuake3ShaderSceneNode : : deformvertexes_bulge ( f32 dt , SModifierFunction & function )
{
function . func = SINUS ;
function . wave = core : : reciprocal ( function . bulgewidth ) ;
dt * = function . bulgespeed * 0.1f ;
const f32 phase = function . phase ;
const u32 vsize = Original - > Vertices . size ( ) ;
for ( u32 i = 0 ; i ! = vsize ; + + i )
{
const video : : S3DVertex2TCoords & src = Original - > Vertices [ i ] ;
video : : S3DVertex & dst = MeshBuffer - > Vertices [ i ] ;
const f32 wavephase = ( Original - > Vertices [ i ] . TCoords . X ) * function . wave ;
function . phase = phase + wavephase ;
const f32 f = function . evaluate ( dt ) ;
if ( 0 = = function . count )
dst . Pos = src . Pos - MeshOffset ;
dst . Pos . X + = f * src . Normal . X ;
dst . Pos . Y + = f * src . Normal . Y ;
dst . Pos . Z + = f * src . Normal . Z ;
if ( i = = 0 )
MeshBuffer - > BoundingBox . reset ( dst . Pos ) ;
else
MeshBuffer - > BoundingBox . addInternalPoint ( dst . Pos ) ;
}
function . count = 1 ;
}
/*!
deformVertexes autosprite
This function can be used to make any given triangle quad
( pair of triangles that form a square rectangle ) automatically behave
like a sprite without having to make it a separate entity . This means
that the " sprite " on which the texture is placed will rotate to always
appear at right angles to the player ' s view as a sprite would . Any four - sided
brush side , flat patch , or pair of triangles in a model can have the autosprite
effect on it . The brush face containing a texture with this shader keyword must
be square .
*/
void CQuake3ShaderSceneNode : : deformvertexes_autosprite ( f32 dt , SModifierFunction & function )
{
u32 vsize = Original - > Vertices . size ( ) ;
u32 g ;
u32 i ;
const core : : vector3df & camPos = SceneManager - > getActiveCamera ( ) - > getPosition ( ) ;
video : : S3DVertex * dv = MeshBuffer - > Vertices . pointer ( ) ;
const video : : S3DVertex2TCoords * vin = Original - > Vertices . const_pointer ( ) ;
core : : matrix4 lookat ( core : : matrix4 : : EM4CONST_NOTHING ) ;
core : : quaternion q ;
for ( i = 0 ; i < vsize ; i + = 4 )
{
// quad-plane
core : : vector3df center = 0.25f * ( vin [ i + 0 ] . Pos + vin [ i + 1 ] . Pos + vin [ i + 2 ] . Pos + vin [ i + 3 ] . Pos ) ;
core : : vector3df forward = camPos - center ;
q . rotationFromTo ( vin [ i ] . Normal , forward ) ;
q . getMatrixCenter ( lookat , center , MeshOffset ) ;
for ( g = 0 ; g < 4 ; + + g )
{
lookat . transformVect ( dv [ i + g ] . Pos , vin [ i + g ] . Pos ) ;
lookat . rotateVect ( dv [ i + g ] . Normal , vin [ i + g ] . Normal ) ;
}
}
function . count = 1 ;
}
/*!
deformVertexes autosprite2
Is a slightly modified " sprite " that only rotates around the middle of its longest axis .
This allows you to make a pillar of fire that you can walk around , or an energy beam
stretched across the room .
*/
struct sortaxis
{
core : : vector3df v ;
bool operator < ( const sortaxis & other ) const
{
return v . getLengthSQ ( ) < other . v . getLengthSQ ( ) ;
}
} ;
/*!
*/
void CQuake3ShaderSceneNode : : deformvertexes_autosprite2 ( f32 dt , SModifierFunction & function )
{
u32 vsize = Original - > Vertices . size ( ) ;
u32 g ;
u32 i ;
const core : : vector3df camPos = SceneManager - > getActiveCamera ( ) - > getAbsolutePosition ( ) ;
video : : S3DVertex * dv = MeshBuffer - > Vertices . pointer ( ) ;
const video : : S3DVertex2TCoords * vin = Original - > Vertices . const_pointer ( ) ;
core : : matrix4 lookat ( core : : matrix4 : : EM4CONST_NOTHING ) ;
core : : array < sortaxis > axis ;
axis . set_used ( 3 ) ;
for ( i = 0 ; i < vsize ; i + = 4 )
{
// quad-plane
core : : vector3df center = 0.25f * ( vin [ i + 0 ] . Pos + vin [ i + 1 ] . Pos + vin [ i + 2 ] . Pos + vin [ i + 3 ] . Pos ) ;
// longes axe
axis [ 0 ] . v = vin [ i + 1 ] . Pos - vin [ i + 0 ] . Pos ;
axis [ 1 ] . v = vin [ i + 2 ] . Pos - vin [ i + 0 ] . Pos ;
axis [ 2 ] . v = vin [ i + 3 ] . Pos - vin [ i + 0 ] . Pos ;
axis . set_sorted ( false ) ;
axis . sort ( ) ;
lookat . buildAxisAlignedBillboard ( camPos , center , MeshOffset , axis [ 1 ] . v , vin [ i + 0 ] . Normal ) ;
for ( g = 0 ; g < 4 ; + + g )
{
lookat . transformVect ( dv [ i + g ] . Pos , vin [ i + g ] . Pos ) ;
lookat . rotateVect ( dv [ i + g ] . Normal , vin [ i + g ] . Normal ) ;
}
}
function . count = 1 ;
}
/*
Generate Vertex Color
*/
void CQuake3ShaderSceneNode : : vertextransform_rgbgen ( f32 dt , SModifierFunction & function )
{
u32 i ;
const u32 vsize = Original - > Vertices . size ( ) ;
switch ( function . rgbgen )
{
case IDENTITY :
//rgbgen identity
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . set ( 0xFFFFFFFF ) ;
break ;
case IDENTITYLIGHTING :
// rgbgen identitylighting TODO: overbright
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . set ( 0xFF7F7F7F ) ;
break ;
case EXACTVERTEX :
// alphagen exactvertex TODO lighting
case VERTEX :
// rgbgen vertex
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color = Original - > Vertices [ i ] . Color ;
break ;
case WAVE :
{
// rgbGen wave <func> <base> <amp> <phase> <freq>
f32 f = function . evaluate ( dt ) * 255.f ;
s32 value = core : : clamp ( core : : floor32 ( f ) , 0 , 255 ) ;
value = 0xFF000000 | value < < 16 | value < < 8 | value ;
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . set ( value ) ;
} break ;
case CONSTANT :
{
//rgbgen const ( x y z )
video : : SColorf cf ( function . x , function . y , function . z ) ;
video : : SColor col = cf . toSColor ( ) ;
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color = col ;
} break ;
default :
break ;
}
}
/*
Generate Vertex Color , Alpha
*/
void CQuake3ShaderSceneNode : : vertextransform_alphagen ( f32 dt , SModifierFunction & function )
{
u32 i ;
const u32 vsize = Original - > Vertices . size ( ) ;
switch ( function . alphagen )
{
case IDENTITY :
//alphagen identity
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . setAlpha ( 0xFF ) ;
break ;
case EXACTVERTEX :
// alphagen exactvertex TODO lighting
case VERTEX :
// alphagen vertex
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . setAlpha ( Original - > Vertices [ i ] . Color . getAlpha ( ) ) ;
break ;
case CONSTANT :
{
// alphagen const
u32 a = ( u32 ) ( function . x * 255.f ) ;
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . setAlpha ( a ) ;
} break ;
case LIGHTINGSPECULAR :
{
// alphagen lightingspecular TODO!!!
const SViewFrustum * frustum = SceneManager - > getActiveCamera ( ) - > getViewFrustum ( ) ;
const core : : matrix4 & view = frustum - > getTransform ( video : : ETS_VIEW ) ;
const f32 * m = view . pointer ( ) ;
for ( i = 0 ; i ! = vsize ; + + i )
{
const core : : vector3df & n = Original - > Vertices [ i ] . Normal ;
MeshBuffer - > Vertices [ i ] . Color . setAlpha ( ( u32 ) ( 128.f * ( 1.f + ( n . X * m [ 0 ] + n . Y * m [ 1 ] + n . Z * m [ 2 ] ) ) ) ) ;
}
} break ;
case WAVE :
{
// alphagen wave
f32 f = function . evaluate ( dt ) * 255.f ;
s32 value = core : : clamp ( core : : floor32 ( f ) , 0 , 255 ) ;
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . Color . setAlpha ( value ) ;
} break ;
default :
break ;
}
}
/*
Generate Texture Coordinates
*/
void CQuake3ShaderSceneNode : : vertextransform_tcgen ( f32 dt , SModifierFunction & function )
{
u32 i ;
const u32 vsize = Original - > Vertices . size ( ) ;
switch ( function . tcgen )
{
case TURBULENCE :
//tcgen turb
{
function . wave = core : : reciprocal ( function . phase ) ;
const f32 phase = function . phase ;
for ( i = 0 ; i ! = vsize ; + + i )
{
const video : : S3DVertex2TCoords & src = Original - > Vertices [ i ] ;
video : : S3DVertex & dst = MeshBuffer - > Vertices [ i ] ;
const f32 wavephase = ( src . Pos . X + src . Pos . Y + src . Pos . Z ) * function . wave ;
function . phase = phase + wavephase ;
const f32 f = function . evaluate ( dt ) ;
dst . TCoords . X = src . TCoords . X + f * src . Normal . X ;
dst . TCoords . Y = src . TCoords . Y + f * src . Normal . Y ;
}
}
break ;
case TEXTURE :
// tcgen texture
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . TCoords = Original - > Vertices [ i ] . TCoords ;
break ;
case LIGHTMAP :
// tcgen lightmap
for ( i = 0 ; i ! = vsize ; + + i )
MeshBuffer - > Vertices [ i ] . TCoords = Original - > Vertices [ i ] . TCoords2 ;
break ;
case ENVIRONMENT :
{
// tcgen environment
const SViewFrustum * frustum = SceneManager - > getActiveCamera ( ) - > getViewFrustum ( ) ;
const core : : matrix4 & view = frustum - > getTransform ( video : : ETS_VIEW ) ;
const f32 * m = view . pointer ( ) ;
core : : vector3df n ;
for ( i = 0 ; i ! = vsize ; + + i )
{
//const core::vector3df &n = Original->Vertices[i].Normal;
n = frustum - > cameraPosition - Original - > Vertices [ i ] . Pos ;
n . normalize ( ) ;
n + = Original - > Vertices [ i ] . Normal ;
n . normalize ( ) ;
MeshBuffer - > Vertices [ i ] . TCoords . X = 0.5f * ( 1.f + ( n . X * m [ 0 ] + n . Y * m [ 1 ] + n . Z * m [ 2 ] ) ) ;
MeshBuffer - > Vertices [ i ] . TCoords . Y = 0.5f * ( 1.f + ( n . X * m [ 4 ] + n . Y * m [ 5 ] + n . Z * m [ 6 ] ) ) ;
}
} break ;
default :
break ;
}
}
#if 0
/*
Transform Texture Coordinates
*/
void CQuake3ShaderSceneNode : : transformtex ( const core : : matrix4 & m , const u32 addressMode )
{
u32 i ;
const u32 vsize = MeshBuffer - > Vertices . size ( ) ;
f32 tx1 ;
f32 ty1 ;
if ( addressMode )
{
for ( i = 0 ; i ! = vsize ; + + i )
{
core : : vector2df & tx = MeshBuffer - > Vertices [ i ] . TCoords ;
tx1 = m [ 0 ] * tx . X + m [ 4 ] * tx . Y + m [ 8 ] ;
ty1 = m [ 1 ] * tx . X + m [ 5 ] * tx . Y + m [ 9 ] ;
tx . X = tx1 ;
tx . Y = ty1 ;
}
}
else
{
for ( i = 0 ; i ! = vsize ; + + i )
{
core : : vector2df & tx = MeshBuffer - > Vertices [ i ] . TCoords ;
tx1 = m [ 0 ] * tx . X + m [ 4 ] * tx . Y + m [ 8 ] ;
ty1 = m [ 1 ] * tx . X + m [ 5 ] * tx . Y + m [ 9 ] ;
tx . X = tx1 < = 0.f ? 0.f : tx1 > = 1.f ? 1.f : tx1 ;
tx . Y = ty1 < = 0.f ? 0.f : ty1 > = 1.f ? 1.f : ty1 ;
//tx.X = core::clamp( tx1, 0.f, 1.f );
//tx.Y = core::clamp( ty1, 0.f, 1.f );
}
}
}
# endif
/*
Texture & Vertex Transform Animator
Return a Texture Transformation for this stage
Vertex transformation are called if found
*/
void CQuake3ShaderSceneNode : : animate ( u32 stage , core : : matrix4 & texture )
{
const SVarGroup * group = Shader - > getGroup ( stage ) ;
// select current texture
SQ3Texture & q3Tex = Q3Texture [ stage ] ;
if ( q3Tex . TextureFrequency ! = 0.f )
{
s32 v = core : : floor32 ( TimeAbs * q3Tex . TextureFrequency ) ;
q3Tex . TextureIndex = v % q3Tex . Texture . size ( ) ;
}
core : : matrix4 m2 ;
SModifierFunction function ;
f32 f [ 16 ] ;
// walk group for all modifiers
for ( u32 g = 0 ; g ! = group - > Variable . size ( ) ; + + g )
{
const SVariable & v = group - > Variable [ g ] ;
// get the modifier
static const c8 * const modifierList [ ] =
{
" tcmod " , " deformvertexes " , " rgbgen " , " tcgen " , " map " , " alphagen "
} ;
u32 pos = 0 ;
function . masterfunc0 = ( eQ3ModifierFunction ) isEqual ( v . name , pos , modifierList , 6 ) ;
if ( UNKNOWN = = function . masterfunc0 )
continue ;
switch ( function . masterfunc0 )
{
//tcmod
case TCMOD :
m2 . makeIdentity ( ) ;
break ;
default :
break ;
}
// get the modifier function
static const c8 * const funclist [ ] =
{
" scroll " , " scale " , " rotate " , " stretch " , " turb " ,
" wave " , " identity " , " vertex " ,
" texture " , " lightmap " , " environment " , " $lightmap " ,
" bulge " , " autosprite " , " autosprite2 " , " transform " ,
" exactvertex " , " const " , " lightingspecular " , " move " , " normal " ,
" identitylighting "
} ;
static const c8 * const groupToken [ ] = { " ( " , " ) " } ;
pos = 0 ;
function . masterfunc1 = ( eQ3ModifierFunction ) isEqual ( v . content , pos , funclist , 22 ) ;
if ( function . masterfunc1 ! = UNKNOWN )
function . masterfunc1 = ( eQ3ModifierFunction ) ( ( u32 ) function . masterfunc1 + FUNCTION2 + 1 ) ;
switch ( function . masterfunc1 )
{
case SCROLL :
// tcMod scroll <sSpeed> <tSpeed>
f [ 0 ] = getAsFloat ( v . content , pos ) * TimeAbs ;
f [ 1 ] = getAsFloat ( v . content , pos ) * TimeAbs ;
m2 . setTextureTranslate ( f [ 0 ] , f [ 1 ] ) ;
break ;
case SCALE :
// tcmod scale <sScale> <tScale>
f [ 0 ] = getAsFloat ( v . content , pos ) ;
f [ 1 ] = getAsFloat ( v . content , pos ) ;
m2 . setTextureScale ( f [ 0 ] , f [ 1 ] ) ;
break ;
case ROTATE :
// tcmod rotate <degrees per second>
m2 . setTextureRotationCenter ( getAsFloat ( v . content , pos ) *
core : : DEGTORAD *
TimeAbs
) ;
break ;
case TRANSFORM :
// tcMod <transform> <m00> <m01> <m10> <m11> <t0> <t1>
memset ( f , 0 , sizeof ( f ) ) ;
f [ 10 ] = f [ 15 ] = 1.f ;
f [ 0 ] = getAsFloat ( v . content , pos ) ;
f [ 1 ] = getAsFloat ( v . content , pos ) ;
f [ 4 ] = getAsFloat ( v . content , pos ) ;
f [ 5 ] = getAsFloat ( v . content , pos ) ;
f [ 8 ] = getAsFloat ( v . content , pos ) ;
f [ 9 ] = getAsFloat ( v . content , pos ) ;
m2 . setM ( f ) ;
break ;
case STRETCH : // stretch
case TURBULENCE : // turb
case WAVE : // wave
case IDENTITY : // identity
case IDENTITYLIGHTING :
case VERTEX : // vertex
case MOVE :
case CONSTANT :
{
// turb == sin, default == sin
function . func = SINUS ;
if ( function . masterfunc0 = = DEFORMVERTEXES )
{
switch ( function . masterfunc1 )
{
case WAVE :
// deformvertexes wave
function . wave = getAsFloat ( v . content , pos ) ;
break ;
case MOVE :
//deformvertexes move
function . x = getAsFloat ( v . content , pos ) ;
function . z = getAsFloat ( v . content , pos ) ;
function . y = getAsFloat ( v . content , pos ) ;
break ;
default :
break ;
}
}
switch ( function . masterfunc1 )
{
case STRETCH :
case TURBULENCE :
case WAVE :
case MOVE :
getModifierFunc ( function , v . content , pos ) ;
break ;
default :
break ;
}
switch ( function . masterfunc1 )
{
case STRETCH :
//tcMod stretch <func> <base> <amplitude> <phase> <frequency>
f [ 0 ] = core : : reciprocal ( function . evaluate ( TimeAbs ) ) ;
m2 . setTextureScaleCenter ( f [ 0 ] , f [ 0 ] ) ;
break ;
case TURBULENCE :
//tcMod turb <base> <amplitude> <phase> <freq>
//function.tcgen = TURBULENCE;
m2 . setTextureRotationCenter ( function . frequency *
core : : DEGTORAD *
TimeAbs
) ;
break ;
case WAVE :
case IDENTITY :
case IDENTITYLIGHTING :
case VERTEX :
case EXACTVERTEX :
case CONSTANT :
case LIGHTINGSPECULAR :
case MOVE :
switch ( function . masterfunc0 )
{
case DEFORMVERTEXES :
switch ( function . masterfunc1 )
{
case WAVE :
deformvertexes_wave ( TimeAbs , function ) ;
break ;
case MOVE :
deformvertexes_move ( TimeAbs , function ) ;
break ;
default :
break ;
}
break ;
case RGBGEN :
function . rgbgen = function . masterfunc1 ;
if ( function . rgbgen = = CONSTANT )
{
isEqual ( v . content , pos , groupToken , 2 ) ;
function . x = getAsFloat ( v . content , pos ) ;
function . y = getAsFloat ( v . content , pos ) ;
function . z = getAsFloat ( v . content , pos ) ;
}
//vertextransform_rgbgen( TimeAbs, function );
break ;
case ALPHAGEN :
function . alphagen = function . masterfunc1 ;
if ( function . alphagen = = CONSTANT )
{
function . x = getAsFloat ( v . content , pos ) ;
}
//vertextransform_alphagen( TimeAbs, function );
break ;
default :
break ;
}
break ;
default :
break ;
}
} break ;
case TEXTURE :
case LIGHTMAP :
case ENVIRONMENT :
// "texture","lightmap","environment"
function . tcgen = function . masterfunc1 ;
break ;
case DOLLAR_LIGHTMAP :
// map == lightmap, tcgen == lightmap
function . tcgen = LIGHTMAP ;
break ;
case BULGE :
// deformvertexes bulge
function . bulgewidth = getAsFloat ( v . content , pos ) ;
function . bulgeheight = getAsFloat ( v . content , pos ) ;
function . bulgespeed = getAsFloat ( v . content , pos ) ;
deformvertexes_bulge ( TimeAbs , function ) ;
break ;
case NORMAL :
// deformvertexes normal
function . amp = getAsFloat ( v . content , pos ) ;
function . frequency = getAsFloat ( v . content , pos ) ;
deformvertexes_normal ( TimeAbs , function ) ;
break ;
case AUTOSPRITE :
// deformvertexes autosprite
deformvertexes_autosprite ( TimeAbs , function ) ;
break ;
case AUTOSPRITE2 :
// deformvertexes autosprite2
deformvertexes_autosprite2 ( TimeAbs , function ) ;
break ;
default :
break ;
} // func
switch ( function . masterfunc0 )
{
case TCMOD :
texture * = m2 ;
break ;
default :
break ;
}
} // group
vertextransform_rgbgen ( TimeAbs , function ) ;
vertextransform_alphagen ( TimeAbs , function ) ;
vertextransform_tcgen ( TimeAbs , function ) ;
}
void CQuake3ShaderSceneNode : : OnAnimate ( u32 timeMs )
{
TimeAbs = f32 ( timeMs ) * ( 1.f / 1000.f ) ;
ISceneNode : : OnAnimate ( timeMs ) ;
}
const core : : aabbox3d < f32 > & CQuake3ShaderSceneNode : : getBoundingBox ( ) const
{
return MeshBuffer - > getBoundingBox ( ) ;
}
u32 CQuake3ShaderSceneNode : : getMaterialCount ( ) const
{
return Q3Texture . size ( ) ;
}
video : : SMaterial & CQuake3ShaderSceneNode : : getMaterial ( u32 i )
{
video : : SMaterial & m = MeshBuffer - > Material ;
m . setTexture ( 0 , 0 ) ;
if ( Q3Texture [ i ] . TextureIndex )
m . setTexture ( 0 , Q3Texture [ i ] . Texture [ Q3Texture [ i ] . TextureIndex ] ) ;
return m ;
}
} // end namespace scene
} // end namespace irr
# endif