From 7028cbe09c688437910a25623098762bf0fa592d Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Mon, 28 Mar 2016 22:28:34 +1000 Subject: Move Irrlicht to src/others. --- .../source/Irrlicht/CQuake3ShaderSceneNode.cpp | 1376 ++++++++++++++++++++ 1 file changed, 1376 insertions(+) create mode 100644 src/others/irrlicht-1.8.1/source/Irrlicht/CQuake3ShaderSceneNode.cpp (limited to 'src/others/irrlicht-1.8.1/source/Irrlicht/CQuake3ShaderSceneNode.cpp') diff --git a/src/others/irrlicht-1.8.1/source/Irrlicht/CQuake3ShaderSceneNode.cpp b/src/others/irrlicht-1.8.1/source/Irrlicht/CQuake3ShaderSceneNode.cpp new file mode 100644 index 0000000..9219782 --- /dev/null +++ b/src/others/irrlicht-1.8.1/source/Irrlicht/CQuake3ShaderSceneNode.cpp @@ -0,0 +1,1376 @@ +// 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" +#include "CShadowVolumeSceneNode.h" + +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" ) || + 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" ) ); + + if ( group->isDefined( "depthwrite" ) ) + { + material.ZWriteEnable = true; + } + else + { + material.ZWriteEnable = drawCount == 0; + } + + //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].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) +{ + 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; +} + + +/*! +3.3.1 deformVertexes wave
+ 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
+ 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 + 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: +
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"). + + 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. + + 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. + + The distance that the deformation moves away from the base value. + See Wave Forms in the introduction for a description of amplitude. + + See Wave Forms in the introduction for a description of phase) + + 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 + 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 * 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 * 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: + // tcMod scroll + 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 + f[0] = getAsFloat( v.content, pos ); + f[1] = getAsFloat( v.content, pos ); + m2.setTextureScale( f[0], f[1] ); + break; + case ROTATE: + // tcmod rotate + m2.setTextureRotationCenter( getAsFloat( v.content, pos ) * + core::DEGTORAD * + TimeAbs + ); + break; + case TRANSFORM: + // tcMod + 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 + f[0] = core::reciprocal( function.evaluate(TimeAbs) ); + m2.setTextureScaleCenter( f[0], f[0] ); + break; + case TURBULENCE: + //tcMod turb + //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& 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 + -- cgit v1.1