#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"
namespace irr
{
namespace scene
{
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), Original(0), MeshBuffer(0), TimeAbs(0.f)
{
#ifdef _DEBUG
core::stringc dName = "CQuake3ShaderSceneNode ";
dName += Shader->name;
setDebugName( dName.c_str() );
#endif
this->Name = Shader->name;
MeshBuffer = new SMeshBuffer();
Mesh = new SMesh ();
Mesh->addMeshBuffer ( MeshBuffer );
MeshBuffer->drop ();
Original = (const scene::SMeshBufferLightMap*) original;
Original->grab();
cloneBuffer(MeshBuffer, Original,
Original->getMaterial().ColorMask != 0);
loadTextures( fileSystem );
setAutomaticCulling( scene::EAC_OFF );
}
CQuake3ShaderSceneNode::~CQuake3ShaderSceneNode()
{
if (Mesh)
Mesh->drop();
if (Original)
Original->drop();
}
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 );
}
if ( translateCenter )
{
MeshOffset = dest->BoundingBox.getCenter();
setPosition( MeshOffset );
core::matrix4 m;
m.setTranslation( -MeshOffset );
SceneManager->getMeshManipulator()->transform( dest, m );
}
dest->Material.setTexture(0, (video::ITexture*) Shader);
}
void CQuake3ShaderSceneNode::loadTextures( io::IFileSystem * fileSystem )
{
const SVarGroup *group;
u32 i;
video::IVideoDriver *driver = SceneManager->getVideoDriver();
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);
}
Q3Texture.setAllocStrategy ( core::ALLOC_STRATEGY_SAFE );
Q3Texture.clear();
for ( i = 0; i != Shader->VarGroup->VariableGroup.size(); ++i )
{
Q3Texture.push_back( SQ3Texture() );
}
u32 pos;
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;
if ( mapname == "$lightmap" )
{
Q3Texture [i].Texture.push_back( Original->getMaterial().getTexture(1) );
}
else
{
pos = 0;
getTextures( Q3Texture [i].Texture, mapname, pos, fileSystem, driver );
}
}
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;
pos = 0;
Q3Texture [i].TextureFrequency = core::max_( 0.0001f, getAsFloat( animmap, pos ) );
getTextures( Q3Texture [i].Texture, animmap, pos,fileSystem, driver );
}
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);
}
void CQuake3ShaderSceneNode::OnRegisterSceneNode()
{
if ( isVisible() )
{
SceneManager->registerNodeForRendering(this, getRenderStage() );
}
ISceneNode::OnRegisterSceneNode();
}
E_SCENE_NODE_RENDER_PASS CQuake3ShaderSceneNode::getRenderStage() const
{
E_SCENE_NODE_RENDER_PASS ret = ESNRP_SOLID;
const SVarGroup *group;
group = Shader->getGroup( 1 );
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" ) ||
group->isDefined( "sort", "underwater" )
)
{
ret = ESNRP_TRANSPARENT_EFFECT;
}
else
{
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 );
if ( blendfunc.isTransparent )
{
ret = ESNRP_TRANSPARENT;
}
break;
}
}
return ret;
}
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;
group = Shader->getGroup( 1 );
material.BackfaceCulling = getCullingFunction( group->get( "cull" ) );
u32 pushProjection = 0;
core::matrix4 projection ( core::matrix4::EM4CONST_NOTHING );
if ( group->isDefined( "polygonoffset" ) )
{
projection = driver->getTransform( video::ETS_PROJECTION );
core::matrix4 decalProjection ( projection );
decalProjection[10] -= 0.0002f;
driver->setTransform( video::ETS_PROJECTION, decalProjection );
pushProjection |= 1;
}
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation );
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];
textureMatrix.makeIdentity();
animate( stage, textureMatrix );
video::ITexture * tex = q.Texture.size() ? q.Texture [ q.TextureIndex ] : 0;
if ( 0 == tex )
continue;
group = Shader->getGroup( stage );
material.setTexture(0, tex );
material.ZBuffer = getDepthFunction( group->get( "depthfunc" ) );
if ( group->isDefined( "depthwrite" ) )
{
material.ZWriteEnable = true;
}
else
{
material.ZWriteEnable = drawCount == 0;
}
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.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 );
}
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 );
core::matrix4 m2;
const scene::IMeshBuffer* mb = MeshBuffer;
const u32 vSize = video::getVertexPitchFromType(mb->getVertexType());
const video::S3DVertex* v = ( const video::S3DVertex*)mb->getVertices();
for ( u32 i=0; i != mb->getVertexCount(); ++i )
{
m2.buildRotateFromTo ( core::vector3df ( 0.f, 1.f, 0 ), v->Normal );
m2.setTranslation ( v->Pos + AbsoluteTransformation.getTranslation () );
driver->setTransform(video::ETS_WORLD, m2 );
deb_m.Lighting = true;
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));
}
}
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;
}
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;
}
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 );
}
}
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;
}
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 )
{
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;
}
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 )
{
core::vector3df center = 0.25f * ( vin[i+0].Pos + vin[i+1].Pos + vin[i+2].Pos + vin[i+3].Pos );
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;
}
void CQuake3ShaderSceneNode::vertextransform_rgbgen( f32 dt, SModifierFunction &function )
{
u32 i;
const u32 vsize = Original->Vertices.size();
switch ( function.rgbgen )
{
case IDENTITY:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].Color.set(0xFFFFFFFF);
break;
case IDENTITYLIGHTING:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].Color.set(0xFF7F7F7F);
break;
case EXACTVERTEX:
case VERTEX:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].Color=Original->Vertices[i].Color;
break;
case WAVE:
{
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:
{
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;
}
}
void CQuake3ShaderSceneNode::vertextransform_alphagen( f32 dt, SModifierFunction &function )
{
u32 i;
const u32 vsize = Original->Vertices.size();
switch ( function.alphagen )
{
case IDENTITY:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].Color.setAlpha ( 0xFF );
break;
case EXACTVERTEX:
case VERTEX:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].Color.setAlpha ( Original->Vertices[i].Color.getAlpha() );
break;
case CONSTANT:
{
u32 a = (u32) ( function.x * 255.f );
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].Color.setAlpha ( a );
} break;
case LIGHTINGSPECULAR:
{
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:
{
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;
}
}
void CQuake3ShaderSceneNode::vertextransform_tcgen( f32 dt, SModifierFunction &function )
{
u32 i;
const u32 vsize = Original->Vertices.size();
switch ( function.tcgen )
{
case TURBULENCE:
{
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:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].TCoords = Original->Vertices[i].TCoords;
break;
case LIGHTMAP:
for ( i = 0; i != vsize; ++i )
MeshBuffer->Vertices[i].TCoords = Original->Vertices[i].TCoords2;
break;
case 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 )
{
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
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;
}
}
}
#endif
void CQuake3ShaderSceneNode::animate( u32 stage,core::matrix4 &texture )
{
const SVarGroup *group = Shader->getGroup( stage );
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];
for ( u32 g = 0; g != group->Variable.size(); ++g )
{
const SVariable &v = group->Variable[g];
static const c8 * 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 )
{
case TCMOD:
m2.makeIdentity();
break;
default:
break;
}
static const c8 * 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 * 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:
f[0] = getAsFloat( v.content, pos ) * TimeAbs;
f[1] = getAsFloat( v.content, pos ) * TimeAbs;
m2.setTextureTranslate( f[0], f[1] );
break;
case SCALE:
f[0] = getAsFloat( v.content, pos );
f[1] = getAsFloat( v.content, pos );
m2.setTextureScale( f[0], f[1] );
break;
case ROTATE:
m2.setTextureRotationCenter( getAsFloat( v.content, pos ) *
core::DEGTORAD *
TimeAbs
);
break;
case TRANSFORM:
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:
case TURBULENCE:
case WAVE:
case IDENTITY:
case IDENTITYLIGHTING:
case VERTEX:
case MOVE:
case CONSTANT:
{
function.func = SINUS;
if ( function.masterfunc0 == DEFORMVERTEXES )
{
switch ( function.masterfunc1 )
{
case WAVE:
function.wave = getAsFloat( v.content, pos );
break;
case 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:
f[0] = core::reciprocal( function.evaluate(TimeAbs) );
m2.setTextureScaleCenter( f[0], f[0] );
break;
case 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 );
}
break;
case ALPHAGEN:
function.alphagen = function.masterfunc1;
if ( function.alphagen == CONSTANT )
{
function.x = getAsFloat( v.content, pos );
}
break;
default:
break;
}
break;
default:
break;
}
} break;
case TEXTURE:
case LIGHTMAP:
case ENVIRONMENT:
function.tcgen = function.masterfunc1;
break;
case DOLLAR_LIGHTMAP:
function.tcgen = LIGHTMAP;
break;
case 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:
function.amp = getAsFloat( v.content, pos );
function.frequency = getAsFloat( v.content, pos );
deformvertexes_normal(TimeAbs, function);
break;
case AUTOSPRITE:
deformvertexes_autosprite(TimeAbs, function);
break;
case AUTOSPRITE2:
deformvertexes_autosprite2(TimeAbs, function);
break;
default:
break;
}
switch ( function.masterfunc0 )
{
case TCMOD:
texture *= m2;
break;
default:
break;
}
}
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;
}
}
}
#endif