/* =========================================================================== maya2q3 - export .md3 files from maya Copyright (C) 2005 HermitWorks Entertainment Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. =========================================================================== */ #include "common.h" namespace shader { /* The routines in this file are based on the Q3 source. They are essentially nothing more than the parse functions int tr_shader.c stripped of the options the plugin won't support and converted into C++. */ void Shader::InitDefault( void ) { m_cull = CullMode::Back; m_polyOffset = false; m_noMips = false; m_noPicMip = false; m_needNorm = false; m_needTexCoord = false; m_numDeforms = 0; memset( m_deforms, 0, sizeof( m_deforms ) ); m_numStages = 0; memset( m_stages, 0, sizeof( m_stages ) ); memset( &m_time, 0, sizeof( m_time ) ); } Shader::WaveFunc Shader::NameToGenFunc( const char *funcname ) { if( !stricmp( funcname, "sin" ) ) return WaveFunc::Sin; else if( !stricmp( funcname, "square" ) ) return WaveFunc::Square; else if( !stricmp( funcname, "triangle" ) ) return WaveFunc::Triangle; else if( !stricmp( funcname, "sawtooth" ) ) return WaveFunc::SawTooth; else if( !stricmp( funcname, "inversesawtooth" ) ) return WaveFunc::InvSawTooth; else if( !stricmp( funcname, "noise" ) ) return WaveFunc::Noise; warn( "invalid wave func" ); return WaveFunc::Sin; } void Shader::ParseTexMod( const char *text, ShaderStage &stage ) { demand( stage.bundle.numTexMods < MaxTexMods, "too many texmods in shader pass" ); parse_token tok; ShaderStage::TextureBundle::TextureMod &tmi = stage.bundle.texMods[stage.bundle.numTexMods++]; ParseExt( text, false, tok ); if( tok == "turb" ) { // turb ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.base = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.amplitude = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.phase = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.frequency = (float)atof( tok ); tmi.type = TexMod::Turbulent; } else if( tok == "scale" ) { // scale ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.scale[0] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.scale[1] = (float)atof( tok ); tmi.type = TexMod::Scale; } else if( tok == "scroll" ) { // scroll ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.scroll[0] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.scroll[1] = (float)atof( tok ); tmi.type = TexMod::Scroll; } else if( tok == "stretch" ) { // stretch ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.type = NameToGenFunc( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.base = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.amplitude = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.phase = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.wave.frequency = (float)atof( tok ); tmi.type = TexMod::Stretch; } else if( tok == "transform" ) { // transform ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.matrix[0][0] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.matrix[0][1] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.matrix[1][0] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.matrix[1][1] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.translate[0] = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.translate[1] = (float)atof( tok ); tmi.type = TexMod::Transform; } else if( tok == "rotate" ) { // rotate ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing tcmod param" ); return; } tmi.rotateSpeed = (float)atof( tok ); tmi.type = TexMod::Rotate; } else if( tok == "entityTranslate" ) { // entityTranslate tmi.type = TexMod::EntityTranslate; } else warn( "invalid tcmod" ); } void Shader::ParseWaveForm( const char *&text, WaveForm &wave ) { parse_token tok; ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing waveform param" ); return; } wave.type = NameToGenFunc( tok ); // BASE, AMP, PHASE, FREQ ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing waveform param" ); return; } wave.base = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing waveform param" ); return; } wave.amplitude = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing waveform param" ); return; } wave.phase = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing waveform param" ); return; } wave.frequency = (float)atof( tok ); } void Shader::ParseStage( const char *&text ) { parse_token tok; StateBits depthMaskBits = StateBits::DepthMask; StateBits blendSrcBits = StateBits::None; StateBits blendDstBits = StateBits::None; StateBits atestBits = StateBits::None; StateBits depthFuncBits = StateBits::None; StateBits wrapBits = StateBits::None; bool depthMaskExplicit = false; ShaderStage &stage = m_stages[m_numStages++]; for( ; ; ) { ParseExt( text, true, tok ); demand( tok != "", "no matching } found in pass" ); if( tok == "}" ) break; if( tok == "map" ) { // map ParseExt( text, false, tok ); demand( tok != "", "missing param for map" ); if( tok == "$lightmap" ) tok = "$whiteimage"; stage.bundle.texNames[0] = tok; stage.bundle.numTextures = 1; wrapBits = StateBits::TexAddr_Wrap; } else if( tok == "clampmap" ) { // clampmap ParseExt( text, false, tok ); demand( tok != "", "missing clampmap param" ); stage.bundle.texNames[0] = tok; stage.bundle.numTextures = 1; wrapBits = StateBits::TexAddr_Clamp; } else if( tok == "eclampmap" ) { // eclampmap ParseExt( text, false, tok ); demand( tok != "", "missing clampmap param" ); stage.bundle.texNames[0] = tok; stage.bundle.numTextures = 1; wrapBits = StateBits::TexAddr_ClampToEdge; } else if( tok == "animMap" ) { // animMap .... ParseExt( text, false, tok ); demand( tok != "", "missing animMap param" ); stage.bundle.texAnimSpeed = (float)atof( tok ); // parse up to MAX_IMAGE_ANIMATIONS animations for( ; ; ) { ParseExt( text, false, tok ); if( tok == "" ) break; int num = stage.bundle.numTextures; if( num < MaxImageAnims ) { stage.bundle.texNames[num] = tok; stage.bundle.numTextures = num + 1; } } } else if( tok == "videoMap" ) { ParseExt( text, false, tok ); warn( "ignoring cinematic" ); } else if( tok == "alphaFunc" ) { // alphafunc ParseExt( text, false, tok ); demand( tok != "", "missing alphaFunc param" ); if( tok == "GT0" ) atestBits = StateBits::AlphaTest_GT_0; else if( tok == "LT128" ) atestBits = StateBits::AlphaTest_LT_0x80; else if( tok == "GE128" ) atestBits = StateBits::AlphaTest_GE_0x80; else { warn( "invalid alpha test param" ); atestBits = StateBits::None; } } else if( tok == "depthfunc" ) { // depthFunc ParseExt( text, false, tok ); demand( tok != "", "missing param for depth func" ); if ( tok == "lequal" ) depthFuncBits = StateBits::None; else if( tok == "equal" ) depthFuncBits = StateBits::DepthFunc_Equal; else { warn( "invalid depthfunc param" ); continue; } } else if( tok == "detail" ) { // detail stage.isDetail = true; } else if( tok == "blendfunc" ) { // blendfunc // or blendfunc ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing blendfunc param" ); continue; } // check for "simple" blends first if( tok == "add" ) { blendSrcBits = StateBits::SrcBlend_One; blendDstBits = StateBits::DstBlend_One; } else if( tok == "filter" ) { blendSrcBits = StateBits::SrcBlend_DstColor; blendDstBits = StateBits::DstBlend_Zero; } else if( tok == "blend" ) { blendSrcBits = StateBits::SrcBlend_SrcAlpha; blendDstBits = StateBits::DstBlend_InvSrcAlpha; } else { // complex double blends if ( tok == "GL_ONE" ) blendSrcBits = StateBits::SrcBlend_One; else if( tok == "GL_ZERO" ) blendSrcBits = StateBits::SrcBlend_Zero; else if( tok == "GL_DST_COLOR" ) blendSrcBits = StateBits::SrcBlend_DstColor; else if( tok == "GL_ONE_MINUS_DST_COLOR" ) blendSrcBits = StateBits::SrcBlend_InvDstColor; else if( tok == "GL_SRC_ALPHA" ) blendSrcBits = StateBits::SrcBlend_SrcAlpha; else if( tok == "GL_ONE_MINUS_SRC_ALPHA" ) blendSrcBits = StateBits::SrcBlend_InvSrcAlpha; else if( tok == "GL_DST_ALPHA" ) blendSrcBits = StateBits::SrcBlend_DstAlpha; else if( tok == "GL_ONE_MINUS_DST_ALPHA" ) blendSrcBits = StateBits::SrcBlend_InvDstAlpha; else if( tok == "GL_SRC_ALPHA_SATURATE" ) blendSrcBits = StateBits::SrcBlend_AlphaSat; else { warn( "invalid source blend mode" ); blendSrcBits = StateBits::SrcBlend_One; } ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing dest blend mode" ); continue; } if( tok == "GL_ONE" ) blendDstBits = StateBits::DstBlend_One; else if( tok == "GL_ZERO" ) blendDstBits = StateBits::DstBlend_Zero; else if( tok == "GL_SRC_ALPHA" ) blendDstBits = StateBits::DstBlend_SrcAlpha; else if( tok == "GL_ONE_MINUS_SRC_ALPHA" ) blendDstBits = StateBits::DstBlend_InvSrcAlpha; else if( tok == "GL_DST_ALPHA" ) blendDstBits = StateBits::DstBlend_DstAlpha; else if( tok == "GL_ONE_MINUS_DST_ALPHA" ) blendDstBits = StateBits::DstBlend_InvDstAlpha; else if( tok == "GL_SRC_COLOR" ) blendDstBits = StateBits::DstBlend_SrcColor; else if( tok == "GL_ONE_MINUS_SRC_COLOR" ) blendDstBits = StateBits::DstBlend_InvSrcColor; else { warn( "invalid dest blend mode" ); blendDstBits = StateBits::DstBlend_One; } } // clear depth mask for blended surfaces if( !depthMaskExplicit ) depthMaskBits = StateBits::None; } else if( tok == "rgbGen" ) { // rgbGen ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing rgbgen param" ); continue; } if( tok == "wave" ) { ParseWaveForm( text, stage.rgb.wave ); stage.rgb.type = RgbGen::Wave; } else if( tok == "const" ) { ParseVector( stage.rgb.c, 3, text, true ); stage.rgb.type = RgbGen::Const; } else if( tok == "identity" ) stage.rgb.type = RgbGen::Identity; else if( tok == "identityLighting" ) stage.rgb.type = RgbGen::IdentityLighting; else if( tok == "entity" ) stage.rgb.type = RgbGen::Entity; else if( tok == "oneMinusEntity" ) stage.rgb.type = RgbGen::InvEntity; else if( tok == "vertex" ) { stage.rgb.type = RgbGen::Vertex; if( stage.alpha.type == AlphaGen::Identity ) stage.alpha.type = AlphaGen::Vertex; } else if( tok == "exactVertex" ) stage.rgb.type = RgbGen::ExactVertex; else if( tok == "lightingDiffuse" ) stage.rgb.type = RgbGen::DiffuseLit; else if( tok == "oneMinusVertex" ) stage.rgb.type = RgbGen::InvVertex; else { warn( "unknown rgbgen type param" ); continue; } } else if( tok == "alphaGen" ) { // alphaGen ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing alphagen param" ); continue; } if( tok == "wave" ) { stage.alpha.type = AlphaGen::Wave; ParseWaveForm( text, stage.alpha.wave ); } else if( tok == "const" ) { ParseExt( text, false, tok ); stage.alpha.a = (float)atof( tok ); stage.alpha.type = AlphaGen::Const; } else if( tok == "identity" ) stage.alpha.type = AlphaGen::Identity; else if( tok == "entity" ) stage.alpha.type = AlphaGen::Entity; else if( tok == "oneMinusEntity" ) stage.alpha.type = AlphaGen::InvEntity; else if( tok == "vertex" ) stage.alpha.type = AlphaGen::Vertex; else if( tok == "lightingSpecular" ) stage.alpha.type = AlphaGen::Specular; else if( tok == "oneMinusVertex" ) stage.alpha.type = AlphaGen::InvVertex; else if( tok == "portal" ) { stage.alpha.type = AlphaGen::Portal; ParseExt( text, false, tok ); if( tok == "" ) { m_portalRange = 256; warn( "missing portal alphagen param" ); } else m_portalRange = (float)atof( tok ); } else { warn( "invalid alphagen param" ); continue; } } else if( tok == "texgen" || tok == "tcGen" ) { // tcGen ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing texgen param" ); continue; } if( tok == "environment" ) stage.bundle.texGen = TexGen::EnvMap; else if( tok == "lightmap" ) { warn( "treating lightmap texgen as base" ); stage.bundle.texGen = TexGen::Texture; } else if( tok == "texture" || tok == "base" ) stage.bundle.texGen = TexGen::Texture; else if( tok == "vector" ) { ParseVector( stage.bundle.texGenVecs[0], 3, text, true ); ParseVector( stage.bundle.texGenVecs[1], 3, text, true ); stage.bundle.texGen = TexGen::Vector; } else warn( "invalid tcgen param" ); } else if( tok == "tcMod" ) { // tcMod <...> sstr< 1024 > buffer = ""; for( ; ; ) { ParseExt( text, false, tok ); if( tok == "" ) break; buffer.Append( tok ); buffer.Append( " " ); } ParseTexMod( buffer, stage ); continue; } else if( tok == "depthwrite" ) { // depthmask depthMaskBits = StateBits::DepthMask; depthMaskExplicit = true; continue; } else { warn( "unknown shader stage paramater, skipping line" ); SkipRestOfLine( text ); } } // if cgen isn't explicitly specified, use either identity or identitylighting if( stage.rgb.type == RgbGen::Bad ) { if( blendSrcBits == StateBits::None || blendSrcBits == StateBits::SrcBlend_One || blendSrcBits == StateBits::SrcBlend_SrcAlpha ) { stage.rgb.type = RgbGen::IdentityLighting; } else { stage.rgb.type = RgbGen::Identity; } } // implicitly assume that a GL_ONE GL_ZERO blend mask disables blending if( blendSrcBits == StateBits::SrcBlend_One && blendDstBits == StateBits::DstBlend_Zero ) { blendSrcBits = StateBits::None; blendDstBits = StateBits::None; depthMaskBits = StateBits::DepthMask; } if( stage.bundle.texGen == TexGen::Bad ) stage.bundle.texGen = TexGen::Texture; // compute state bits stage.state = depthMaskBits | blendSrcBits | blendDstBits | atestBits | depthFuncBits | wrapBits; } void Shader::ParseDeform( const char *&text ) { parse_token tok; ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing deform param" ); return; } if( m_numDeforms == MaxDeforms ) { warn( "exceeded max deforms in shader" ); return; } DeformStage &ds = m_deforms[m_numDeforms]; m_numDeforms++; if( tok == "projectionShadow" ) { warn( "projectioniShadow deformation not supported" ); return; } if( tok == "autosprite" ) { ds.type = Deformation::AutoSprite; return; } if( tok == "autosprite2" ) { ds.type = Deformation::AutoSprite2; return; } if( tok.BeginsWith( "text" ) ) { warn( "textN deformation not supported" ); return; } if( tok == "bulge" ) { ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing bulge deformation param" ); return; } ds.bulge.width = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing bulge deformation param" ); return; } ds.bulge.height = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing bulge deformation param" ); return; } ds.bulge.speed = (float)atof( tok ); ds.type = Deformation::Bulge; return; } if( tok == "wave" ) { ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing deformation param" ); return; } float val = (float)atof( tok ); if( val != 0 ) { ds.spread = 1.0F / val; } else { ds.spread = 100.0F; warn( "invalid div param on deform wave" ); } ParseWaveForm( text, ds.wave ); ds.type = Deformation::Wave; return; } if( tok == "normal" ) { ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing deform param" ); return; } ds.wave.amplitude = (float)atof( tok ); ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing deform param" ); return; } ds.wave.frequency = (float)atof( tok ); ds.type = Deformation::Normals; return; } if( tok == "move" ) { for( int i = 0; i < 3 ; i++ ) { ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing deform param" ); return; } ds.moveVec[i] = (float)atof( tok ); } ParseWaveForm( text, ds.wave ); ds.type = Deformation::Move; return; } warn( "unknown deformation type" ); } void Shader::Parse( const char *text ) { InitDefault(); parse_token tok; ParseExt( text, true, tok ); demand( tok == "{", "expecting { in shader" ); for( ; ; ) { ParseExt( text, true, tok ); if ( tok == "" ) fail( "no concluding } in shader" ); if( tok == "}" ) // end of shader definition break; else if( tok == "{" ) { // stage definition ParseStage( text ); continue; } else if( tok.BeginsWith( "qer" ) ) { // skip stuff that only the QuakeEdRadient needs SkipRestOfLine( text ); continue; } else if( tok == "q3map_sun" ) { // sun parms warn( "skipping sun info" ); //use 6 of these rather than a skip-line //to maintain parallel to q3 code ParseExt( text, false, tok ); ParseExt( text, false, tok ); ParseExt( text, false, tok ); ParseExt( text, false, tok ); ParseExt( text, false, tok ); ParseExt( text, false, tok ); } else if( tok == "deformVertexes" ) { ParseDeform( text ); continue; } else if( tok == "tesssize" ) { SkipRestOfLine( text ); continue; } else if( tok == "clampTime" ) { ParseExt( text, false, tok ); if( tok != "" ) m_time.clamp = (float)atof( tok ); } else if( tok.BeginsWith( "q3map" ) ) { // skip stuff that only the q3map needs SkipRestOfLine( text ); continue; } else if( tok == "surfaceParm" ) { ParseExt( text, false, tok ); if( tok == "nodraw" ) m_surfFlags |= SurfaceFlags::NoDraw; else if( tok == "trans" ) m_surfFlags |= SurfaceFlags::Transparent; continue; } else if( tok == "nomipmaps" ) { // no mip maps m_noMips = true; m_noPicMip = true; continue; } else if( tok == "nopicmip" ) { // no picmip adjustment m_noPicMip = true; continue; } else if( tok == "polygonOffset" ) { // polygonOffset m_polyOffset = true; continue; } else if( tok == "entityMergable" ) { continue; } else if( tok == "fogParms" ) { // fogParms ParseVector( null, 3, text, true ); ParseExt( text, false, tok ); if ( tok == "" ) { warn( "missing for param" ); continue; } // skip any old gradient directions SkipRestOfLine( text ); continue; } else if( tok == "portal" ) { // portal continue; } else if( tok == "skyparms" ) { // skyparms ParseExt( text, false, tok ); if( tok == "" ) warn( "malformed sky params" ); ParseExt( text, false, tok ); if( tok == "" ) warn( "malformed sky params" ); ParseExt( text, false, tok ); if( tok == "" ) warn( "malformed sky params" ); continue; } else if( tok == "light" ) { // light determines flaring in q3map, not needed here ParseExt( text, false, tok ); continue; } else if( tok == "cull" ) { // cull ParseExt( text, false, tok ); if( tok == "" ) { warn( "missing cull paramater" ); continue; } if( tok == "none" || tok == "twosided" || tok == "disable" ) { m_cull = CullMode::None; } else if( tok == "back" || tok == "backside" || tok == "backsided" ) { m_cull = CullMode::Front; } else { warn( "invalid cull param" ); } continue; } else if( tok == "sort" ) { // sort ParseExt( text, false, tok ); if( tok == "" ) warn( "missing sort param in shader" ); continue; } else fail( "unknown shader param" ); } if( !m_numStages ) fail( "shader defines no stages" ); //do some optimizing //for( int i = 0; i < m_numStages; i++ ) // OptimizeTexMods( m_stages[i].bundle ); } void Shader::SetToTexture( const char *texName ) { InitDefault(); m_numStages = 1; ShaderStage &stage = m_stages[0]; stage.bundle.numTextures = 1; stage.bundle.texNames[0] = texName; stage.rgb.type = RgbGen::DiffuseLit; stage.state = StateBits::Default; } /* static */ const char* Shader::FindShaderInFile( const char *fileText, const char *shaderName ) { parse_token tok; for( ; ; ) { ParseExt( fileText, true, tok ); if( tok == "" ) return null; if( tok == "{" ) { //scan to the matching closing brace int brace_count = 1; while( brace_count ) { ParseExt( fileText, true, tok ); if( tok == "" ) return null; if( tok == "{" ) brace_count++; else if( tok == "}" ) brace_count--; } continue; } if( tok == shaderName ) return fileText; } } /* static */ void Shader::GetShadersInFile( const char *fileText, std::vector< q3::qname > &retList ) { parse_token tok; for( ; ; ) { ParseExt( fileText, true, tok ); if( tok == "" ) return; if( tok == "{" ) { //scan to the matching closing brace int brace_count = 1; while( brace_count ) { ParseExt( fileText, true, tok ); if( tok == "" ) return; if( tok == "{" ) brace_count++; else if( tok == "}" ) brace_count--; } continue; } retList.push_back( (const char*)tok ); } } };