blob: 294f2fe035536c32619c3c76824d20c6f5477264 [file] [log] [blame] [edit]
/*
Copyright (c) 2011-2013 NVIDIA Corporation
Copyright (c) 2011-2013 Cass Everitt
Copyright (c) 2012 Scott Nations
Copyright (c) 2012 Mathias Schott
Copyright (c) 2012 Nigel Stewart
Copyright (c) 2013 Seth Williams
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "pch.h" /* For MS precompiled header support */
#include "RegalUtil.h"
#if REGAL_EMULATION
REGAL_GLOBAL_BEGIN
#include <cstring>
#include <string>
using std::string;
#include <boost/print/string_list.hpp>
typedef boost::print::string_list<string> string_list;
#include "RegalIff.h"
#include "RegalLog.h"
#include "RegalToken.h"
#include "RegalHelper.h"
#include "RegalShader.h"
REGAL_GLOBAL_END
REGAL_NAMESPACE_BEGIN
namespace Emu
{
using namespace ::REGAL_NAMESPACE_INTERNAL::Logging;
using namespace ::REGAL_NAMESPACE_INTERNAL::Shader;
using namespace ::REGAL_NAMESPACE_INTERNAL::Token;
typedef Iff::State State;
typedef Iff::State::Texture Texture;
typedef Iff::State::Light Light;
typedef Iff::State::MaterialUniform MaterialUniform;
typedef Iff::State::Store Store;
typedef Iff::State::StoreUniform StoreUniform;
typedef Iff::Program Program;
enum FFLightEl
{
LE_Ambient = 0,
LE_Diffuse = 1,
LE_Specular = 2,
LE_Position = 3,
LE_SpotDir = 4,
LE_Atten = 5,
LE_Elements = 6
};
enum FFMaterialEl
{
ME_Ambient = 0,
ME_Diffuse = 1,
ME_Specular = 2,
ME_Emission = 3,
ME_Shininess = 4,
ME_Elements = 5
};
static Iff::TextureTargetBitfield TargetToBitfield( GLenum target )
{
switch( target )
{
case GL_TEXTURE_1D:
return Iff::TT_1D;
case GL_TEXTURE_2D:
return Iff::TT_2D;
case GL_TEXTURE_RECTANGLE:
return Iff::TT_Rect;
case GL_TEXTURE_3D:
return Iff::TT_3D;
case GL_TEXTURE_CUBE_MAP:
return Iff::TT_CubeMap;
default:
break;
}
return Iff::TT_None;
}
static void GenerateVertexShaderSource( const Iff * rff, const Iff::State & state, string_list & src )
{
Internal("Regal::Iff::GenerateVertexShaderSource", boost::print::optional(rff,Logging::pointers));
const bool gles = rff->gles;
const bool legacy = rff->legacy;
const Store & st = state.processed;
bool hasNormalMap = false;
bool hasSphereMap = false;
bool hasReflectionMap = false;
bool hasEyeLinearTexGen = false;
size_t n = array_size( st.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.tex, i );
if ( st.tex[i].enables == 0 )
{
continue;
}
for ( size_t j = 0; j < 4; j++ )
{
RegalAssertArrayIndex( st.tex[i].texgen, j );
if ( st.tex[i].texgen[j].enable == false )
{
continue;
}
Iff::TexgenMode mode = st.tex[i].texgen[j].mode;
hasNormalMap = hasNormalMap || mode == Iff::TG_NormalMap;
hasSphereMap = hasSphereMap || mode == Iff::TG_SphereMap;
hasReflectionMap = hasReflectionMap || mode == Iff::TG_ReflectionMap;
hasEyeLinearTexGen = hasEyeLinearTexGen || mode == Iff::TG_EyeLinear;
}
}
bool hasClipPlanes = false;
n = array_size( st.clipPlaneEnabled );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.clipPlaneEnabled, i );
hasClipPlanes = hasClipPlanes || st.clipPlaneEnabled[i];
}
if ( gles )
{
#if REGAL_FORCE_DESKTOP_GLSL
// Desktop version 140 corresponds to OpenGL 3.1,
// which corresponds to the functionality of ES2.
src << "#version 140\n";
#else
src << "#version 100\n";
#endif
}
else if ( legacy )
{
src << "#version 120\n";
}
else
{
src << "#version 140\n";
}
src << "// program number " << rff->progcount << "\n";
if ( gles || legacy )
{
src << "#define in attribute\n";
src << "#define out varying\n";
}
if ( st.shadeModelFlat && ! legacy && ! gles )
{
src << "#define FLAT flat\n";
}
else
{
src << "#define FLAT \n";
}
if ( st.lighting )
{
src << "\n";
src << "#define ME_AMBIENT " << int(ME_Ambient) << "\n";
src << "#define ME_DIFFUSE " << int(ME_Diffuse) << "\n";
src << "#define ME_SPECULAR " << int(ME_Specular) << "\n";
src << "#define ME_EMISSION " << int(ME_Emission) << "\n";
src << "#define ME_SHININESS " << int(ME_Shininess) << "\n";
src << "#define ME_ELEMENTS " << int(ME_Elements) << "\n";
src << "\n";
src << "#define LE_AMBIENT " << int(LE_Ambient) << "\n";
src << "#define LE_DIFFUSE " << int(LE_Diffuse) << "\n";
src << "#define LE_SPECULAR " << int(LE_Specular) << "\n";
src << "#define LE_POSITION " << int(LE_Position) << "\n";
src << "#define LE_SPOTDIR " << int(LE_SpotDir) << "\n";
src << "#define LE_ATTEN " << int(LE_Atten) << "\n";
src << "#define LE_ELEMENTS " << int(LE_Elements) << "\n";
src << "\n";
}
if ( gles )
{
src << "precision highp float;\n";
}
RegalAssertArrayIndex( rff->ffAttrMap, RFF2A_Color );
if ( ~st.attrArrayFlags & ( 1 << rff->ffAttrMap[ RFF2A_Color ] ) )
{
src << "uniform vec4 rglAttrib[" << REGAL_EMU_MAX_VERTEX_ATTRIBS << "];\n";
}
src << "uniform mat4 rglModelViewMatrix;\n";
src << "uniform mat4 rglProjectionMatrix;\n";
src << "in vec4 rglVertex;\n";
n = array_size( st.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.tex, i );
if ( st.tex[i].useMatrix )
{
src << "uniform mat4 rglTextureMatrix" << i << ";\n";
}
}
n = array_size( st.clipPlaneEnabled );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.clipPlaneEnabled, i );
if ( st.clipPlaneEnabled[i] )
{
src << "uniform vec4 rglClipPlane" << i << ";\n";
if ( gles || legacy )
{
src << "out float rglClipDistance" << i << ";\n";
}
}
}
//src << "in vec4 rglWeight;\n";
if ( st.lighting || hasNormalMap || hasSphereMap || hasReflectionMap )
{
src << "uniform mat3 rglNormalMatrix;\n";
src << "in vec3 rglNormal;\n";
}
if ( st.lighting )
{
src << "uniform vec4 rglLightModelAmbient;\n";
src << "uniform vec4 rglMaterialFront" << "[ ME_ELEMENTS ];\n";
if ( st.lightModelTwoSide )
{
src << "uniform vec4 rglMaterialBack" << "[ ME_ELEMENTS ];\n";
}
for ( size_t i = 0; i < REGAL_FIXED_FUNCTION_MAX_LIGHTS; i++ )
{
if ( st.light[ i ].enable )
{
src << "uniform vec4 rglLight" << i << "[ LE_ELEMENTS ];\n";
}
}
}
RegalAssertArrayIndex( rff->ffAttrMap, RFF2A_Color );
if ( st.attrArrayFlags & ( 1 << rff->ffAttrMap[ RFF2A_Color ] ) && ( st.lighting == false || st.colorMaterial ) )
{
src << "in vec4 rglColor;\n";
}
else
{
src << "#define rglColor rglAttrib[" << rff->ffAttrMap[ RFF2A_Color ] << "]\n";
}
if ( st.colorSum && st.lighting == false )
{
src << "in vec4 rglSecondaryColor;\n";
}
if ( st.fog.enable )
{
src << "uniform vec4 rglFog[2];\n";
src << "out vec4 rglFOG;\n";
if ( st.fog.useDepth == false )
{
src << "in float rglFogCoord;\n";
}
}
n = array_size( st.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.tex, i );
if ( st.tex[i].enables == 0 )
{
continue;
}
src << "in vec4 rglMultiTexCoord" << i << ";\n";
}
src << "FLAT out vec4 rglFrontColor;\n";
if ( st.lighting )
{
if ( st.lightModelTwoSide )
{
src << "FLAT out vec4 rglBackColor;\n";
}
if ( st.lightModelSeparateSpecular )
{
src << "out vec4 rglSCOL0;\n";
if ( st.lightModelTwoSide )
{
src << "out vec4 rglSCOL1;\n";
}
}
}
else if( st.colorSum )
{
src << "out vec4 rglSCOL0;\n";
}
n = array_size( st.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.tex, i );
const State::Texture & t = st.tex[i];
if ( t.enables == 0 )
{
continue;
}
const char *tc[] = { "S", "T", "R", "Q" };
for ( size_t j = 0; j < 4; j++ )
{
RegalAssertArrayIndex( t.texgen, j );
const State::Texgen & g = t.texgen[j];
if ( g.enable == false )
{
continue;
}
switch( g.mode )
{
case Iff::TG_EyeLinear:
src << "uniform vec4 rglTexGen" << i << "Eye" << tc[j] << ";\n";
break;
case Iff::TG_ObjectLinear:
src << "uniform vec4 rglTexGen" << i << "Obj" << tc[j] << ";\n";
break;
default:
src << "//ERROR: unsupported gen mode\n";
break;
}
}
src << "out vec4 rglTEXCOORD" << i << ";\n";
}
if ( hasNormalMap )
{
src << "vec4 ComputeNormalMap( vec3 n ) {\n";
src << " return vec4( n.x, n.y, n.z, 0.0 );\n";
src << "}\n";
}
if ( hasReflectionMap )
{
src << "vec4 ComputeReflectionMap( vec3 i, vec3 n ) {\n";
src << " vec3 r = reflect( i, n );\n";
src << " return vec4( r.x, r.y, r.z, 0.0 );\n";
src << "}\n";
}
if ( hasSphereMap )
{
src << "vec4 ComputeSphereMap( vec3 i, vec3 n ) {\n";
src << " vec3 r = reflect( i, n );\n";
src << " float minv = 1.0 / sqrt( 2.0 * r.x + 2.0 * r.y + 2.0 * (r.z + 1.0 ) );\n";
src << " return vec4( r.x * minv + 0.5 , r.y * minv + 0.5, 0.0, 0.0 );\n";
src << "}\n";
}
src << "void main() {\n";
src << " gl_Position = rglProjectionMatrix * rglModelViewMatrix * rglVertex;\n";
if ( st.lighting || hasNormalMap || hasReflectionMap || hasEyeLinearTexGen ||
hasClipPlanes || ( st.fog.enable && st.fog.useDepth ) || hasSphereMap )
{
src << " vec4 eh = rglModelViewMatrix * rglVertex;\n";
}
if ( st.lighting || hasNormalMap || hasReflectionMap || hasSphereMap )
{
src << " vec4 ep = eh / eh.w;\n";
src << " vec3 ev = -normalize( ep.xyz );\n";
src << " vec3 en = rglNormalMatrix * rglNormal;\n";
if ( st.normalize )
{
src << " en = normalize( en );\n";
}
}
if ( st.lighting )
{
src << " vec4 mFront[ ME_ELEMENTS ];\n";
src << " mFront[ ME_AMBIENT ] = rglMaterialFront[ ME_AMBIENT ];\n";
src << " mFront[ ME_DIFFUSE ] = rglMaterialFront[ ME_DIFFUSE ];\n";
src << " mFront[ ME_SPECULAR ] = rglMaterialFront[ ME_SPECULAR ];\n";
src << " mFront[ ME_EMISSION ] = rglMaterialFront[ ME_EMISSION ];\n";
src << " mFront[ ME_SHININESS ] = rglMaterialFront[ ME_SHININESS ];\n";
if ( st.lightModelTwoSide )
{
src << " vec4 mBack [ ME_ELEMENTS ];\n";
src << " mBack [ ME_AMBIENT ] = rglMaterialBack [ ME_AMBIENT ];\n";
src << " mBack [ ME_DIFFUSE ] = rglMaterialBack [ ME_DIFFUSE ];\n";
src << " mBack [ ME_SPECULAR ] = rglMaterialBack [ ME_SPECULAR ];\n";
src << " mBack [ ME_EMISSION ] = rglMaterialBack [ ME_EMISSION ];\n";
src << " mBack [ ME_SHININESS ] = rglMaterialBack [ ME_SHININESS ];\n";
}
if ( st.colorMaterial )
{
switch( st.colorMaterialTarget0 )
{
case Iff::CM_None:
break;
case Iff::CM_Ambient:
src << " mFront[ ME_AMBIENT ] = rglColor;\n";
break;
case Iff::CM_Diffuse:
src << " mFront[ ME_DIFFUSE ] = rglColor;\n";
break;
case Iff::CM_Specular:
src << " mFront[ ME_SPECULAR ] = rglColor;\n";
break;
case Iff::CM_Emission:
src << " mFront[ ME_EMISSION ] = rglColor;\n";
break;
case Iff::CM_AmbientAndDiffuse:
src << " mFront[ ME_AMBIENT ] = rglColor;\n";
src << " mFront[ ME_DIFFUSE ] = rglColor;\n";
break;
default:
src << "//ERROR: unsupported color material[0] target\n";
break;
}
if ( st.lightModelTwoSide )
{
switch( st.colorMaterialTarget1 )
{
case Iff::CM_None:
break;
case Iff::CM_Ambient:
src << " mBack[ ME_AMBIENT ] = rglColor;\n";
break;
case Iff::CM_Diffuse:
src << " mBack[ ME_DIFFUSE ] = rglColor;\n";
break;
case Iff::CM_Specular:
src << " mBack[ ME_SPECULAR ] = rglColor;\n";
break;
case Iff::CM_Emission:
src << " mBack[ ME_EMISSION ] = rglColor;\n";
break;
case Iff::CM_AmbientAndDiffuse:
src << " mBack[ ME_AMBIENT ] = rglColor;\n";
src << " mBack[ ME_DIFFUSE ] = rglColor;\n";
break;
default:
src << "//ERROR: unsupported color material[1] target\n";
break;
}
}
}
src << " rglFrontColor.xyz = mFront[ ME_EMISSION ].xyz;\n";
src << " rglFrontColor.w = mFront[ ME_DIFFUSE ].w;\n";
src << " rglFrontColor.xyz += rglLightModelAmbient.xyz * mFront[ ME_AMBIENT ].xyz;\n";
if ( st.lightModelSeparateSpecular )
{
src << " rglSCOL0 = vec4( 0, 0, 0, 0);\n";
}
if ( st.lightModelTwoSide )
{
src << " rglBackColor.xyz = mBack[ ME_EMISSION ].xyz;\n";
src << " rglBackColor.w = mBack[ ME_DIFFUSE ].w;\n";
src << " rglBackColor.xyz += rglLightModelAmbient.xyz * mBack[ ME_AMBIENT ].xyz;\n";
if ( st.lightModelSeparateSpecular )
{
src << " rglSCOL1 = vec4( 0, 0, 0, 0);\n";
}
}
n = array_size( st.light );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.light, i );
if ( st.light[ i ].enable )
{
src << " {\n";
string attenmul = "";
src << " vec4 lvec = rglLight" << i << "[ LE_POSITION ];\n";
if ( st.light[ i ].attenuate || st.light[ i ].spotlight )
{
src << " float att = 1.0;\n";
attenmul = "att *";
}
if ( st.light[ i ].local )
{
src << " lvec.xyz -= ep.xyz;\n";
src << " float ad = sqrt( dot( lvec.xyz, lvec.xyz ) );\n";
src << " lvec.xyz /= ad;\n";
}
if ( st.lightModelLocalViewer )
{
src << " vec3 hvec = normalize( lvec.xyz + ev );\n";
}
else
{
src << " vec3 hvec = normalize( lvec.xyz + vec3( 0, 0, 1 ) );\n";
}
src << " vec3 ambient = rglLight" << i << "[ LE_AMBIENT ].xyz * mFront[ ME_AMBIENT ].xyz;\n";
src << " float dc = max( dot( en, lvec.xyz ), 0.0 );\n";
src << " float sc = max( dot( en, hvec ), 0.0 );\n";
src << " vec3 diffuse = dc * rglLight" << i << "[ LE_DIFFUSE ].xyz * mFront[ ME_DIFFUSE ].xyz;\n";
src << " sc = ( dc > 0.0 && sc > 0.0 ) ? exp( mFront[ ME_SHININESS ].x * log( sc ) ) : 0.0;\n";
src << " vec3 specular = sc * rglLight" << i << "[ LE_SPECULAR ].xyz * mFront[ ME_SPECULAR ].xyz;\n";
if ( st.light[ i ].attenuate && st.light[ i ].local )
{
src << " vec3 dist = vec3( 1.0, ad, ad * ad );\n";
src << " att = 1.0 / dot( dist, rglLight" << i << "[ LE_ATTEN ].xyz );\n";
}
if ( st.light[ i ].spotlight )
{
src << " float spcut = cos( radians( rglLight" << i << "[ LE_SPOTDIR ].w ) );\n";
src << " float sd = dot( rglLight" << i << "[ LE_SPOTDIR ].xyz, lvec.xyz );\n";
src << " att *= ( sd > spcut ) ? exp( rglLight" << i << "[ LE_ATTEN ].w * log( sd ) ) : 0.0;\n";
}
if ( st.lightModelSeparateSpecular )
{
src << " rglFrontColor.xyz += " << attenmul << " ( ambient + diffuse );\n";
src << " rglSCOL0.xyz += " << attenmul << " specular;\n";
}
else
{
src << " rglFrontColor.xyz += " << attenmul << " ( ambient + diffuse + specular );\n";
}
if ( st.lightModelTwoSide )
{
src << " ambient = rglLight" << i << "[ LE_AMBIENT ].xyz * mBack[ ME_AMBIENT ].xyz;\n";
src << " dc = max( dot( -en, lvec.xyz ), 0.0 );\n";
src << " sc = max( dot( -en, hvec ), 0.0 );\n";
src << " diffuse = dc * rglLight" << i << "[ LE_DIFFUSE ].xyz * mBack[ ME_DIFFUSE ].xyz;\n";
src << " sc = ( dc > 0.0 && sc > 0.0 ) ? exp( mBack[ ME_SHININESS ].x * log( sc ) ) : 0.0;\n";
src << " specular = sc * rglLight" << i << "[ LE_SPECULAR ].xyz * mBack[ ME_SPECULAR ].xyz;\n";
if ( st.lightModelSeparateSpecular )
{
src << " rglBackColor.xyz += " << attenmul << " ( ambient + diffuse );\n";
src << " rglSCOL1.xyz += " << attenmul << " specular;\n";
}
else
{
src << " rglBackColor.xyz += " << attenmul << " ( ambient + diffuse + specular );\n";
}
}
src << " }\n";
}
}
}
else
{
src << " rglFrontColor = rglColor;\n";
if ( st.colorSum )
{
src << " rglSCOL0 = rglSecondaryColor;\n";
}
}
// clamp all the output colors to (0.0, 1.0)
src << " rglFrontColor = clamp( rglFrontColor, 0.0, 1.0 );\n";
if ( st.lighting )
{
if ( st.lightModelTwoSide )
{
src << " rglBackColor = clamp( rglBackColor, 0.0, 1.0 );\n";
}
if ( st.lightModelSeparateSpecular )
{
src << " rglSCOL0 = clamp( rglSCOL0, 0.0, 1.0 );\n";
if ( st.lightModelTwoSide )
{
src << " rglSCOL1 = clamp( rglSCOL1, 0.0, 1.0 );\n";
}
}
}
else if( st.colorSum )
{
src << " rglSCOL0 = clamp( rglSCOL0, 0.0, 1.0 );\n";
}
if ( st.fog.enable )
{
if ( st.fog.useDepth )
{
src << " rglFOG = eh;\n";
}
else
{
src << " rglFOG = vec4(0, 0, -rglFogCoord, 1);\n";
}
}
if ( hasNormalMap )
{
src << " vec4 nmTc = ComputeNormalMap( en );\n";
}
if ( hasReflectionMap )
{
src << " vec4 rmTc = ComputeReflectionMap( -ev, en );\n";
}
if ( hasSphereMap )
{
src << " vec4 smTc = ComputeSphereMap( -ev, en );\n";
}
bool tc_declared = false;
n = array_size( state.processed.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( state.processed.tex, i );
const State::Texture & t = state.processed.tex[i];
if ( t.enables == 0 )
{
continue;
}
if ( !tc_declared )
{
src << " vec4 tc;\n";
tc_declared = true;
}
src << " tc = rglMultiTexCoord" << i << ";\n";
const char *comp[] = { "x", "y", "z", "w" };
const char *tc[] = { "S", "T", "R", "Q" };
for ( size_t j = 0; j < 4; j++ )
{
RegalAssertArrayIndex( t.texgen, j );
if ( t.texgen[j].enable == false )
{
continue;
}
switch( t.texgen[j].mode )
{
case Iff::TG_ObjectLinear:
src << " tc." << comp[j] << " = dot( rglTexGen" << i << "Obj" << tc[j] << ", rglVertex );\n";
break;
case Iff::TG_EyeLinear:
src << " tc." << comp[j] << " = dot( rglTexGen" << i << "Eye" << tc[j] << ", eh );\n";
break;
case Iff::TG_NormalMap:
src << " tc." << comp[j] << " = nmTc." << comp[j] << ";\n";
break;
case Iff::TG_ReflectionMap:
src << " tc." << comp[j] << " = rmTc." << comp[j] << ";\n";
break;
case Iff::TG_SphereMap:
src << " tc." << comp[j] << " = smTc." << comp[j] << ";\n";
break;
default:
src << "//ERROR: unsupported tex gen mode\n";
break;
}
}
if ( t.useMatrix )
{
src << " rglTEXCOORD" << i << " = rglTextureMatrix" << i << " * tc;\n";
}
else
{
src << " rglTEXCOORD" << i << " = tc;\n";
}
}
n = array_size( st.clipPlaneEnabled );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.clipPlaneEnabled, i );
if ( st.clipPlaneEnabled[i] )
{
if ( gles || legacy )
{
src << " rglClipDistance" << i;
}
else
{
src << " gl_ClipDistance[" << i << "]";
}
src << " = dot( eh, rglClipPlane" << i << " );\n";
}
}
src << "}\n";
}
static void AddTexEnv( size_t i, Iff::TexenvMode env, GLenum fmt, string_list & s )
{
Internal("Regal::Iff::AddTexEnv","()");
switch( env )
{
case Iff::TEM_Replace:
switch( fmt )
{
case GL_ALPHA:
s << " p.w = s.w;\n";
break;
case GL_LUMINANCE:
case GL_RGB:
s << " p.xyz = s.xyz;\n";
break;
case GL_LUMINANCE_ALPHA:
case GL_RGBA:
s << " p = s;\n";
break;
default:
s << " p = s; //ERROR: Unsupported tex fmt " << Token::GLenumToString(fmt) << "\n";
break;
}
break;
case Iff::TEM_Modulate:
switch( fmt )
{
case GL_ALPHA:
s << " p.w *= s.w;\n";
break;
case GL_LUMINANCE:
case GL_RGB:
s << " p.xyz *= s.xyz;\n";
break;
case GL_LUMINANCE_ALPHA:
case GL_RGBA:
s << " p *= s;\n";
break;
default:
s << " p = s; //ERROR: Unsupported tex fmt" << Token::GLenumToString(fmt) << "\n";
break;
}
break;
case Iff::TEM_Blend:
switch( fmt )
{
case GL_ALPHA:
s << " p.w *= s.w;\n";
break;
case GL_LUMINANCE:
case GL_RGB:
s << " p.xyz = mix( p.xyz, rglTexEnvColor" << i << ".xyz, s.xyz );\n";
break;
case GL_LUMINANCE_ALPHA:
case GL_RGBA:
s << " p.xyz = mix( p.xyz, rglTexEnvColor" << i << ".xyz, s.xyz );\n";
s << " p.w *= s.w;\n";
break;
default:
s << " p = s; //ERROR: Unsupported tex fmt " << Token::GLenumToString(fmt) << "\n";
break;
}
break;
case Iff::TEM_Add:
switch( fmt )
{
case GL_ALPHA:
s << " p.w *= s.w;\n";
break;
case GL_LUMINANCE:
case GL_RGB:
s << " p.xyz += s.xyz;\n";
break;
case GL_LUMINANCE_ALPHA:
case GL_RGBA:
s << " p.xyz += s.xyz;\n";
s << " p.w *= s.w;\n";
break;
default:
s << " p = s; //ERROR: Unsupported tex fmt " << Token::GLenumToString(fmt) << "\n";
break;
}
break;
case Iff::TEM_Decal:
switch( fmt )
{
case GL_ALPHA:
case GL_LUMINANCE:
case GL_LUMINANCE_ALPHA:
s << " //ERROR: tex env mode undefined for texture format " << Token::GLenumToString(fmt) << "\n";
break;
case GL_RGB:
s << " p.xyz = s.xyz;\n";
break;
case GL_RGBA:
s << " p.xyz = mix( p.xyz, s.xyz, s.w );\n";
break;
default:
s << " p = s; //ERROR: Unsupported tex fmt " << Token::GLenumToString(fmt) << "\n";
break;
}
break;
default:
s << " //ERROR: Unsupported tex env mode\n";
break;
}
}
static void AddTexEnvCombine( Iff::TextureEnv & env, string_list & s )
{
Internal("Regal::Iff::AddTexEnvCombine","()");
bool skipAlpha = env.rgb.mode == Iff::TEC_Dot3Rgba;
int rgbSources = 0;
int aSources = 0;
s << " {\n";
switch( env.rgb.mode )
{
case Iff::TEC_Replace:
rgbSources = 1;
break;
case Iff::TEC_Modulate:
case Iff::TEC_Add:
case Iff::TEC_AddSigned:
case Iff::TEC_Dot3Rgb:
case Iff::TEC_Dot3Rgba:
rgbSources = 2;
break;
case Iff::TEC_Interpolate:
rgbSources = 3;
break;
default:
break;
}
if ( skipAlpha == false )
{
switch( env.a.mode )
{
case Iff::TEC_Replace:
aSources = 1;
break;
case Iff::TEC_Modulate:
case Iff::TEC_Add:
case Iff::TEC_AddSigned:
case Iff::TEC_Subtract:
case Iff::TEC_Dot3Rgb:
case Iff::TEC_Dot3Rgba:
aSources = 2;
break;
case Iff::TEC_Interpolate:
aSources = 3;
break;
default:
break;
}
}
for ( int i = 0; i < rgbSources; i++ )
{
bool skip = false;
string source;
Iff::TexenvCombineSrc src = i == 0 ? env.rgb.src0 : i == 1 ? env.rgb.src1 : env.rgb.src2;
switch( src )
{
case Iff::TCS_PrimaryColor:
source = "rglFrontColor";
break;
case Iff::TCS_Constant:
source = "rglConstantColor";
break;
case Iff::TCS_Previous:
source = "p";
break;
case Iff::TCS_Texture:
source = "s";
break;
default:
skip = true;
break;
}
string suffix;
Iff::TexenvCombineOp op = i == 0 ? env.rgb.op0 : i == 1 ? env.rgb.op1 : env.rgb.op2;
switch( op )
{
case Iff::TCO_Alpha:
case Iff::TCO_OneMinusAlpha:
suffix = ".www";
break;
case Iff::TCO_Color:
case Iff::TCO_OneMinusColor:
suffix = ".xyz";
break;
default:
skip = true;
break;
}
if ( skip )
{
s << " vec3 csrc" << i << " = vec3(0.0f, 0.0f, 0.0f);\n";
}
else
{
s << " vec3 csrc" << i << " = ";
if ( op == Iff::TCO_OneMinusColor || op == Iff::TCO_OneMinusAlpha )
{
s << "1 - ";
}
s << source << suffix << ";\n";
}
}
switch( env.rgb.mode )
{
case Iff::TEC_Replace:
s << " p.xyz = csrc0;\n";
break;
case Iff::TEC_Modulate:
s << " p.xyz = csrc0 * csrc1;\n";
break;
case Iff::TEC_Add:
s << " p.xyz = csrc0 + csrc1;\n";
break;
case Iff::TEC_AddSigned:
s << " p.xyz = csrc0 + csrc1 - 0.5;\n";
break;
case Iff::TEC_Subtract:
s << " p.xyz = csrc0 - csrc1;\n";
break;
case Iff::TEC_Dot3Rgb:
case Iff::TEC_Dot3Rgba:
s << " p.xyz = dot( ( 2.0 * csrc0 - 1.0 ), ( 2.0 * csrc1 - 1.0 ) );\n";
break;
case Iff::TEC_Interpolate:
s << " p.xyz = mix( csrc0, csrc1, csrc2);\n";
break;
default:
s << "//ERROR: Unsupported tex env combine rgb mode\n";
break;
break;
}
if ( env.rgb.scale != 1.0 )
s << " p.xyz = clamp(" << env.rgb.scale << " * p.xyz, 0.0, 1.0);\n";
if ( skipAlpha )
{
s << " p.w = p.x;\n";
}
else
{
for ( int i = 0; i < aSources; i++ )
{
bool skip = false;
string source;
Iff::TexenvCombineSrc src = i == 0 ? env.a.src0 : i == 1 ? env.a.src1 : env.a.src2;
switch( src )
{
case Iff::TCS_PrimaryColor:
source = "rglFrontColor";
break;
case Iff::TCS_Constant:
source = "rglConstantColor";
break;
case Iff::TCS_Previous:
source = "p";
break;
case Iff::TCS_Texture:
source = "s";
break;
default:
skip = true;
break;
}
if ( skip )
{
s << " float asrc" << i << " = 0.0f;\n";
}
else
{
Iff::TexenvCombineOp op = i == 0 ? env.a.op0 : i == 1 ? env.a.op1 : env.a.op2;
s << " float asrc" << i << " = ";
if ( op == Iff::TCO_OneMinusAlpha )
{
s << "1 - ";
}
s << source << ".w;\n";
}
}
switch( env.a.mode )
{
case Iff::TEC_Replace:
s << " p.w = asrc0;\n";
break;
case Iff::TEC_Modulate:
s << " p.w = asrc0 * asrc1;\n";
break;
case Iff::TEC_Add:
s << " p.w = asrc0 + asrc1;\n";
break;
case Iff::TEC_AddSigned:
s << " p.w = asrc0 + asrc1 - 0.5;\n";
break;
case Iff::TEC_Subtract:
s << " p.w = asrc0 - asrc1;\n";
break;
case Iff::TEC_Interpolate:
s << " p.w = mix( asrc0, asrc1, asrc2 );\n";
break;
default:
s << "//ERROR: Unsupported tex env combine alpha mode\n";
break;
break;
}
if ( env.a.scale != 1.0 )
s << " p.w = clamp(" << env.a.scale << " * p.w, 0.0, 1.0);\n";
}
s << " }\n";
}
static string TargetSuffix( Iff::TextureTargetBitfield ttb )
{
switch( ttb )
{
case Iff::TT_1D:
return "1D";
case Iff::TT_2D:
return "2D";
case Iff::TT_Rect:
return "Rect";
case Iff::TT_3D:
return "3D";
case Iff::TT_CubeMap:
return "Cube";
default:
break;
}
return "";
}
static string TextureFetch( bool es, bool legacy, Iff::TextureTargetBitfield b )
{
if ( es || legacy )
{
switch( b )
{
case Iff::TT_1D:
return "texture1D";
case Iff::TT_2D:
return "texture2D";
case Iff::TT_CubeMap:
return "textureCube";
default:
break;
}
}
return "texture";
}
static string TextureFetchSwizzle( bool es, bool legacy, Iff::TextureTargetBitfield b )
{
if ( es || legacy )
{
switch( b )
{
case Iff::TT_1D:
return ".x";
case Iff::TT_2D:
return ".xy";
case Iff::TT_Rect:
return ".xy";
case Iff::TT_3D:
return ".xyz";
case Iff::TT_CubeMap:
return ".xyz";
default:
break;
}
return "";
}
switch( b )
{
case Iff::TT_1D:
return ".x";
case Iff::TT_2D:
return ".xy";
case Iff::TT_Rect:
return ".xy";
case Iff::TT_3D:
return ".xyz";
case Iff::TT_CubeMap:
return ".xyz";
default:
break;
}
return "";
}
static void GenerateFragmentShaderSource( Iff * rff, string_list &src )
{
Internal("Regal::Iff::GenerateFragmentShaderSource", boost::print::optional(rff,Logging::pointers));
const Store & st = rff->ffstate.processed;
if ( rff->gles )
{
#if REGAL_FORCE_DESKTOP_GLSL
src << "#version 140\n";
#else
src << "#version 100\n";
#endif
}
else if( rff->legacy )
{
src << "#version 120\n";
}
else
{
src << "#version 140\n";
}
src << "// program number " << rff->progcount << "\n";
if ( rff->gles || rff->legacy )
{
src << "#define in varying\n";
src << "#define rglFragColor gl_FragColor\n";
}
else
{
src << "out vec4 rglFragColor;\n";
}
if ( st.shadeModelFlat && ! rff->legacy && ! rff->gles )
{
src << "#define FLAT flat\n";
}
else
{
src << "#define FLAT \n";
}
if ( rff->gles )
{
src << "precision highp float;\n";
}
src << "FLAT in vec4 rglFrontColor;\n";
if ( st.lighting )
{
if ( st.lightModelTwoSide )
{
src << "FLAT in vec4 rglBackColor;\n";
}
if ( st.lightModelSeparateSpecular )
{
src << "in vec4 rglSCOL0;\n";
if ( st.lightModelTwoSide )
{
src << "in vec4 rglSCOL1;\n";
}
}
}
else if( st.colorSum )
{
src << "in vec4 rglSCOL0;\n";
}
if ( st.fog.enable )
{
src << "uniform vec4 rglFog[2];\n";
src << "in vec4 rglFOG;\n";
}
bool needsConstantColor = false;
size_t n = array_size( rff->ffstate.processed.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( rff->ffstate.processed.tex, i );
Texture t = rff->ffstate.processed.tex[i];
if ( t.enables == 0 )
{
continue;
}
src << "uniform sampler" << TargetSuffix( static_cast<Iff::TextureTargetBitfield>(t.enables) ) << " rglSampler" << i << ";\n";
src << "in vec4 rglTEXCOORD" << i << ";\n";
Iff::TextureEnv & env = t.unit.env;
if ( env.mode == Iff::TEM_Combine )
{
needsConstantColor =
env.rgb.src0 == Iff::TCS_Constant ||
env.rgb.src1 == Iff::TCS_Constant ||
env.rgb.src2 == Iff::TCS_Constant ;
}
if ( env.mode == Iff::TEM_Blend )
{
src << "uniform vec4 rglTexEnvColor" << i << ";\n";
}
}
if ( needsConstantColor )
{
src << "uniform vec4 rglConstantColor;\n";
}
n = array_size( st.clipPlaneEnabled );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.clipPlaneEnabled, i );
if ( ( rff->gles || rff->legacy ) && st.clipPlaneEnabled[i] )
{
src << "in float rglClipDistance" << i << ";\n";
}
}
if ( st.alphaTest.enable )
{
if (st.alphaTest.comp != Iff::CF_Never && st.alphaTest.comp != Iff::CF_Always )
{
src << "uniform vec2 rglAlphaRef;\n";
}
}
src << "void main() {\n";
if ( rff->gles || rff->legacy )
{
n = array_size( st.clipPlaneEnabled );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( st.clipPlaneEnabled, i );
if ( st.clipPlaneEnabled[i] )
{
src << " if( rglClipDistance" << i << " < 0.0 ) discard;\n";
}
}
}
if ( st.lighting && st.lightModelTwoSide )
{
src << " vec4 p = gl_FrontFacing ? rglFrontColor : rglBackColor;\n";
}
else
{
src << " vec4 p = rglFrontColor;\n";
}
bool s_declared = false;
n = array_size( rff->ffstate.processed.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( rff->ffstate.processed.tex, i );
Texture t = rff->ffstate.processed.tex[ i ];
Iff::TextureTargetBitfield b = rff->ffstate.GetTextureEnable( i );
if ( b == 0 )
{
continue;
}
if ( !s_declared )
{
src << " vec4 s;\n";
s_declared = true;
}
switch( b )
{
case Iff::TT_1D:
case Iff::TT_2D:
{
src << " s = " << TextureFetch( rff->gles, rff->legacy, b )
<< "( rglSampler" << i << ", rglTEXCOORD" << i
<< TextureFetchSwizzle( rff->gles, rff->legacy, b ) << " / rglTEXCOORD" << i << ".w );\n";
break;
}
case Iff::TT_CubeMap:
{
src << " s = " << TextureFetch( rff->gles, rff->legacy, b )
<< "( rglSampler" << i << ", rglTEXCOORD" << i
<< TextureFetchSwizzle( rff->gles, rff->legacy, b ) << " );\n";
break;
}
default:
src << "// ERROR: Unsupported texture target\n";
src << " s = vec4( 0.0f, 0.0f, 0.0f, 0.0f );\n";
break;
}
switch( t.unit.env.mode )
{
case Iff::TEM_Replace:
case Iff::TEM_Modulate:
case Iff::TEM_Add:
case Iff::TEM_Decal:
case Iff::TEM_Blend:
AddTexEnv( i, t.unit.env.mode, t.unit.fmt, src );
break;
case Iff::TEM_Combine:
AddTexEnvCombine( t.unit.env, src );
break;
default:
src << "//ERROR: Unsupported texture env mode\n";
break;
}
}
src << " rglFragColor = p;\n";
if ( st.lighting )
{
if ( st.lightModelSeparateSpecular )
{
if ( st.lightModelTwoSide )
{
src << " rglFragColor += gl_FrontFacing ? rglSCOL0 : rglSCOL1;\n";
}
else
{
src << " rglFragColor += rglSCOL0;\n";
}
}
}
else if( st.colorSum )
{
src << " rglFragColor.xyz += rglSCOL0.xyz;\n";
}
if ( st.fog.enable )
{
src << " float f = abs( -rglFOG.z / rglFOG.w );\n";
switch( st.fog.mode )
{
case Iff::FG_Linear:
src << " float fogFactor = ( rglFog[0].z - f ) / ( rglFog[0].z - rglFog[0].y );\n";
break;
case Iff::FG_Exp:
src << " float fogFactor = exp( -( rglFog[0].x * f ) );\n";
break;
case Iff::FG_Exp2:
src << " float fogFactor = exp( -( rglFog[0].x * rglFog[0].x * f * f ) );\n";
break;
default:
src << "//ERROR: Unsupported fog mode\n";
src << " float fogFactor = 0.0f;\n";
break;
}
src << " fogFactor = clamp( fogFactor, 0.0, 1.0 );\n";
src << " rglFragColor.xyz = mix( rglFog[1].xyz, rglFragColor.xyz, fogFactor );\n";
}
if ( st.alphaTest.enable )
{
switch( st.alphaTest.comp )
{
case Iff::CF_Never:
src << " discard;\n";
break;
case Iff::CF_Less:
src << " if( rglFragColor.w >= rglAlphaRef.x ) discard;\n";
break;
case Iff::CF_Greater:
src << " if( rglFragColor.w <= rglAlphaRef.x ) discard;\n";
break;
case Iff::CF_Lequal:
src << " if( rglFragColor.w > rglAlphaRef.x ) discard;\n";
break;
case Iff::CF_Gequal:
src << " if( rglFragColor.w < rglAlphaRef.x ) discard;\n";
break;
case Iff::CF_Equal:
src << " if( rglFragColor.w != rglAlphaRef.x ) discard;\n";
break;
case Iff::CF_NotEqual:
src << " if( rglFragColor.w == rglAlphaRef.x ) discard;\n";
break;
case Iff::CF_Always:
break;
default:
src << "//ERROR: Unsupported alpha comp func\n";
break;
}
}
src << "}\n";
}
static void Copy( Float4 & dst, const GLfloat * src )
{
dst.x = src[0];
dst.y = src[1];
dst.z = src[2];
dst.w = src[3];
}
static void Copy( GLfloat * dst, Float4 & src )
{
dst[0] = src.x;
dst[1] = src.y;
dst[2] = src.z;
dst[3] = src.w;
}
static void Transform( Float4 & dst, const r3::Matrix4f & m, const GLfloat * src )
{
r3::Vec4f v( src );
m.MultMatrixVec( v );
dst.x = v.x;
dst.y = v.y;
dst.z = v.z;
dst.w = v.w;
}
// Not currently used
#if 0
void TransformDir( Float4 & dst, const r3::Matrix4f & m, const GLfloat * src )
{
r3::Vec3f v( src );
m.MultMatrixDir( v );
v.Normalize();
dst.x = v.x;
dst.y = v.y;
dst.z = v.z;
}
#endif
static r3::Matrix4f RescaleNormal( const r3::Matrix4f & m )
{
r3::Matrix4f r = m;
for ( int i = 0; i < 3; i++ )
{
r3::Vec3f v( r( i, 0 ), r( i, 1 ), r( i, 2 ) );
v.Normalize();
r( i, 0 ) = v.x;
r( i, 1 ) = v.y;
r( i, 2 ) = v.z;
}
return r;
}
bool State::SetEnable( Iff * ffn, bool enable, GLenum cap )
{
Internal("Regal::Iff::State::SetEnable",enable," ",Token::GLenumToString(cap));
Iff::Version & ver = ffn->ver;
int shift = 0;
switch( cap )
{
case GL_TEXTURE_1D:
shift = TP_1D;
break;
case GL_TEXTURE_2D:
shift = TP_2D;
break;
case GL_TEXTURE_RECTANGLE:
shift = TP_Rect;
break;
case GL_TEXTURE_3D:
shift = TP_3D;
break;
case GL_TEXTURE_CUBE_MAP:
shift = TP_CubeMap;
break;
case GL_COLOR_SUM:
raw.ver = ver.Update();
raw.colorSum = enable;
return true;
case GL_FOG:
raw.ver = ver.Update();
raw.fog.enable = enable;
return true;
case GL_LIGHTING:
raw.ver = ver.Update();
raw.lighting = enable;
return true;
case GL_LIGHT0:
case GL_LIGHT1:
case GL_LIGHT2:
case GL_LIGHT3:
case GL_LIGHT4:
case GL_LIGHT5:
case GL_LIGHT6:
case GL_LIGHT7:
raw.ver = ver.Update();
RegalAssertArrayIndex( raw.light, cap - GL_LIGHT0 );
raw.light[ cap - GL_LIGHT0 ].enable = enable;
return true;
case GL_COLOR_MATERIAL:
raw.ver = ver.Update();
raw.colorMaterial = enable;
return true;
case GL_RESCALE_NORMAL:
raw.ver = ver.Update();
raw.rescaleNormal = enable;
return true;
case GL_NORMALIZE:
raw.ver = ver.Update();
raw.normalize = enable;
return true;
case GL_TEXTURE_GEN_S:
case GL_TEXTURE_GEN_T:
case GL_TEXTURE_GEN_R:
case GL_TEXTURE_GEN_Q:
{
int activeTex = ffn->activeTextureIndex;
if ( activeTex < REGAL_EMU_MAX_TEXTURE_UNITS )
{
raw.ver = ver.Update();
int idx = cap - GL_TEXTURE_GEN_S;
RegalAssertArrayIndex( raw.tex, activeTex );
RegalAssertArrayIndex( raw.tex[ activeTex ].texgen, idx );
raw.tex[ activeTex ].texgen[ idx ].enable = enable;
}
return true;
}
case GL_CLIP_PLANE0:
case GL_CLIP_PLANE1:
case GL_CLIP_PLANE2:
case GL_CLIP_PLANE3:
case GL_CLIP_PLANE4:
case GL_CLIP_PLANE5:
case GL_CLIP_PLANE0+6:
case GL_CLIP_PLANE0+7:
raw.ver = ver.Update();
RegalAssertArrayIndex( raw.clipPlaneEnabled, cap - GL_CLIP_PLANE0 );
raw.clipPlaneEnabled[ cap - GL_CLIP_PLANE0 ] = enable;
return true;
case GL_ALPHA_TEST:
uniform.alphaTest.alphaTestEnable = enable ? GLfloat(1) : GLfloat(0);
uniform.alphaTest.ver = uniform.ver = raw.ver = ver.Update();
raw.alphaTest.enable = enable;
return true;
default:
return false;
}
int activeTex = ffn->activeTextureIndex;
if ( activeTex >= REGAL_EMU_MAX_TEXTURE_UNITS )
{
// track state for all texture units, but bail before fixed function
// texture unit tracking
return true;
}
RegalAssertArrayIndex( raw.tex, activeTex );
Texture & t = raw.tex[ activeTex ];
GLuint v = 1 << shift;
if ( enable )
{
t.enables |= v;
}
else
{
t.enables &= ~v;
}
Internal("Regal::Iff::State::SetEnable","activeTex=",activeTex," enables=",t.enables);
raw.ver = ver.Update();
return true;
}
void State::SetTexInfo( Iff::Version & ver, GLuint activeTex, Iff::TextureUnit & unit )
{
if (activeTex >= REGAL_EMU_MAX_TEXTURE_UNITS)
return;
raw.tex[ activeTex ].unit = unit;
raw.ver = ver.Update();
}
void State::SetLight( Iff * ffn, GLenum light, GLenum pname, const GLfloat * params )
{
Internal("Regal::Iff::State::SetLight",light,Token::GLenumToString(pname));
Iff::Version & ver = ffn->ver;
int idx = light - GL_LIGHT0;
if ( idx < 0 || idx >= REGAL_FIXED_FUNCTION_MAX_LIGHTS )
{
return;
}
RegalAssertArrayIndex( uniform.light, idx );
State::LightUniform & lu = uniform.light[ idx ];
switch( pname )
{
case GL_AMBIENT:
Copy( lu.ambient, params );
break;
case GL_DIFFUSE:
Copy( lu.diffuse, params );
break;
case GL_SPECULAR:
Copy( lu.specular, params );
break;
case GL_POSITION:
{
Transform( lu.position, ffn->modelview.Top(), params );
if (lu.position.w == 0.0)
{
lu.position.w = 1.0f/sqrtf(lu.position.x * lu.position.x +
lu.position.y * lu.position.y +
lu.position.z * lu.position.z);
lu.position.x *= lu.position.w;
lu.position.y *= lu.position.w;
lu.position.z *= lu.position.w;
lu.position.w = 0.0;
RegalAssertArrayIndex( raw.light, idx );
raw.light[ idx ].local = false;
}
else
{
lu.position.x /= lu.position.w;
lu.position.y /= lu.position.w;
lu.position.z /= lu.position.w;
lu.position.w = 1.0;
RegalAssertArrayIndex( raw.light, idx );
raw.light[ idx ].local = true;
}
break;
}
case GL_SPOT_DIRECTION:
{
Float4 spdir( params[0], params[1], params[2], 1.0f );
r3::Matrix4f & m = ffn->modelview.Top();
float x = m.element(0,0) * spdir.x + m.element(0,1) * spdir.y + m.element(0,2) * spdir.z;
float y = m.element(1,0) * spdir.x + m.element(1,1) * spdir.y + m.element(1,2) * spdir.z;
float z = m.element(2,0) * spdir.x + m.element(2,1) * spdir.y + m.element(2,2) * spdir.z;
float d = -1.0f/sqrtf(x*x + y*y + z*z);
lu.spotDirection.x = x * d;
lu.spotDirection.y = y * d;
lu.spotDirection.z = z * d;
}
break;
case GL_SPOT_EXPONENT:
lu.attenuation.w = params[0];
break;
case GL_SPOT_CUTOFF:
lu.spotDirection.w = params[0];
break;
case GL_CONSTANT_ATTENUATION:
lu.attenuation.x = params[0];
break;
case GL_LINEAR_ATTENUATION:
lu.attenuation.y = params[0];
break;
case GL_QUADRATIC_ATTENUATION:
lu.attenuation.z = params[0];
break;
default:
return;
}
// some raw state is derived from uniform state
bool updateRawVersion = false;
switch( pname )
{
case GL_POSITION:
case GL_SPOT_CUTOFF:
case GL_CONSTANT_ATTENUATION:
case GL_LINEAR_ATTENUATION:
case GL_QUADRATIC_ATTENUATION:
updateRawVersion = true;
break;
default:
break;
}
lu.ver = uniform.ver = ver.Update();
if ( updateRawVersion )
{
raw.ver = uniform.ver;
}
}
void State::SetMaterial( Iff * ffn, GLenum face, GLenum pname, const GLfloat * params )
{
Internal("Regal::Iff::State::SetMaterial",face,Token::GLenumToString(pname));
Iff::Version & ver = ffn->ver;
for ( int i = 0; i < 2; i++ )
{
if ( ( i == 0 && face == GL_BACK ) || ( i == 1 && face == GL_FRONT ) )
{
continue;
}
RegalAssertArrayIndex( uniform.mat, i );
State::MaterialUniform & m = uniform.mat[ i ];
switch( pname )
{
case GL_AMBIENT:
Copy( m.ambient, params );
break;
case GL_DIFFUSE:
Copy( m.diffuse, params );
break;
case GL_AMBIENT_AND_DIFFUSE:
Copy( m.ambient, params );
m.diffuse = m.ambient;
break;
case GL_SPECULAR:
Copy( m.specular, params );
break;
case GL_EMISSION:
Copy( m.emission, params );
break;
case GL_SHININESS:
m.shininess.x = params[0];
break;
default:
return;
}
m.ver = uniform.ver = ver.Update();
}
}
void State::GetMaterial( Iff * ffn, GLenum face, GLenum pname, GLfloat * params )
{
Internal("Regal::Iff::State::GetMaterial",face,Token::GLenumToString(pname));
UNUSED_PARAMETER(ffn);
for ( int i = 0; i < 2; i++ )
{
if ( ( i == 0 && face == GL_BACK ) || ( i == 1 && face == GL_FRONT ) )
{
continue;
}
RegalAssertArrayIndex( uniform.mat, i );
State::MaterialUniform & m = uniform.mat[ i ];
switch( pname )
{
case GL_AMBIENT:
Copy( params, m.ambient );
break;
case GL_DIFFUSE:
Copy( params, m.diffuse );
break;
case GL_SPECULAR:
Copy( params, m.specular );
break;
case GL_EMISSION:
Copy( params, m.emission );
break;
case GL_SHININESS:
params[0] = m.shininess.x;
break;
default:
return;
}
}
}
void State::SetTexgen( Iff * ffn, int coord, GLenum space, const GLfloat * params )
{
Internal("Regal::Iff::State::SetTexgen",ffn,coord,toString(space),boost::print::array(params,4));
r3::Matrix4f ident;
RegalAssertArrayIndex( uniform.tex, ffn->activeTextureIndex );
RegalAssertArrayIndex( uniform.tex[ ffn->activeTextureIndex ].texgen, coord );
TexgenUniform & tgu = uniform.tex[ ffn->activeTextureIndex ].texgen[ coord ];
GLuint64 *tguver = NULL;
switch( space )
{
case GL_OBJECT_PLANE:
Transform( tgu.obj, ident, params );
tguver = & tgu.objVer;
break;
case GL_EYE_PLANE:
Transform( tgu.eye, ffn->modelview.Top().Inverse().Transpose(), params );
tguver = & tgu.eyeVer;
break;
default:
return;
}
*tguver = uniform.ver = ffn->ver.Update();
}
void State::GetTexgen( Iff * ffn, int coord, GLenum space, GLfloat * params )
{
Internal("Regal::Iff::State::GetTexgen", boost::print::optional(ffn,Logging::pointers)," ",coord," ",toString(space));
RegalAssertArrayIndex( uniform.tex, ffn->activeTextureIndex );
RegalAssertArrayIndex( uniform.tex[ ffn->activeTextureIndex ].texgen, coord );
TexgenUniform & tgu = uniform.tex[ ffn->activeTextureIndex ].texgen[ coord ];
switch( space )
{
case GL_OBJECT_PLANE:
params[0] = tgu.obj.x;
params[1] = tgu.obj.y;
params[2] = tgu.obj.z;
params[3] = tgu.obj.w;
break;
case GL_EYE_PLANE:
params[0] = tgu.eye.x;
params[1] = tgu.eye.y;
params[2] = tgu.eye.z;
params[3] = tgu.eye.w;
break;
default:
break;
}
}
void State::SetAlphaFunc( Iff * ffn, Iff::CompareFunc comp, GLfloat alphaRef )
{
raw.alphaTest.comp = comp;
uniform.alphaTest.alphaRef = std::min( 1.0f, std::max( 0.0f, alphaRef ) );
uniform.alphaTest.ver = uniform.ver = raw.ver = ffn->ver.Update();
}
void State::SetClip( Iff * ffn, GLenum plane, const GLfloat * equation )
{
int idx = plane - GL_CLIP_PLANE0;
if ( idx >= REGAL_FIXED_FUNCTION_MAX_CLIP_PLANES )
{
return;
}
RegalAssertArrayIndex( uniform.clip, idx );
Transform( uniform.clip[ idx ].plane, ffn->modelview.Top().Inverse().Transpose(), equation );
uniform.clip[ idx ].ver = uniform.ver = ffn->ver.Update();
}
Iff::TextureTargetBitfield State::GetTextureEnable( size_t unit ) const
{
return TextureTargetBitfield( ( processed.tex[unit].enables ) );
}
GLuint64 State::Ver() const
{
return uniform.ver;
}
GLuint State::HighestPriorityTextureEnable( GLuint enables )
{
for (int i = TP_CubeMap; i >= 0; i--)
{
if (enables & ( 1 << i ))
return static_cast<GLubyte>(1 << i);
}
return 0;
}
void Program::Init( RegalContext * ctx, const Store & sstore, GLuint vshd, GLuint fshd )
{
Internal("Regal::Iff::Program::Init","()");
// update emu info with the limits that this layer supports
RegalAssert(ctx);
RegalAssert(ctx->emuInfo);
ctx->emuInfo->gl_max_texture_coords = REGAL_EMU_MAX_TEXTURE_COORDS;
ctx->emuInfo->gl_max_vertex_attribs = REGAL_EMU_MAX_VERTEX_ATTRIBS;
ctx->emuInfo->gl_max_texture_units = REGAL_EMU_MAX_TEXTURE_UNITS;
ver = ::std::numeric_limits<GLuint64>::max();
progcount = 0;
RegalAssert(ctx);
DispatchTableGL & tbl = ctx->dispatcher.emulation;
store = sstore;
pg = tbl.call(&tbl.glCreateProgram)();
vs = vshd;
tbl.call(&tbl.glAttachShader)(pg, vs );
fs = fshd;
tbl.call(&tbl.glAttachShader)(pg, fs );
Attribs( ctx );
tbl.call(&tbl.glLinkProgram)( pg );
#ifndef NDEBUG
GLint status = 0;
tbl.call(&tbl.glGetProgramiv)( pg, GL_LINK_STATUS, &status );
if (!status)
{
std::string log;
if (helper::getInfoLog(log,tbl.call(&tbl.glGetProgramInfoLog),tbl.call(&tbl.glGetProgramiv),pg))
Warning( "Regal::Program::Init", log);
}
#endif
tbl.call(&tbl.glUseProgram)( pg );
Samplers( ctx, tbl );
Uniforms( ctx, tbl );
tbl.call(&tbl.glUseProgram)( ctx->iff->program );
}
void Program::Shader( RegalContext * ctx, DispatchTableGL & tbl, GLenum type, GLuint & shader, const GLchar *src )
{
Internal("Regal::Iff::Program::Shader","()");
UNUSED_PARAMETER(ctx);
const GLchar *srcs[] = { src };
GLint len[] = { 0 };
len[0] = (GLint)strlen( src );
shader = tbl.call(&tbl.glCreateShader)(type);
tbl.call(&tbl.glShaderSource)( shader, 1, srcs, len );
tbl.call(&tbl.glCompileShader)( shader );
#ifndef NDEBUG
GLint status = 0;
tbl.call(&tbl.glGetShaderiv)( shader, GL_COMPILE_STATUS, &status );
if (!status)
{
std::string log;
if (helper::getInfoLog(log,tbl.call(&tbl.glGetShaderInfoLog),tbl.call(&tbl.glGetShaderiv),shader))
Warning("Regal::Program::Shader", log);
}
#endif
}
void Program::Attribs( RegalContext * ctx )
{
Internal("Regal::Iff::Program::Attribs","()");
DispatchTableGL & tbl = ctx->dispatcher.emulation;
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Vertex );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_Vertex ], "rglVertex" );
//tbl.call(&tbl.glBindAttribLocation)( pg, 1, "rglWeight" );
if ( store.lighting )
{
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Normal );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_Normal ], "rglNormal" );
}
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Color );
if ( store.attrArrayFlags & ( 1 << ctx->iff->ffAttrMap[ RFF2A_Color ] ) && ( store.lighting == false || store.colorMaterial ) )
{
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Color );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_Color ], "rglColor" );
}
if ( store.colorSum && store.lighting == false )
{
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_SecondaryColor );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_SecondaryColor ], "rglSecondaryColor" );
}
if ( store.fog.enable && store.fog.useDepth == false )
{
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_FogCoord );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_FogCoord ], "rglFogCoord" );
}
GLuint units = std::min( (GLuint)ctx->iff->ffAttrNumTex, (GLuint)REGAL_EMU_MAX_TEXTURE_UNITS );
for ( GLuint i = 0; i < units; i++ )
{
#ifndef REGAL_HACK_SET_001
RegalAssertArrayIndex( store.tex, i );
if ( store.tex[i].enables == 0 )
{
continue;
}
#endif
string_list ss;
ss << "rglMultiTexCoord" << i;
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrTexBegin + i, ss.str().c_str() );
}
}
// seth: for user program mode just do all the bind attribs, aliasing is OK in GL
void Program::UserShaderModeAttribs( RegalContext * ctx )
{
Internal("Regal::Iff::Program::UserShaderModeAttribs","()");
DispatchTableGL & tbl = ctx->dispatcher.emulation;
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Vertex );
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Normal );
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_Color );
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_SecondaryColor );
RegalAssertArrayIndex( ctx->iff->ffAttrMap, RFF2A_FogCoord );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_Vertex ], "rglVertex" );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_Normal ], "rglNormal" );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_Color ], "rglColor" );
if ( ctx->iff->ffAttrMap[ RFF2A_SecondaryColor ] != RFF2A_Invalid )
{
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_SecondaryColor ], "rglSecondaryColor" );
}
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrMap[ RFF2A_FogCoord ], "rglFogCoord" );
GLuint units = std::min( /*(GLuint)ctx->iff->ffAttrNumTex*/(GLuint)16, (GLuint)REGAL_EMU_MAX_TEXTURE_UNITS );
for ( GLuint i = 0; i < units; i++ )
{
RegalAssertArrayIndex( store.tex, i );
string s( "rglMultiTexCoord" );
const char *nums = "0123456789abcdefghijklmnop";
s += string( nums + i, 1 );
tbl.call(&tbl.glBindAttribLocation)( pg, ctx->iff->ffAttrTexBegin + i, s.c_str() );
}
}
void Program::Samplers( RegalContext * ctx, DispatchTableGL & tbl )
{
Internal("Regal::Iff::Program::Samplers","()");
UNUSED_PARAMETER(ctx);
for ( GLint ii = 0; ii < REGAL_EMU_MAX_TEXTURE_UNITS; ii++ )
{
std::string samplerName = boost::print::print_string("rglSampler",ii);
GLint slot = tbl.call(&tbl.glGetUniformLocation)( pg, samplerName.c_str() );
if ( slot >= 0 )
tbl.call(&tbl.glUniform1i)( slot, ii );
}
}
void Iff::UserProgramInstance::LocateUniforms( RegalContext * ctx, DispatchTableGL & tbl ) {
Internal("Regal::Iff::UserProgramInstance::LocateUniforms","()");
UNUSED_PARAMETER(ctx);
size_t n = array_size( regalFFUniformInfo );
for ( size_t i = 1; i < n; i++ )
{
RegalAssertArrayIndex( regalFFUniformInfo, i );
const RegalFFUniformInfo & ri = regalFFUniformInfo[i];
GLint slot = tbl.call(&tbl.glGetUniformLocation)( inst.prog, ri.name );
if (slot > -1)
ffuniforms[ ri.val ] = UniformInfo(~GLuint64(0), slot);
}
}
void Program::Uniforms( RegalContext * ctx, DispatchTableGL & tbl )
{
Internal("Regal::Iff::Program::Uniforms","()");
UNUSED_PARAMETER(ctx);
size_t n = array_size( regalFFUniformInfo );
for ( size_t i = 1; i < n; i++ )
{
RegalAssertArrayIndex( regalFFUniformInfo, i );
const RegalFFUniformInfo & ri = regalFFUniformInfo[i];
GLint slot = tbl.call(&tbl.glGetUniformLocation)( pg, ri.name );
if (slot > -1)
uniforms[ ri.val ] = UniformInfo(~GLuint64(0), slot);
}
}
Iff::Iff()
: progcount(0)
, catIndex(0)
, ffAttrTexBegin(0)
, ffAttrTexEnd(0)
, ffAttrNumTex(0)
, max_vertex_attribs(0)
, immActive(false)
, immProvoking(0)
, immCurrent(0)
, immPrim(GL_POINTS)
, immVbo(0)
, immVboElement(0)
, immVao(0)
, immShadowVao(0)
, shadowMatrixMode(GL_MODELVIEW)
, shadowActiveTextureIndex(0)
, activeTextureIndex(0)
, programPipeline(0)
, program(0)
, currprog(NULL)
, currMatrixStack(&modelview)
, currVao(0)
, gles(false)
, legacy(false)
{
memset(immArray,0,sizeof(immArray));
size_t n = array_size( ffAttrMap );
RegalAssert( array_size( ffAttrInvMap ) == n);
for (size_t i = 0; i < n; i++)
{
ffAttrMap[ i ] = 0;
ffAttrInvMap[ i ] = 0;
}
n = array_size( texture );
RegalAssert( array_size( textureUnit ) == n);
RegalAssert( array_size( textureEnvColor ) == n);
RegalAssert( array_size( textureEnvColorVer ) == n);
RegalAssert( array_size( textureBinding ) >= n);
for (size_t i = 0; i < n; i++)
{
textureEnvColor[ i ] = Float4( 0.0f, 0.0f, 0.0f, 0.0f );
textureEnvColorVer[ i ] = 0;
textureBinding[ i ] = 0;
}
n = array_size( ffprogs );
for (size_t i = 0; i < n; ++i)
ffprogs[ i ] = Program();
}
void Iff::Cleanup( RegalContext &ctx )
{
Internal("Regal::Iff::Cleanup","()");
RestoreVao(&ctx);
DispatchTableGL &tbl = ctx.dispatcher.emulation;
tbl.call(&tbl.glDeleteBuffers)(1, &immVbo);
tbl.call(&tbl.glDeleteBuffers)(1, &immVboElement);
tbl.call(&tbl.glDeleteVertexArrays)(1, &immVao);
size_t n = array_size( ffprogs );
for (size_t i = 0; i < n; ++i)
{
RegalAssertArrayIndex( ffprogs, i );
const Program &pgm = ffprogs[i];
if (pgm.pg)
{
if (&pgm == currprog)
{
tbl.call(&tbl.glUseProgram)(0);
currprog = NULL;
}
tbl.call(&tbl.glDeleteShader)(pgm.vs);
tbl.call(&tbl.glDeleteShader)(pgm.fs);
tbl.call(&tbl.glDeleteProgram)(pgm.pg);
}
}
const bool isWebGLish = (ctx.info->vendor == "Chromium" || ctx.info->webgl);
tbl.glBindBuffer(GL_ARRAY_BUFFER, 0);
tbl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
for (GLuint i=0; i<ctx.emuInfo->gl_max_vertex_attribs; ++i)
{
// Chromium/PepperAPI GLES generates an error (visible through glGetError)
// and logs a message if a call is made to glVertexAttribPointer and no
// GL_ARRAY_BUFFER is bound.
if (!isWebGLish)
tbl.glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, NULL);
tbl.glDisableVertexAttribArray(i);
}
}
void Iff::InitVertexArray(RegalContext &ctx)
{
Internal("Regal::Iff::InitVertexArray","()");
max_vertex_attribs = ctx.emuInfo->gl_max_vertex_attribs;
if (max_vertex_attribs >= 16)
{
RegalAssert( REGAL_EMU_MAX_VERTEX_ATTRIBS == 16);
//RegalOutput( "Setting up for %d Vertex Attribs\n", max_vertex_attribs );
for (size_t i = 0; i < 16; i++)
{
ffAttrMap[i] = RFF2AMap16[i];
ffAttrInvMap[i] = RFF2AInvMap16[i];
}
ffAttrTexBegin = RFF2ATexBegin16;
ffAttrTexEnd = RFF2ATexEnd16;
}
else
{
RegalAssert( max_vertex_attribs >= 8 );
//RegalOutput( "Setting up for 8 Vertex Attribs" );
for (size_t i = 0; i < 8; i++)
{
ffAttrMap[i] = RFF2AMap8[i];
ffAttrInvMap[i] = RFF2AInvMap8[i];
}
for (size_t i = 8; i < REGAL_EMU_MAX_VERTEX_ATTRIBS; i++)
{
ffAttrMap[i] = GLuint(-1);
ffAttrInvMap[i] = GLuint(-1);
}
ffAttrTexBegin = RFF2ATexBegin8;
ffAttrTexEnd = RFF2ATexEnd8;
}
ffAttrNumTex = ffAttrTexEnd - ffAttrTexBegin;
catIndex = 0;
}
GLuint Iff::ClientStateToIndex( GLenum state )
{
switch( state )
{
case GL_VERTEX_ARRAY:
return ffAttrMap[ RFF2A_Vertex ];
case GL_NORMAL_ARRAY:
return ffAttrMap[ RFF2A_Normal ];
case GL_COLOR_ARRAY:
return ffAttrMap[ RFF2A_Color ];
case GL_SECONDARY_COLOR_ARRAY:
return ffAttrMap[ RFF2A_SecondaryColor ];
case GL_FOG_COORD_ARRAY:
return ffAttrMap[ RFF2A_FogCoord ];
case GL_EDGE_FLAG_ARRAY:
return ffAttrMap[ RFF2A_EdgeFlag ];
case GL_TEXTURE_COORD_ARRAY:
if (catIndex < ffAttrNumTex)
return ffAttrTexBegin + catIndex;
break;
default:
break;
}
return ~0u;
}
void Iff::EnableClientState( RegalContext * ctx, GLenum state )
{
const GLuint idx = ClientStateToIndex( state );
if (idx == GLuint(~0))
return;
RestoreVao( ctx );
RegalAssert( idx < max_vertex_attribs );
if ( idx < max_vertex_attribs )
{
ctx->dispatcher.emulation.glEnableVertexAttribArray( idx );
EnableArray( ctx, idx ); // keep ffn up to date
}
}
void Iff::DisableClientState( RegalContext * ctx, GLenum state )
{
const GLuint idx = ClientStateToIndex( state );
if (idx == GLuint(~0))
return;
RestoreVao( ctx );
RegalAssert( idx < max_vertex_attribs );
if ( idx < max_vertex_attribs )
{
ctx->dispatcher.emulation.glDisableVertexAttribArray( idx );
DisableArray( ctx, idx ); // keep ffn up to date
}
}
void Iff::VertexPointer( RegalContext * ctx, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
{
switch (size)
{
case 2:
case 3:
case 4:
break;
default:
return;
}
switch (type)
{
case GL_SHORT:
case GL_INT:
case GL_FLOAT:
case GL_DOUBLE:
break;
default:
return;
}
if (stride < 0)
return;
RestoreVao( ctx );
ctx->dispatcher.emulation.glVertexAttribPointer( ffAttrMap[ RFF2A_Vertex ], size, type, GL_FALSE, stride, pointer );
}
void Iff::NormalPointer( RegalContext * ctx, GLenum type, GLsizei stride, const GLvoid *pointer )
{
RestoreVao( ctx );
GLboolean n = type == GL_FLOAT ? GL_FALSE : GL_TRUE;
ctx->dispatcher.emulation.glVertexAttribPointer( ffAttrMap[ RFF2A_Normal ], 3, type, n, stride, pointer );
}
void Iff::ColorPointer( RegalContext * ctx, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
{
RestoreVao( ctx );
GLboolean n = type == GL_FLOAT ? GL_FALSE : GL_TRUE;
ctx->dispatcher.emulation.glVertexAttribPointer( ffAttrMap[ RFF2A_Color ], size, type, n, stride, pointer );
}
void Iff::SecondaryColorPointer( RegalContext * ctx, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
{
RestoreVao( ctx );
GLboolean n = type == GL_FLOAT ? GL_FALSE : GL_TRUE;
ctx->dispatcher.emulation.glVertexAttribPointer( ffAttrMap[ RFF2A_SecondaryColor ], size, type, n, stride, pointer );
}
void Iff::FogCoordPointer( RegalContext * ctx, GLenum type, GLsizei stride, const GLvoid *pointer )
{
RestoreVao( ctx );
ctx->dispatcher.emulation.glVertexAttribPointer( ffAttrMap[ RFF2A_FogCoord ], 1, type, GL_FALSE, stride, pointer );
}
void Iff::EdgeFlagPointer( RegalContext * ctx, GLsizei stride, const GLvoid *pointer )
{
RestoreVao( ctx );
GLuint index = ffAttrMap[ RFF2A_EdgeFlag ];
if (index == RFF2A_Invalid)
return;
ctx->dispatcher.emulation.glVertexAttribPointer( index, 1, GL_UNSIGNED_BYTE, GL_FALSE, stride, pointer );
}
void Iff::TexCoordPointer( RegalContext * ctx, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
{
if (catIndex >= ffAttrNumTex)
{
// FIXME: set an error here!
return;
}
RestoreVao( ctx );
ctx->dispatcher.emulation.glVertexAttribPointer( ffAttrTexBegin + catIndex, size, type, GL_FALSE, stride, pointer );
}
void Iff::GetAttrib( RegalContext * ctx, GLuint index, GLenum pname, GLdouble * d )
{
ctx->dispatcher.emulation.glGetVertexAttribdv( index, pname, d );
}
void Iff::GetAttrib( RegalContext * ctx, GLuint index, GLenum pname, GLfloat * f )
{
ctx->dispatcher.emulation.glGetVertexAttribfv( index, pname, f );
}
void Iff::GetAttrib( RegalContext * ctx, GLuint index, GLenum pname, GLint * i )
{
ctx->dispatcher.emulation.glGetVertexAttribiv( index, pname, i );
}
bool Iff::IsEnabled( RegalContext * ctx, GLenum pname, GLboolean &enabled )
{
if (activeTextureIndex >= ctx->emuInfo->gl_max_texture_units)
return false;
State::Store & st = ffstate.raw;
int shift = 0;
switch (pname)
{
case GL_TEXTURE_1D:
shift = TP_1D;
break;
case GL_TEXTURE_2D:
shift = TP_2D;
break;
case GL_TEXTURE_RECTANGLE:
shift = TP_Rect;
break;
case GL_TEXTURE_3D:
shift = TP_3D;
break;
case GL_TEXTURE_CUBE_MAP:
shift = TP_CubeMap;
break;
case GL_COLOR_SUM:
enabled = (st.colorSum ? GL_TRUE : GL_FALSE);
return true;
case GL_FOG:
enabled = (st.fog.enable ? GL_TRUE : GL_FALSE);
return true;
case GL_LIGHTING:
enabled = (st.lighting ? GL_TRUE : GL_FALSE);
return true;
case GL_LIGHT0:
case GL_LIGHT1:
case GL_LIGHT2:
case GL_LIGHT3:
case GL_LIGHT4:
case GL_LIGHT5:
case GL_LIGHT6:
case GL_LIGHT7:
RegalAssertArrayIndex( st.light, pname - GL_LIGHT0 );
enabled = (st.light[ pname - GL_LIGHT0 ].enable ? GL_TRUE : GL_FALSE);
return true;
case GL_COLOR_MATERIAL:
enabled = (st.colorMaterial ? GL_TRUE : GL_FALSE);
return true;
case GL_RESCALE_NORMAL:
enabled = (st.rescaleNormal ? GL_TRUE : GL_FALSE);
return true;
case GL_NORMALIZE:
enabled = (st.normalize ? GL_TRUE : GL_FALSE);
return true;
case GL_TEXTURE_GEN_S:
case GL_TEXTURE_GEN_T:
case GL_TEXTURE_GEN_R:
case GL_TEXTURE_GEN_Q:
RegalAssertArrayIndex( st.tex, activeTextureIndex );
RegalAssertArrayIndex( st.tex[ activeTextureIndex ].texgen, pname - GL_TEXTURE_GEN_S );
enabled = (st.tex[ activeTextureIndex ].texgen[ pname - GL_TEXTURE_GEN_S ].enable ? GL_TRUE : GL_FALSE);
return true;
case GL_CLIP_PLANE0:
case GL_CLIP_PLANE1:
case GL_CLIP_PLANE2:
case GL_CLIP_PLANE3:
case GL_CLIP_PLANE4:
case GL_CLIP_PLANE5:
case GL_CLIP_PLANE0+6:
case GL_CLIP_PLANE0+7:
RegalAssertArrayIndex( st.clipPlaneEnabled, pname - GL_CLIP_PLANE0 );
enabled = (st.clipPlaneEnabled[ pname - GL_CLIP_PLANE0 ] ? GL_TRUE : GL_FALSE);
return true;
case GL_ALPHA_TEST:
enabled = (st.alphaTest.enable ? GL_TRUE : GL_FALSE);
return true;
default:
return false;
}
RegalAssertArrayIndex( st.tex, activeTextureIndex );
Texture & t = st.tex[ activeTextureIndex ];
GLuint v = 1 << shift;
enabled = ((t.enables & v) ? GL_TRUE : GL_FALSE);
return true;
}
void Iff::InitImmediate(RegalContext &ctx)
{
DispatchTableGL &tbl = ctx.dispatcher.emulation;
tbl.glGenVertexArrays( 1, & immVao );
tbl.glBindVertexArray( immVao );
BindVertexArray( &ctx, immVao ); // to keep ffn current
tbl.glGenBuffers( 1, & immVbo );
tbl.glBindBuffer( GL_ARRAY_BUFFER, immVbo );
tbl.glGenBuffers( 1, & immVboElement );
tbl.glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, immVboElement );
#if REGAL_SYS_EMSCRIPTEN
// We need this to be an allocated buffer for WebGL, because a dangling VertexAttribPointer
// doesn't work. XXX -- this might be a Firefox bug, check?
tbl.glBufferData( GL_ARRAY_BUFFER, sizeof( immArray ), NULL, GL_STATIC_DRAW );
tbl.glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( immArrayElement ), NULL, GL_STATIC_DRAW );
#endif
for (GLuint i = 0; i < max_vertex_attribs; i++)
{
EnableArray( &ctx, i ); // to keep ffn current
tbl.glEnableVertexAttribArray( i );
tbl.glVertexAttribPointer( i, 4, GL_FLOAT, GL_FALSE, max_vertex_attribs * sizeof(Float4), (GLubyte *)NULL + i * sizeof(Float4) );
}
tbl.glBindVertexArray( 0 );
BindVertexArray( &ctx, 0 ); // to keep ffn current
// The initial texture coordinates are (s; t; r; q) = (0; 0; 0; 1)
// for each texture coordinate set.
memset(&immVab, 0, sizeof(immVab));
for (catIndex = 0; catIndex < REGAL_EMU_MAX_TEXTURE_UNITS; catIndex++)
Attr<4>( &ctx, AttrIndex( RFF2A_TexCoord ), 0, 0, 0, 1 );
catIndex = 0;
// The initial current normal has coordinates (0; 0; 1).
Attr<3>( &ctx, AttrIndex( RFF2A_Normal ), 0, 0, 1 );
// The initial RGBA color is (R;G;B;A) = (1; 1; 1; 1) and
// the initial RGBA secondary color is (0; 0; 0; 1).
Attr<4>( &ctx, AttrIndex( RFF2A_Color ), 1, 1, 1, 1 );
Attr<4>( &ctx, AttrIndex( RFF2A_SecondaryColor ), 0, 0, 0, 1 );
// The initial fog coordinate is zero.
// ... so nothing to do for fog coordinate
// The initial color index is 1.
// The initial values for all generic vertex attributes are (0:0; 0:0; 0:0; 1:0).
}
void Iff::glDeleteVertexArrays( RegalContext * ctx, GLsizei n, const GLuint * arrays )
{
RegalAssert( ctx != NULL );
for (GLsizei i = 0; i < n; i++)
{
GLuint name = arrays[ i ];
if (name != immVao)
ctx->dispatcher.emulation.glDeleteVertexArrays( 1, &name );
}
}
void Iff::glDeleteBuffers( RegalContext * ctx, GLsizei n, const GLuint * buffers )
{
RegalAssert( ctx != NULL );
for (GLsizei i = 0; i < n; i++)
{
GLuint name = buffers[ i ];
if (name != immVbo && name != immVboElement)
ctx->dispatcher.emulation.glDeleteBuffers( 1, &name );
}
}
GLboolean Iff::IsVertexArray( RegalContext * ctx, GLuint name )
{
RegalAssert( ctx != NULL );
if (name == immVao )
return GL_FALSE;
return ctx->dispatcher.emulation.glIsVertexArray( name );
}
void Iff::glBindVertexArray( RegalContext *ctx, GLuint vao )
{
//<> glBindVertexArray is not allowed between a glBegin/glEnd pair so if immActive
//<> is true then I don't believe the shadow vao shouldn't be updated.
if (immActive == false)
{
immShadowVao = vao;
BindVertexArray( ctx, vao );
}
}
void Iff::ShadowClientActiveTexture( GLenum texture )
{
if ( (texture - GL_TEXTURE0) < REGAL_EMU_MAX_TEXTURE_COORDS)
catIndex = texture - GL_TEXTURE0;
else
Warning( "Client active texture out of range: ", Token::GLtextureToString(texture), " > ", Token::GLtextureToString(GL_TEXTURE0 + REGAL_EMU_MAX_TEXTURE_COORDS - 1));
}
void Iff::Begin( RegalContext * ctx, GLenum mode )
{
if (immActive == false)
{
immActive = true;
ctx->dispatcher.emulation.glBindVertexArray( immVao );
BindVertexArray( ctx, immVao ); // keep ffn current
}
PreDraw( ctx );
immCurrent = 0;
immCurrentElement = 0;
immPrim = mode;
}
void Iff::End( RegalContext * ctx )
{
Flush( ctx );
RestoreVao( ctx );
}
void Iff::RestoreVao( RegalContext * ctx )
{
if (immActive)
{
ctx->dispatcher.emulation.glBindVertexArray( immShadowVao );
BindVertexArray( ctx, immShadowVao );
immActive = false;
}
}
void Iff::Flush( RegalContext * ctx )
{
if (immCurrent>0) // Do nothing for empty buffer
{
DispatchTableGL &tbl = ctx->dispatcher.emulation;
tbl.glBufferData( GL_ARRAY_BUFFER, immCurrent * max_vertex_attribs * sizeof(Float4), immArray, GL_DYNAMIC_DRAW );
GLenum derivedPrim = immPrim;
if (( immPrim == GL_POLYGON ) && ( ctx->info->core == true || ctx->info->es2 ))
derivedPrim = GL_TRIANGLE_FAN;
tbl.glDrawArrays( derivedPrim, 0, immCurrent );
}
if (immCurrentElement > 0)
{
DispatchTableGL &tbl = ctx->dispatcher.emulation;
if(immShadowVao != immVao)
{
tbl.glBindVertexArray( immShadowVao );
BindVertexArray( ctx, immShadowVao );
}
GLuint immShadowVboElement = 0;
ctx->dispatcher.emulation.glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, reinterpret_cast<GLint*>(&immShadowVboElement) );
if(immShadowVboElement != immVboElement) tbl.glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, immVboElement );
tbl.glBufferData( GL_ELEMENT_ARRAY_BUFFER, immCurrentElement * sizeof(GLint), immArrayElement, GL_DYNAMIC_DRAW );
GLenum derivedPrim = immPrim;
if (( immPrim == GL_POLYGON ) && ( ctx->info->core == true || ctx->info->es2 ))
derivedPrim = GL_TRIANGLE_FAN;
tbl.glDrawElements( derivedPrim, immCurrentElement, GL_UNSIGNED_INT, 0 );
if(immShadowVboElement != immVboElement) tbl.glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, immShadowVboElement );
if(immShadowVao != immVao)
{
tbl.glBindVertexArray( immVao );
BindVertexArray( ctx, immVao );
}
}
}
void Iff::Provoke( RegalContext * ctx )
{
memcpy( immArray + immCurrent * max_vertex_attribs * sizeof(Float4), &immVab[0].x, max_vertex_attribs * sizeof(Float4) );
immCurrent++;
if ( immCurrent >= ((REGAL_IMMEDIATE_BUFFER_SIZE * REGAL_EMU_MAX_VERTEX_ATTRIBS) / max_vertex_attribs) )
{
Flush( ctx );
int restartVerts = 0;
switch( immPrim )
{
case GL_QUADS:
restartVerts = REGAL_IMMEDIATE_BUFFER_SIZE % 4;
break;
case GL_TRIANGLES:
restartVerts = REGAL_IMMEDIATE_BUFFER_SIZE % 3;
break;
case GL_LINES:
restartVerts = REGAL_IMMEDIATE_BUFFER_SIZE % 2;
break;
case GL_QUAD_STRIP:
restartVerts = 2;
break;
case GL_TRIANGLE_STRIP:
restartVerts = 2;
break;
case GL_LINE_STRIP:
restartVerts = 1;
break;
default:
break;
}
// For triangle fan we need the first and last vertices
// for restarting. All others concern the most recent n.
if (immPrim==GL_TRIANGLE_FAN)
{
memcpy( immArray + max_vertex_attribs * sizeof(Float4), immArray + (REGAL_IMMEDIATE_BUFFER_SIZE - 1) * max_vertex_attribs * sizeof(Float4), max_vertex_attribs * sizeof(Float4));
immCurrent = 2;
}
else
{
int offset = REGAL_IMMEDIATE_BUFFER_SIZE - restartVerts;
memcpy( immArray, immArray + offset * max_vertex_attribs * sizeof(Float4), restartVerts * max_vertex_attribs * sizeof(Float4));
immCurrent = restartVerts;
}
}
}
void Iff::ProvokeElement( RegalContext * ctx, GLint i )
{
immArrayElement[immCurrentElement++] = i;
if ( immCurrentElement >= REGAL_IMMEDIATE_BUFFER_SIZE )
{
Flush( ctx );
int restartVerts = 0;
switch( immPrim )
{
case GL_QUADS:
restartVerts = REGAL_IMMEDIATE_BUFFER_SIZE % 4;
break;
case GL_TRIANGLES:
restartVerts = REGAL_IMMEDIATE_BUFFER_SIZE % 3;
break;
case GL_LINES:
restartVerts = REGAL_IMMEDIATE_BUFFER_SIZE % 2;
break;
case GL_QUAD_STRIP:
restartVerts = 2;
break;
case GL_TRIANGLE_STRIP:
restartVerts = 2;
break;
case GL_LINE_STRIP:
restartVerts = 1;
break;
default:
break;
}
// For triangle fan we need the first and last vertices
// for restarting. All others concern the most recent n.
if (immPrim==GL_TRIANGLE_FAN)
{
immArrayElement[1] = immArrayElement[REGAL_IMMEDIATE_BUFFER_SIZE - 1];
immCurrentElement = 2;
}
else
{
int offset = REGAL_IMMEDIATE_BUFFER_SIZE - restartVerts;
memcpy( immArrayElement, immArrayElement + offset * sizeof(GLint), restartVerts * sizeof(GLint));
immCurrentElement = restartVerts;
}
}
}
GLuint Iff::AttrIndex( RegalFixedFunctionAttrib attr, int cat ) const
{
if (attr < RFF2A_TexCoord)
{
RegalAssertArrayIndex( ffAttrMap, attr );
return ffAttrMap[ attr ];
}
if (cat < 0)
cat = catIndex;
if (attr == RFF2A_TexCoord && GLuint(cat) < ffAttrNumTex)
return ffAttrTexBegin + cat;
return ~0u;
}
void Iff::InitFixedFunction(RegalContext &ctx)
{
Internal("Regal::Iff::InitFixedFunction","()");
RegalAssert(ctx.info);
gles = ctx.info->es2;
legacy = ctx.info->compat && ctx.info->gl_version_major<=2;
vaoAttrMap[0] = 0;
fmtmap[ 1 ] = GL_LUMINANCE;
fmtmap[ 2 ] = GL_LUMINANCE_ALPHA;
fmtmap[ 3 ] = GL_RGB;
fmtmap[ 4 ] = GL_RGBA;
fmtmap[ GL_ALPHA ] = GL_ALPHA;
fmtmap[ GL_ALPHA4 ] = GL_ALPHA;
fmtmap[ GL_ALPHA8 ] = GL_ALPHA;
fmtmap[ GL_ALPHA12 ] = GL_ALPHA;
fmtmap[ GL_ALPHA16 ] = GL_ALPHA;
fmtmap[ GL_LUMINANCE ] = GL_LUMINANCE;
fmtmap[ GL_LUMINANCE4 ] = GL_LUMINANCE;
fmtmap[ GL_LUMINANCE8 ] = GL_LUMINANCE;
fmtmap[ GL_LUMINANCE12 ] = GL_LUMINANCE;
fmtmap[ GL_LUMINANCE16 ] = GL_LUMINANCE;
fmtmap[ GL_LUMINANCE_ALPHA ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_LUMINANCE4_ALPHA4 ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_LUMINANCE6_ALPHA2 ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_LUMINANCE8_ALPHA8 ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_LUMINANCE12_ALPHA4 ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_LUMINANCE12_ALPHA12 ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_LUMINANCE16_ALPHA16 ] = GL_LUMINANCE_ALPHA;
fmtmap[ GL_INTENSITY ] = GL_INTENSITY;
fmtmap[ GL_INTENSITY4 ] = GL_INTENSITY;
fmtmap[ GL_INTENSITY8 ] = GL_INTENSITY;
fmtmap[ GL_INTENSITY12 ] = GL_INTENSITY;
fmtmap[ GL_INTENSITY12 ] = GL_INTENSITY;
fmtmap[ GL_INTENSITY16 ] = GL_INTENSITY;
fmtmap[ GL_RGB ] = GL_RGB;
fmtmap[ GL_RGB4 ] = GL_RGB;
fmtmap[ GL_RGB5 ] = GL_RGB;
fmtmap[ GL_RGB8 ] = GL_RGB;
fmtmap[ GL_RGB10 ] = GL_RGB;
fmtmap[ GL_RGB12 ] = GL_RGB;
fmtmap[ GL_RGB16 ] = GL_RGB;
fmtmap[ GL_RGBA ] = GL_RGBA;
fmtmap[ GL_RGBA4 ] = GL_RGBA;
fmtmap[ GL_RGB5_A1 ] = GL_RGBA;
fmtmap[ GL_RGBA8 ] = GL_RGBA;
fmtmap[ GL_RGB10_A2 ] = GL_RGBA;
fmtmap[ GL_RGBA12 ] = GL_RGBA;
fmtmap[ GL_RGBA16 ] = GL_RGBA;
fmtmap[ GL_RGBA16F ] = GL_RGBA16F;
fmtmap[ GL_SRGB ] = GL_SRGB;
fmtmap[ GL_SRGB8 ] = GL_SRGB;
fmtmap[ GL_SRGB8_ALPHA8 ] = GL_SRGB8_ALPHA8;
fmtmap[ GL_SLUMINANCE ] = GL_SLUMINANCE;
fmtmap[ GL_SLUMINANCE8 ] = GL_SLUMINANCE;
fmtmap[ GL_RGB16F_ARB ] = GL_RGB;
fmtmap[ GL_RGBA32F_ARB ] = GL_RGB;
fmtmap[ GL_DEPTH_COMPONENT16 ] = GL_DEPTH_COMPONENT16;
fmtmap[ GL_INTENSITY16F_ARB ] = GL_INTENSITY;
// GL_ARB_ES2_compatibility
fmtmap[ GL_RGB565 ] = GL_RGB;
// ARB_texture_rg and EXT_texture_rg
fmtmap[ GL_RED ] = GL_RED;
fmtmap[ GL_RED_INTEGER ] = GL_RED;
fmtmap[ GL_R8 ] = GL_RED;
fmtmap[ GL_R16 ] = GL_RED;
fmtmap[ GL_R16F ] = GL_RED;
fmtmap[ GL_R32F ] = GL_RED;
fmtmap[ GL_R8I ] = GL_RED;
fmtmap[ GL_R8UI ] = GL_RED;
fmtmap[ GL_R16I ] = GL_RED;
fmtmap[ GL_R16UI ] = GL_RED;
fmtmap[ GL_R32I ] = GL_RED;
fmtmap[ GL_R32UI ] = GL_RED;
fmtmap[ GL_RG ] = GL_RG;
fmtmap[ GL_RG_INTEGER ] = GL_RG;
fmtmap[ GL_RG8 ] = GL_RG;
fmtmap[ GL_RG16 ] = GL_RG;
fmtmap[ GL_RG16F ] = GL_RG;
fmtmap[ GL_RG32F ] = GL_RG;
fmtmap[ GL_RG8I ] = GL_RG;
fmtmap[ GL_RG8UI ] = GL_RG;
fmtmap[ GL_RG16I ] = GL_RG;
fmtmap[ GL_RG16UI ] = GL_RG;
fmtmap[ GL_RG32I ] = GL_RG;
fmtmap[ GL_RG32UI ] = GL_RG;
}
void Iff::PreDraw( RegalContext * ctx )
{
if (programPipeline)
return; // FIXME: Eventually will need to handle empty or partially populated PPO
ver.Reset();
if (program)
UseShaderProgram( ctx );
else
UseFixedFunctionProgram( ctx );
}
void Iff::SetCurrentMatrixStack( GLenum mode )
{
switch( mode )
{
case GL_MODELVIEW:
currMatrixStack = &modelview;
break;
case GL_PROJECTION:
currMatrixStack = &projection;
break;
case GL_TEXTURE:
if (activeTextureIndex < GLuint( REGAL_EMU_MAX_TEXTURE_UNITS ))
currMatrixStack = &texture[ activeTextureIndex ];
break;
case GL_TEXTURE0:
case GL_TEXTURE1:
case GL_TEXTURE2:
case GL_TEXTURE3:
{
GLuint idx = mode - GL_TEXTURE0;
if (idx > GLuint( REGAL_EMU_MAX_TEXTURE_UNITS - 1 ))
break;
currMatrixStack = &texture[ idx ];
}
break;
default:
RegalAssert( true && "WTF?" );
break;
}
}
bool Iff::ShadowMatrixMode( GLenum mode )
{
shadowMatrixMode = mode;
return true;
}
void Iff::ShadowActiveTexture( GLenum texture )
{
if (validTextureEnum(texture))
shadowActiveTextureIndex = texture - GL_TEXTURE0;
}
bool Iff::ShadowEnable( GLenum cap )
{
return EnableIndexed( cap, shadowActiveTextureIndex );
}
bool Iff::ShadowDisable( GLenum cap )
{
return DisableIndexed( cap, shadowActiveTextureIndex );
}
bool Iff::EnableIndexed( GLenum cap, GLuint index )
{
activeTextureIndex = index;
bool ret = ffstate.SetEnable( this, true, cap );
return ret;
}
bool Iff::DisableIndexed( GLenum cap, GLuint index )
{
activeTextureIndex = index;
bool ret = ffstate.SetEnable( this, false, cap );
return ret;
}
bool Iff::ShadowUseProgram( GLuint prog )
{
program = prog;
// ensure program bind when program changes
if( currinst ) {
currinst->prevInstance = NULL;
}
if( prog && inst.count( prog ) ) {
currinst = & inst[prog];
} else {
currinst = NULL;
}
return prog == 0 || currinst != NULL; // pass the call along only if it's non-zero and not instanced
}
bool Iff::ShadowBindProgramPipeline( GLuint progPipeline )
{
programPipeline = progPipeline;
return false; // always pass this through since we're not emulating it
}
void Iff::ShadowMultiTexBinding( GLenum texunit, GLenum target, GLuint obj )
{
Internal("Regal::Iff::ShadowMultiTexBinding",toString(texunit)," ",toString(target)," ",obj);
// texture unit state is only set when the active texture index is <4 (for fixed function)
if ( texunit - GL_TEXTURE0 >= REGAL_EMU_MAX_TEXTURE_COORDS
|| texunit - GL_TEXTURE0 >= REGAL_EMU_MAX_TEXTURE_UNITS )
return;
activeTextureIndex = texunit - GL_TEXTURE0;
GLenum fmt = textureObjToFmt[ obj ];
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
RegalAssertArrayIndex( textureBinding, activeTextureIndex );
TextureUnit & tu = textureUnit[ activeTextureIndex ];
textureBinding[ activeTextureIndex ] = obj;
tu.fmt = fmt;
tu.ttb = TargetToBitfield( target );
ffstate.raw.ver = ver.Update();
}
void Iff::ShadowTexBinding( GLenum target, GLuint obj )
{
ShadowMultiTexBinding( GL_TEXTURE0 + shadowActiveTextureIndex, target, obj );
}
void Iff::ShadowTextureInfo( GLuint obj, GLenum target, GLint internalFormat )
{
Internal("Regal::Iff::ShadowTextureInfo",obj," ",GLenumToString(target)," ",GLenumToString(internalFormat));
UNUSED_PARAMETER(target);
if ( fmtmap.count( internalFormat ) == 0 )
{
Warning( "Unknown internal format: ", GLenumToString(internalFormat) );
}
GLenum fmt = fmtmap[ internalFormat ];
textureObjToFmt[ obj ] = fmt;
ffstate.raw.ver = ver.Update();
}
void Iff::ShadowMultiTexInfo( GLenum texunit, GLenum target, GLint internalFormat )
{
if ( (texunit - GL_TEXTURE0) < REGAL_EMU_MAX_TEXTURE_COORDS)
{
activeTextureIndex = texunit - GL_TEXTURE0;
ShadowTexInfo( target, internalFormat );
}
else
Warning( "Texture unit out of range: ", Token::GLtextureToString(texunit), " > ", Token::GLtextureToString(GL_TEXTURE0 + REGAL_EMU_MAX_TEXTURE_COORDS - 1));
}
void Iff::ShadowTexInfo( GLenum target, GLint internalFormat )
{
// texture unit state is only set when the active texture index <4 (for fixed function)
if ( shadowActiveTextureIndex > ( REGAL_EMU_MAX_TEXTURE_UNITS - 1 ) )
{
return;
}
RegalAssertArrayIndex( textureBinding, shadowActiveTextureIndex );
RegalAssertArrayIndex( textureUnit, shadowActiveTextureIndex );
ShadowTextureInfo( textureBinding[ shadowActiveTextureIndex ], target, internalFormat );
textureUnit[ shadowActiveTextureIndex ].fmt = fmtmap[ internalFormat ];
}
void Iff::TexEnv( GLenum texunit, GLenum target, GLenum pname, const GLfloat *v )
{
activeTextureIndex = texunit - GL_TEXTURE0;
switch( target )
{
case GL_TEXTURE_ENV:
switch( pname )
{
case GL_TEXTURE_ENV_COLOR:
{
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
RegalAssertArrayIndex( textureEnvColor, activeTextureIndex );
RegalAssertArrayIndex( textureEnvColorVer, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
Copy( textureEnvColor[ activeTextureIndex ], v );
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
textureEnvColorVer[ activeTextureIndex ] = ffstate.uniform.ver = ver.Update();
return;
}
}
}
GLint iv[4];
iv[0] = static_cast<GLint>(v[0]);
iv[1] = static_cast<GLint>(v[1]);
iv[2] = static_cast<GLint>(v[2]);
iv[3] = static_cast<GLint>(v[3]);
TexEnv( texunit, target, pname, iv );
}
void Iff::TexEnv( GLenum texunit, GLenum target, GLenum pname, const GLint *v )
{
Internal("Regal::Iff::TexEnv",GLenumToString(texunit)," ",GLenumToString(target)," ",GLenumToString(pname));
activeTextureIndex = texunit - GL_TEXTURE0;
switch( target )
{
case GL_TEXTURE_ENV:
switch( pname )
{
case GL_TEXTURE_ENV_MODE:
{
if ( activeTextureIndex >= REGAL_EMU_MAX_TEXTURE_UNITS )
{
return;
}
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
TexenvMode & m = tup->env.mode;
switch( v[0] )
{
case GL_REPLACE:
m = TEM_Replace;
break;
case GL_MODULATE:
m = TEM_Modulate;
break;
case GL_ADD:
m = TEM_Add;
break;
case GL_DECAL:
m = TEM_Decal;
break;
case GL_BLEND:
m = TEM_Blend;
break;
case GL_COMBINE:
m = TEM_Combine;
break;
default:
return; // error?
}
// assert( target == tip->tgt );
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
break;
}
case GL_COMBINE_RGB:
{
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
TexenvCombineState & s = tup->env.rgb;
switch( v[0] )
{
case GL_REPLACE:
s.mode = TEC_Replace;
break;
case GL_MODULATE:
s.mode = TEC_Modulate;
break;
case GL_ADD:
s.mode = TEC_Add;
break;
case GL_ADD_SIGNED:
s.mode = TEC_AddSigned;
break;
case GL_SUBTRACT:
s.mode = TEC_Subtract;
break;
case GL_DOT3_RGB:
s.mode = TEC_Dot3Rgb;
break;
case GL_DOT3_RGBA:
s.mode = TEC_Dot3Rgba;
break;
case GL_INTERPOLATE:
s.mode = TEC_Interpolate;
break;
default:
return; // error?
}
// assert( target == tip->tgt );
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
break;
}
case GL_COMBINE_ALPHA:
{
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
TexenvCombineState & s = tup->env.a;
switch( v[0] )
{
case GL_REPLACE:
s.mode = TEC_Replace;
break;
case GL_MODULATE:
s.mode = TEC_Modulate;
break;
case GL_ADD:
s.mode = TEC_Add;
break;
case GL_ADD_SIGNED:
s.mode = TEC_AddSigned;
break;
case GL_SUBTRACT:
s.mode = TEC_Subtract;
break;
case GL_INTERPOLATE:
s.mode = TEC_Interpolate;
break;
default:
return; // error?
}
// assert( target == tip->tgt );
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
break;
}
case GL_SRC0_RGB:
case GL_SRC1_RGB:
case GL_SRC2_RGB:
case GL_SRC0_ALPHA:
case GL_SRC1_ALPHA:
case GL_SRC2_ALPHA:
{
int idx = pname - GL_SRC0_RGB;
bool isRgb = true;
if ( idx > 3 )
{
isRgb = false;
idx = pname - GL_SRC0_ALPHA;
}
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
TexenvCombineState & c = isRgb ? tup->env.rgb : tup->env.a;
if ( idx == 0 )
{
switch( v[0] )
{
case GL_PREVIOUS:
c.src0 = TCS_Previous;
break;
case GL_CONSTANT:
c.src0 = TCS_Constant;
break;
case GL_TEXTURE:
c.src0 = TCS_Texture;
break;
case GL_PRIMARY_COLOR:
c.src0 = TCS_PrimaryColor;
break;
default:
return; // error?
}
}
else if( idx == 1 )
{
switch( v[0] )
{
case GL_PREVIOUS:
c.src1 = TCS_Previous;
break;
case GL_CONSTANT:
c.src1 = TCS_Constant;
break;
case GL_TEXTURE:
c.src1 = TCS_Texture;
break;
case GL_PRIMARY_COLOR:
c.src1 = TCS_PrimaryColor;
break;
default:
return; // error?
}
}
else if( idx == 2 )
{
switch( v[0] )
{
case GL_PREVIOUS:
c.src2 = TCS_Previous;
break;
case GL_CONSTANT:
c.src2 = TCS_Constant;
break;
case GL_TEXTURE:
c.src2 = TCS_Texture;
break;
case GL_PRIMARY_COLOR:
c.src2 = TCS_PrimaryColor;
break;
default:
return; // error?
}
}
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
break;
}
case GL_OPERAND0_RGB:
case GL_OPERAND1_RGB:
case GL_OPERAND2_RGB:
case GL_OPERAND0_ALPHA:
case GL_OPERAND1_ALPHA:
case GL_OPERAND2_ALPHA:
{
int idx = pname - GL_OPERAND0_RGB;
bool isRgb = true;
if ( idx > 3 )
{
isRgb = false;
idx = pname - GL_OPERAND0_ALPHA;
}
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
TexenvCombineState & c = isRgb ? tup->env.rgb : tup->env.a;
if ( idx == 0 )
{
switch( v[0] )
{
case GL_SRC_COLOR:
c.op0 = TCO_Color;
break;
case GL_SRC_ALPHA:
c.op0 = TCO_Alpha;
break;
case GL_ONE_MINUS_SRC_COLOR:
c.op0 = TCO_OneMinusColor;
break;
case GL_ONE_MINUS_SRC_ALPHA:
c.op0 = TCO_OneMinusAlpha;
break;
default:
return; // error?
}
}
else if( idx == 1 )
{
switch( v[0] )
{
case GL_SRC_COLOR:
c.op1 = TCO_Color;
break;
case GL_SRC_ALPHA:
c.op1 = TCO_Alpha;
break;
case GL_ONE_MINUS_SRC_COLOR:
c.op1 = TCO_OneMinusColor;
break;
case GL_ONE_MINUS_SRC_ALPHA:
c.op1 = TCO_OneMinusAlpha;
break;
default:
return; // error?
}
}
else if( idx == 2 )
{
switch( v[0] )
{
case GL_SRC_COLOR:
c.op2 = TCO_Color;
break;
case GL_SRC_ALPHA:
c.op2 = TCO_Alpha;
break;
case GL_ONE_MINUS_SRC_COLOR:
c.op2 = TCO_OneMinusColor;
break;
case GL_ONE_MINUS_SRC_ALPHA:
c.op2 = TCO_OneMinusAlpha;
break;
default:
return; // error?
}
}
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
break;
}
case GL_RGB_SCALE:
case GL_ALPHA_SCALE:
{
RegalAssertArrayIndex( textureUnit, activeTextureIndex );
TextureUnit *tup = & textureUnit[ activeTextureIndex ];
TexenvCombineState & c = (pname == GL_RGB_SCALE ? tup->env.rgb : tup->env.a);
GLfloat pscale = static_cast<GLfloat>(v[0]);
if (pscale != 1.0 && pscale != 2.0 && pscale != 4.0)
{
Warning("Invalid value set for TexEnv Scale %f - must be 1.0, 2.0, or 4.0\n", pscale);
return; // error
}
c.scale = pscale;
ffstate.SetTexInfo( ver, activeTextureIndex, *tup );
break;
}
default:
break;
}
break;
default:
break;
}
}
void Iff::TexEnv( GLenum texunit, GLenum target, GLenum pname, GLfloat v )
{
TexEnv( texunit, target, pname, &v );
}
void Iff::TexEnv( GLenum texunit, GLenum target, GLenum pname, GLint v )
{
TexEnv( texunit, target, pname, &v );
}
void Iff::ShadeModel( GLenum mode )
{
State::Store & r = ffstate.raw;
switch( mode )
{
case GL_FLAT:
if (!r.shadeModelFlat)
{
r.shadeModelFlat = true;
r.ver = ver.Update();
}
break;
case GL_SMOOTH:
if (r.shadeModelFlat)
{
r.shadeModelFlat = false;
r.ver = ver.Update();
}
break;
}
}
void Iff::ColorMaterial( GLenum face, GLenum mode )
{
ColorMaterialMode m;
switch( mode )
{
case GL_EMISSION:
m = CM_Emission;
break;
case GL_AMBIENT:
m = CM_Ambient;
break;
case GL_DIFFUSE:
m = CM_Diffuse;
break;
case GL_SPECULAR:
m = CM_Specular;
break;
case GL_AMBIENT_AND_DIFFUSE:
m = CM_AmbientAndDiffuse;
break;
default:
return;
}
switch( face )
{
case GL_FRONT:
ffstate.raw.colorMaterialTarget0 = m;
ffstate.raw.colorMaterialTarget1 = CM_None;
break;
case GL_BACK:
ffstate.raw.colorMaterialTarget0 = CM_None;
ffstate.raw.colorMaterialTarget1 = m;
break;
case GL_FRONT_AND_BACK:
ffstate.raw.colorMaterialTarget0 = m;
ffstate.raw.colorMaterialTarget1 = m;
break;
default:
return;
}
ffstate.raw.ver = ver.Update();
}
void Iff::AlphaFunc( GLenum comp, const GLfloat ref )
{
CompareFunc cf = CF_Always;
switch( comp )
{
case GL_NEVER:
cf = CF_Never;
break;
case GL_LESS:
cf = CF_Less;
break;
case GL_EQUAL:
cf = CF_Equal;
break;
case GL_LEQUAL:
cf = CF_Lequal;
break;
case GL_NOTEQUAL:
cf = CF_NotEqual;
break;
case GL_GREATER:
cf = CF_Greater;
break;
case GL_GEQUAL:
cf = CF_Gequal;
break;
case GL_ALWAYS:
cf = CF_Always;
break;
default:
break; // should be an error...
}
ffstate.SetAlphaFunc( this, cf, ref );
}
void Iff::ClipPlane( GLenum plane, const GLdouble * equation )
{
Float4 eqn( equation[0], equation[1], equation[2], equation[3] );
ffstate.SetClip( this, plane, & eqn.x );
}
void Iff::MatrixPush( GLenum mode )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Push();
}
void Iff::MatrixPop( GLenum mode )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Pop();
UpdateMatrixVer();
}
void Iff::UpdateMatrixVer()
{
currMatrixStack->Ver() = ffstate.uniform.ver = ver.Update();
if (currMatrixStack != &modelview && currMatrixStack != &projection)
ffstate.raw.ver = ffstate.uniform.ver;
}
void Iff::MatrixLoadIdentity( GLenum mode )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Top().MakeIdentity();
UpdateMatrixVer();
}
void Iff::MatrixLoad( GLenum mode, const r3::Matrix4f & m )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Top() = m;
UpdateMatrixVer();
}
void Iff::MatrixLoadTranspose( GLenum mode, const r3::Matrix4f & m )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Top() = m.Transpose();
UpdateMatrixVer();
}
void Iff::MatrixMult( GLenum mode, const r3::Matrix4f & m )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Top().MultRight( m );
UpdateMatrixVer();
}
void Iff::MatrixMultTranspose( GLenum mode, const r3::Matrix4f & m )
{
SetCurrentMatrixStack( mode );
currMatrixStack->Top().MultRight( m.Transpose() );
UpdateMatrixVer();
}
void Iff::PushMatrix()
{
MatrixPush( shadowMatrixMode );
}
void Iff::PopMatrix()
{
MatrixPop( shadowMatrixMode );
}
void Iff::LoadIdentity()
{
MatrixLoadIdentity( shadowMatrixMode );
}
void Iff::LoadMatrix( const r3::Matrix4f & m )
{
MatrixLoad( shadowMatrixMode, m );
}
void Iff::LoadTransposeMatrix( const r3::Matrix4f & m )
{
MatrixLoadTranspose( shadowMatrixMode, m );
}
void Iff::MultMatrix( const r3::Matrix4f & m )
{
MatrixMult( shadowMatrixMode, m );
}
void Iff::MultTransposeMatrix( const r3::Matrix4f & m )
{
MatrixMultTranspose( shadowMatrixMode, m );
}
void Iff::Viewport( GLint x, GLint y, GLsizei w, GLsizei h )
{
viewport.x = x;
viewport.y = y;
viewport.w = w;
viewport.h = h;
}
void Iff::DepthRange( GLfloat znear, GLfloat zfar )
{
viewport.zn = znear;
viewport.zf = zfar;
}
void Iff::RasterPos( RegalContext * ctx, GLdouble x, GLdouble y, GLdouble z )
{
r3::Vec3f pos( x, y, z );
r3::Vec3f s( 0.5f * GLfloat(viewport.w), 0.5f * GLfloat(viewport.h), 0.5f * GLfloat( viewport.zf - viewport.zn ) );
r3::Vec3f b( GLfloat(viewport.x), GLfloat(viewport.y), 0.5f + GLfloat(viewport.zn) );
r3::Matrix4f sb;
sb.SetScale( s );
sb.SetTranslate( s + b );
r3::Matrix4f m = sb * projection.Top() * modelview.Top();
m.MultMatrixVec( pos );
WindowPos( ctx, pos.x, pos.y, pos.z );
}
void Iff::WindowPos( RegalContext * ctx, GLdouble x, GLdouble y, GLdouble z )
{
if (ctx->isCore() || ctx->isCompat())
{
// todo - cache rasterpos and implement glDrawPixels and glBitmap
return;
}
ctx->dispatcher.emulation.glWindowPos3d( x, y, z );
}
void Iff::BindVertexArray( RegalContext * ctx, GLuint vao )
{
UNUSED_PARAMETER(ctx);
vaoAttrMap[ currVao ] = ffstate.raw.attrArrayFlags;
currVao = vao;
ffstate.raw.attrArrayFlags = vaoAttrMap[ currVao ];
ffstate.uniform.vabVer = ver.Update();
}
void Iff::EnableArray( RegalContext * ctx, GLuint index )
{
RestoreVao( ctx );
ffstate.raw.attrArrayFlags |= 1 << index;
ffstate.raw.ver = ffstate.uniform.vabVer = ver.Update();
}
void Iff::DisableArray( RegalContext * ctx, GLuint index )
{
RestoreVao( ctx );
ffstate.raw.attrArrayFlags &= ~( 1 << index );
ffstate.raw.ver = ffstate.uniform.vabVer = ver.Update();
}
GLuint Iff::CreateShader( RegalContext *ctx, GLenum shaderType )
{
GLuint sh = ctx->dispatcher.emulation.glCreateShader( shaderType );
shaderTypeMap[ sh ] = shaderType;
return sh;
}
void Iff::Init( RegalContext &ctx )
{
shadowMatrixMode = GL_MODELVIEW;
shadowActiveTextureIndex = 0;
activeTextureIndex = 0;
programPipeline = 0;
program = 0;
currprog = NULL;
currinst = NULL;
currMatrixStack = &modelview;
currVao = 0;
gles = false;
legacy = false;
RegalContext *sharingWith = ctx.shareGroup->front();
if (sharingWith)
textureObjToFmt = sharingWith->iff->textureObjToFmt;
InitVertexArray(ctx);
InitFixedFunction(ctx);
InitImmediate(ctx);
}
void Iff::State::Process( Iff * ffn )
{
Internal("Iff::State::Process","()");
const Store & r = raw;
Store & p = processed;
StoreUniform & u = uniform;
if ( r.ver > 0 && r.ver == p.ver )
{
return;
}
p = r;
r3::Matrix4f identity;
if ( p.fog.enable == false )
{
p.fog.useDepth = true;
p.fog.mode = FG_Exp;
}
// alpha testing is done at the precision of the color buffer so adjust
// alpharef to an 8-bit range
u.alphaTest.alphaRef = floor( u.alphaTest.alphaRef * 255.0f + 0.5f ) / 255.0f;
size_t n = array_size( ffn->textureUnit );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( ffn->textureUnit, i );
TextureUnit & ti = ffn->textureUnit[ i ];
RegalAssertArrayIndex( raw.tex, i );
raw.tex[i].unit = ti;
}
n = array_size( p.tex );
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( p.tex, i );
RegalAssertArrayIndex( r.tex, i );
Texture & pt = p.tex[i];
const Texture & rt= r.tex[i];
// for processed state, only the highest priority texture enable set (others are dont-care)
//<> dsn:
//<> but do we need to track and check against ttb? If no texture was
//<> explicitly bound to a texture target then the default texture (which
//<> has the name 0) for that target will be used, right?
//<> pt.enables = HighestPriorityTextureEnable( rt.enables & rt.unit.ttb );
pt.enables = HighestPriorityTextureEnable( rt.enables );
pt.unit = rt.unit;
RegalAssertArrayIndex( ffn->texture, i );
pt.useMatrix = pt.enables != 0 && ffn->texture[i].Top() != identity;
if ( pt.unit.env.mode != TEM_Combine )
{
pt.unit.env.rgb = TexenvCombineState(true);
pt.unit.env.a = TexenvCombineState(false);
}
}
n = array_size( p.light );
if ( p.lighting == false )
{
p.rescaleNormal = false;
p.normalize = false;
for ( size_t i = 0; i <n; i++ )
{
RegalAssertArrayIndex( p.light, i );
Light & l = p.light[i];
l.enable = l.spotlight = l.attenuate = false;
}
}
else
{
for ( size_t i = 0; i < n; i++ )
{
RegalAssertArrayIndex( p.light, i );
RegalAssertArrayIndex( u.light, i );
Light & l = p.light[i];
LightUniform & lu = u.light[i];
if ( l.enable == true )
{
l.spotlight = lu.spotDirection.w != 180.0f;
l.attenuate = lu.attenuation.x != 1.0f || lu.attenuation.y != 0.0f || lu.attenuation.z != 0.0f;
}
}
}
p.hash = Lookup3::hashlittle(reinterpret_cast<const char *>(&p.hash) + sizeof( p.hash ),
reinterpret_cast<const char *>((&p)+1) - reinterpret_cast<const char *>(&p.hash) - sizeof( p.hash ), 0);
}
void Iff::UpdateUniforms( RegalContext * ctx )
{
Internal("Regal::Iff::UpdateUniforms", boost::print::optional(ctx,Logging::pointers));
DispatchTableGL & tbl = ctx->dispatcher.emulation;
UniformMap * umap = NULL;
if( currinst ) {
if( currinst->prevInstance == NULL ) {
return;
}
UserProgramInstance & upi = *currinst->prevInstance;
if ( upi.ffver == ffstate.Ver() ) {
return;
}
upi.ffver = ffstate.Ver();
umap = & upi.ffuniforms;
} else {
Program & pgm = *currprog;
if ( pgm.ver == ffstate.Ver() ) {
return;
}
pgm.ver = ffstate.Ver();
umap = & pgm.uniforms;
}
const State::Store & p = ffstate.processed;
const State::StoreUniform & u = ffstate.uniform;
for ( UniformMap::iterator i = umap->begin(); i != umap->end(); ++i )
{
UniformInfo & ui = (*i).second;
bool inverse = false;
bool transpose = false;
RegalFFUniformEnum ue = (*i).first;
if( FFU_ModelViewMatrixInverse <= ue && ue <= FFU_TextureMatrix15Inverse ) {
inverse = true;
ue = RegalFFUniformEnum( FFU_ModelViewMatrix + ( ue - FFU_ModelViewMatrixInverse ) );
} else if( FFU_ModelViewMatrixTranspose <= ue && ue <= FFU_TextureMatrix15Transpose ) {
transpose = true;
ue = RegalFFUniformEnum( FFU_ModelViewMatrix + ( ue - FFU_ModelViewMatrixTranspose ) );
} else if( FFU_ModelViewMatrixInverseTranspose <= ue && ue <= FFU_TextureMatrix15InverseTranspose ) {
inverse = true;
transpose = true;
ue = RegalFFUniformEnum( FFU_ModelViewMatrix + ( ue - FFU_ModelViewMatrixInverseTranspose ) );
}
switch( ue )
{
case FFU_NormalMatrix:
{
if( ui.ver != modelview.Ver() ) {
ui.ver = modelview.Ver();
r3::Matrix4f m = modelview.Top().Inverse().Transpose();
if ( p.rescaleNormal )
{
m = RescaleNormal( m );
}
r3::Matrix3f m3 = r3::ToMatrix3( m ).Transpose(); // FIXME: r3::Matrix3f should be column major like Matrix4...
tbl.glUniformMatrix3fv( ui.slot, 1, GL_FALSE, m3.Ptr() );
}
break;
}
case FFU_ModelViewMatrix:
{
if ( ui.ver != modelview.Ver() )
{
ui.ver = modelview.Ver();
r3::Matrix4f m = modelview.Top();
if( inverse ) {
m = m.Inverse();
}
if( transpose ) {
m = m.Transpose();
}
tbl.glUniformMatrix4fv( ui.slot, 1, GL_FALSE, m.Ptr() );
}
break;
}
case FFU_ProjectionMatrix:
{
if ( ui.ver != projection.Ver() )
{
ui.ver = projection.Ver();
r3::Matrix4f m = projection.Top();
if( inverse ) {
m = m.Inverse();
}
if( transpose ) {
m = m.Transpose();
}
tbl.glUniformMatrix4fv( ui.slot, 1, GL_FALSE, m.Ptr() );
}
break;
}
case FFU_ModelViewProjectionMatrix:
{
GLuint64 mvpVer = std::max( modelview.Ver(), projection.Ver() );
if ( ui.ver != mvpVer )
{
ui.ver = mvpVer;
r3::Matrix4f m = projection.Top() * modelview.Top();
if( inverse ) {
m = m.Inverse();
}
if( transpose ) {
m = m.Transpose();
}
tbl.glUniformMatrix4fv( ui.slot, 1, GL_FALSE, m.Ptr() );
}
break;
}
case FFU_TextureMatrix0:
case FFU_TextureMatrix1:
case FFU_TextureMatrix2:
case FFU_TextureMatrix3:
case FFU_TextureMatrix4:
case FFU_TextureMatrix5:
case FFU_TextureMatrix6:
case FFU_TextureMatrix7:
case FFU_TextureMatrix8:
case FFU_TextureMatrix9:
case FFU_TextureMatrix10:
case FFU_TextureMatrix11:
case FFU_TextureMatrix12:
case FFU_TextureMatrix13:
case FFU_TextureMatrix14:
case FFU_TextureMatrix15:
{
int idx = ( ue - FFU_TextureMatrix0 );
RegalAssertArrayIndex( texture, idx );
if ( ui.ver != texture[ idx ].Ver() )
{
ui.ver = texture[ idx ].Ver();
r3::Matrix4f m = texture[ idx ].Top();
if( inverse ) {
m = m.Inverse();
}
if( transpose ) {
m = m.Transpose();
}
tbl.glUniformMatrix4fv( ui.slot, 1, GL_FALSE, m.Ptr() );
}
break;
}
case FFU_TextureEnvColor0:
case FFU_TextureEnvColor1:
case FFU_TextureEnvColor2:
case FFU_TextureEnvColor3:
case FFU_TextureEnvColor4:
case FFU_TextureEnvColor5:
case FFU_TextureEnvColor6:
case FFU_TextureEnvColor7:
case FFU_TextureEnvColor8:
case FFU_TextureEnvColor9:
case FFU_TextureEnvColor10:
case FFU_TextureEnvColor11:
case FFU_TextureEnvColor12:
case FFU_TextureEnvColor13:
case FFU_TextureEnvColor14:
case FFU_TextureEnvColor15:
{
int idx = ( ((*i).first) - FFU_TextureEnvColor0 );
RegalAssertArrayIndex( textureEnvColorVer, idx );
if ( ui.ver != textureEnvColorVer[ idx ] )
{
ui.ver = textureEnvColorVer[ idx ];
tbl.glUniform4fv( ui.slot, 1, &textureEnvColor[ idx ].x);
}
break;
}
case FFU_Light0:
case FFU_Light1:
case FFU_Light2:
case FFU_Light3:
case FFU_Light4:
case FFU_Light5:
case FFU_Light6:
case FFU_Light7:
{
int idx = ( ((*i).first) - FFU_Light0 );
RegalAssertArrayIndex( u.light, idx );
if ( ui.ver != u.light[ idx ].ver )
{
ui.ver = u.light[ idx ].ver;
tbl.glUniform4fv( ui.slot, LE_Elements, &u.light[ idx ].ambient.x);
}
break;
}
case FFU_MaterialFront:
case FFU_MaterialBack:
{
int idx = ( ((*i).first) - FFU_MaterialFront );
RegalAssertArrayIndex( u.mat, idx );
if ( ui.ver != u.mat[ idx ].ver )
{
ui.ver = u.mat[ idx ].ver;
tbl.glUniform4fv( ui.slot, ME_Elements, &u.mat[ idx ].ambient.x);
}
break;
}
case FFU_LightModelAmbient:
{
tbl.glUniform4fv( ui.slot, 1, &u.lightModelAmbient.x);
break;
}
case FFU_Texgen0ObjS:
case FFU_Texgen0ObjT:
case FFU_Texgen0ObjR:
case FFU_Texgen0ObjQ:
case FFU_Texgen0EyeS:
case FFU_Texgen0EyeT:
case FFU_Texgen0EyeR:
case FFU_Texgen0EyeQ:
case FFU_Texgen1ObjS:
case FFU_Texgen1ObjT:
case FFU_Texgen1ObjR:
case FFU_Texgen1ObjQ:
case FFU_Texgen1EyeS:
case FFU_Texgen1EyeT:
case FFU_Texgen1EyeR:
case FFU_Texgen1EyeQ:
case FFU_Texgen2ObjS:
case FFU_Texgen2ObjT:
case FFU_Texgen2ObjR:
case FFU_Texgen2ObjQ:
case FFU_Texgen2EyeS:
case FFU_Texgen2EyeT:
case FFU_Texgen2EyeR:
case FFU_Texgen2EyeQ:
case FFU_Texgen3ObjS:
case FFU_Texgen3ObjT:
case FFU_Texgen3ObjR:
case FFU_Texgen3ObjQ:
case FFU_Texgen3EyeS:
case FFU_Texgen3EyeT:
case FFU_Texgen3EyeR:
case FFU_Texgen3EyeQ:
{
int idx = ( (*i).first ) - FFU_Texgen0ObjS;
int comp = idx % 4;
int unit = idx >> 3;
RegalAssertArrayIndex( u.tex, unit );
RegalAssertArrayIndex( u.tex[unit].texgen, comp );
const State::TexgenUniform & tg = u.tex[unit].texgen[comp];
if ( idx & 4 )
{
if ( ui.ver != tg.eyeVer )
{
ui.ver = tg.eyeVer;
tbl.glUniform4fv( ui.slot, 1, & tg.eye.x );
}
}
else
{
if ( ui.ver != tg.objVer )
{
ui.ver = tg.objVer;
tbl.glUniform4fv( ui.slot, 1, & tg.obj.x );
}
}
break;
}
case FFU_ClipPlane0:
case FFU_ClipPlane1:
case FFU_ClipPlane2:
case FFU_ClipPlane3:
case FFU_ClipPlane4:
case FFU_ClipPlane5:
case FFU_ClipPlane6:
case FFU_ClipPlane7:
{
int idx = ( (*i).first ) - FFU_ClipPlane0;
RegalAssertArrayIndex( u.clip, idx );
if ( ui.ver != u.clip[idx].ver )
{
ui.ver = u.clip[idx].ver;
tbl.glUniform4fv( ui.slot, 1, & u.clip[idx].plane.x );
}
break;
}
case FFU_Fog:
{
if ( ui.ver != u.fog.ver )
{
ui.ver = u.fog.ver;
tbl.glUniform4fv( ui.slot, 2, &u.fog.params[0].x);
}
break;
}
case FFU_AlphaRef:
{
if ( ui.ver != u.alphaTest.ver )
{
ui.ver = u.alphaTest.ver;
tbl.glUniform2f( ui.slot, u.alphaTest.alphaRef, u.alphaTest.alphaTestEnable );
}
break;
}
case FFU_Attrib:
{
if ( ui.ver != u.vabVer )
{
ui.ver = u.vabVer;
tbl.glUniform4fv( ui.slot, REGAL_EMU_MAX_VERTEX_ATTRIBS, & immVab[0].x );
}
break;
}
default:
break;
}
}
}
// a debug routine for forcing instanced program's uniforms to be updated
void Iff::ClearVersionsForProgram( RegalContext * ctx )
{
Internal("Regal::Iff::UpdateUniforms", boost::print::optional(ctx,Logging::pointers));
UniformMap * umap = NULL;
if( currinst && currinst->prevInstance) {
UserProgramInstanceInfo & ii = *currinst;
UserProgramInstance & upi = *ii.prevInstance;
upi.ffver = ~GLuint64(0);
umap = & upi.ffuniforms;
} else {
if( currprog == NULL ) {
return;
}
Program & pgm = *currprog;
pgm.ver = ~GLuint64(0);
umap = & pgm.uniforms;
}
for ( UniformMap::iterator i = umap->begin(); i != umap->end(); ++i )
{
UniformInfo & ui = (*i).second;
ui.ver = ~GLuint64(0);
}
}
inline bool operator == ( const Iff::State::Light & lhs, const Iff::State::Light & rhs )
{
if ( !(lhs.enable == rhs.enable) )
return false;
if ( !(lhs.spotlight == rhs.spotlight) )
return false;
if ( !(lhs.attenuate == rhs.attenuate) )
return false;
if ( !(lhs.local == rhs.local) )
return false;
return true;
}
inline bool operator == ( const Iff::TexenvCombineState & lhs, const Iff::TexenvCombineState & rhs )
{
if ( !(lhs.mode == rhs.mode) )
return false;
if ( !(lhs.src0 == rhs.src0) )
return false;
if ( !(lhs.src1 == rhs.src1) )
return false;
if ( !(lhs.src2 == rhs.src2) )
return false;
if ( !(lhs.op0 == rhs.op0) )
return false;
if ( !(lhs.op1 == rhs.op1) )
return false;
if ( !(lhs.op2 == rhs.op2) )
return false;
if ( !(lhs.mode == rhs.mode) )
return false;
if (lhs.scale != rhs.scale)
return false;
return true;
}
inline bool operator == ( const Iff::TextureEnv & lhs, const Iff::TextureEnv & rhs )
{
if ( !(lhs.mode == rhs.mode) )
return false;
if ( !(lhs.rgb == rhs.rgb) )
return false;
if ( !(lhs.a == rhs.a) )
return false;
return true;
}
inline bool operator == ( const Iff::TextureUnit & lhs, const Iff::TextureUnit & rhs )
{
if (lhs.ttb != rhs.ttb)
return false;
if (lhs.fmt != rhs.fmt)
return false;
if ( !(lhs.env == rhs.env) )
return false;
return true;
}
inline bool operator == ( const Iff::State::Texgen & lhs, const Iff::State::Texgen & rhs )
{
if (lhs.enable != rhs.enable)
return false;
if ( !(lhs.mode == rhs.mode) )
return false;
return true;
}
inline bool operator == ( const Iff::State::Texture & lhs, const Iff::State::Texture & rhs )
{
if (lhs.enables != rhs.enables)
return false;
if (lhs.useMatrix != rhs.useMatrix)
return false;
if ( !(lhs.unit == rhs.unit) )
return false;
size_t n = array_size( lhs.texgen );
for (size_t ii=0; ii<n; ii++)
{
RegalAssertArrayIndex( lhs.texgen, ii );
if ( !(lhs.texgen[ii] == rhs.texgen[ii]) )
return false;
}
return true;
}
inline bool operator == ( const Iff::State::Store & lhs, const Iff::State::Store & rhs )
{
if (lhs.hash != rhs.hash)
return false;
if (lhs.colorSum != rhs.colorSum)
return false;
if (lhs.rescaleNormal != rhs.rescaleNormal)
return false;
if (lhs.normalize != rhs.normalize)
return false;
if (lhs.shadeModelFlat != rhs.shadeModelFlat)
return false;
if (lhs.lighting != rhs.lighting)
return false;
if (lhs.lightModelLocalViewer != rhs.lightModelLocalViewer)
return false;
if (lhs.lightModelTwoSide != rhs.lightModelTwoSide)
return false;
if (lhs.lightModelSeparateSpecular != rhs.lightModelSeparateSpecular)
return false;
if (lhs.colorMaterial != rhs.colorMaterial)
return false;
if (lhs.attrArrayFlags != rhs.attrArrayFlags)
return false;
if (lhs.colorMaterialTarget0 != rhs.colorMaterialTarget0)
return false;
if (lhs.colorMaterialTarget1 != rhs.colorMaterialTarget1)
return false;
size_t n = array_size( lhs.light );
for (size_t ii=0; ii<n; ii++)
{
RegalAssertArrayIndex( lhs.light, ii );
if ( !(lhs.light[ii] == rhs.light[ii]) )
return false;
}
n = array_size( lhs.tex );
for (size_t ii=0; ii<n; ii++)
{
RegalAssertArrayIndex( lhs.tex, ii );
if ( !(lhs.tex[ii] == rhs.tex[ii]) )
return false;
}
n = array_size( lhs.clipPlaneEnabled );
for (size_t ii=0; ii<n; ii++)
{
RegalAssertArrayIndex( lhs.clipPlaneEnabled, ii );
if ( !(lhs.clipPlaneEnabled[ii] == rhs.clipPlaneEnabled[ii]) )
return false;
}
return true;
}
GLuint Iff::GetFixedFunctionStateHash() {
ffstate.Process( this ); // this early outs if version numbers match between raw and processed
return ffstate.processed.hash;
}
void Iff::UseFixedFunctionProgram( RegalContext * ctx )
{
Internal("Regal::Iff::UseFixedFunctionProgram", boost::print::optional(ctx,Logging::pointers));
if ( currprog != NULL && currprog->ver == ver.Current() )
{
return;
}
ffstate.Process( this );
uint32_t base = ( static_cast<uint32_t>(ffstate.processed.hash) & REGAL_FIXED_FUNCTION_PROGRAM_CACHE_MASK ) << REGAL_FIXED_FUNCTION_PROGRAM_CACHE_SET_BITS;
int match = -1;
const uint32_t n = static_cast<int>(array_size( ffprogs ));
for ( uint32_t i = 0; i < REGAL_FIXED_FUNCTION_PROGRAM_CACHE_SET; i++ )
{
RegalAssertArrayIndex( ffprogs, base + i );
if ( base + i >= n )
continue;
if ( ffprogs[ base + i ].store == ffstate.processed )
{
match = i;
break;
}
}
Program * p = NULL;
if ( match < 0 )
{
match = 0;
progcount++;
for ( int i = 1; i < REGAL_FIXED_FUNCTION_PROGRAM_CACHE_SET; i++ )
{
RegalAssertArrayIndex( ffprogs, base + i );
RegalAssertArrayIndex( ffprogs, base + match );
if ( ffprogs[ base + i ].ver < ffprogs[ base + match ].ver )
{
match = i;
}
}
RegalAssertArrayIndex( ffprogs, base + match );
p = & ffprogs[ base + match ];
// delete this program
if ( p->pg != 0 )
{
DispatchTableGL & tbl = ctx->dispatcher.emulation;
tbl.call(&tbl.glDeleteShader)( p->vs );
tbl.call(&tbl.glDeleteShader)( p->fs );
tbl.call(&tbl.glDeleteProgram)( p->pg );
*p = Program();
}
GLuint vs = CreateFixedFunctionVertexShader( ctx );
GLuint fs = CreateFixedFunctionFragmentShader( ctx );
p->Init( ctx, ffstate.processed, vs, fs );
p->progcount = progcount;
}
RegalAssertArrayIndex( ffprogs, base + match );
currprog = & ffprogs[ base + match ];
ctx->dispatcher.emulation.glUseProgram( currprog->pg );
UpdateUniforms( ctx );
}
GLuint Iff::CreateFixedFunctionVertexShader( RegalContext * ctx ) {
ffstate.Process( this );
string_list vsSrc;
GenerateVertexShaderSource( this, ffstate, vsSrc );
GLuint vs;
Program::Shader( ctx, ctx->dispatcher.emulation, GL_VERTEX_SHADER, vs, vsSrc.str().c_str() );
return vs;
}
GLuint Iff::CreateFixedFunctionFragmentShader( RegalContext * ctx ) {
ffstate.Process( this );
string_list fsSrc;
GenerateFragmentShaderSource( this, fsSrc );
GLuint fs;
Program::Shader( ctx, ctx->dispatcher.emulation, GL_FRAGMENT_SHADER, fs, fsSrc.str().c_str() );
return fs;
}
bool NeedsUserShaderProgramInstance( State::Store & st ) {
return st.alphaTest.enable && st.alphaTest.comp != Iff::CF_Always;
}
void Iff::UseShaderProgram( RegalContext * ctx )
{
Internal("Regal::Iff::UseShaderProgram", boost::print::optional(ctx,Logging::pointers));
ffstate.Process( this );
// update currprog if necessary
if ( (currprog == NULL) || (currprog->pg != program) ) {
RegalAssert( shprogmap.count( program ) != 0 );
currprog = & shprogmap[ program ];
}
if ( currprog->pg == 0 ) {
Warning( "The program is 0. That can't be right.\n" );
return;
}
if ( currprog->ver == ver.Current() ) {
// no updates necessary
return;
}
// determine if instancing is required
if( (currprog->instanced == false) && NeedsUserShaderProgramInstance( ffstate.raw ) ) {
currprog->instanced = true;
}
// if instanced, update per-instance uniforms if necessary
DispatchTableGL & tbl = ctx->dispatcher.emulation;
if( currprog->instanced ) {
if( currinst == NULL ) {
currinst = & inst[ currprog->pg ];
}
// create the primary program object, and make it the "vanilla" version the first instance
if( currinst->instances.size() == 0 ) {
ShaderInstance::InitProgram( tbl, currprog->pg, currinst->program );
UserProgramInstanceKey k;
ShaderInstance::InitProgramInstance( tbl, currinst->program, currprog->pg, currinst->instances[ k ].inst );
currinst->prevKey = k;
currinst->prevInstance = &currinst->instances[ k ];
currinst->prevInstance->LocateUniforms( ctx, tbl );
}
UserProgramInstanceKey k( ffstate.processed );
if( currinst->prevInstance == NULL || k != currinst->prevKey ) {
currinst->prevKey = k;
currinst->prevInstance = & currinst->instances[ k ];
if( currinst->prevInstance->inst.prog != 0 ) {
tbl.call(&tbl.glUseProgram)( currinst->prevInstance->inst.prog );
}
}
UserProgramInstance & upi = * currinst->prevInstance;
// create this instance of the program
if( upi.inst.prog == 0 ) {
std::vector<ShaderInstance::ShaderSource> sources;
// alter the original sources as necessary, use a fetched or cached copy as preferred
ShaderInstance::GetProgramSources( tbl, currinst->program.prog, sources);
for( size_t i = 0; i < sources.size(); i++ ) {
ShaderInstance::ShaderSource & ss = sources[i];
string output_src;
// If a tranformation or optimization failed should we optionally fallback
// to a passthru shader if it's a fragment shader?
// For now, blame glsl parse and fall back to existing source string.
if ( OptimizeGLSL( gles, ss.type, ss.src, output_src, k.alphaFunc ) ) {
ss.src = output_src;
}
}
ShaderInstance::CreateProgramInstance( tbl, currinst->program, sources, upi.inst );
upi.LocateUniforms( ctx, tbl );
tbl.call(&tbl.glUseProgram)( upi.inst.prog );
}
if( upi.inst.ver != currinst->program.ver ) {
upi.inst.UpdateUniforms( tbl, currinst->program );
upi.inst.ver = currinst->program.ver;
}
}
UpdateUniforms( ctx );
}
static int remove_version( string &str) {
if( str.size() == 0 ) {
return -1;
}
size_t pos = 0;
while( (pos = str.find( "#version ", pos ) ) != string::npos ) {
if( pos == 0 || str[pos-1] == '\n' ) { // at beginning of line
str[ pos + 0 ] = '/';
str[ pos + 1 ] = '/';
string ver = str.substr( pos + 9, 3 );
return atoi( ver.c_str() );
}
pos += 9;
}
return -1;
}
static void remove_precision( string & str ) {
if( str.size() == 0 ) {
return;
}
size_t pos = 0;
while( (pos = str.find( "precision ", pos ) ) != string::npos ) {
if( pos == 0 || str[pos-1] == '\n' ) { // at beginning of line
str[ pos + 0 ] = '/';
str[ pos + 1 ] = '/';
}
pos += 10;
}
}
// replace ftransform with "rgl_ftform" in order to avoid conflict with possibly deprecated ftransform
static bool replace_ftransform( string & str ) {
if( str.size() == 0 ) {
return false;
}
size_t pos = 0;
while ( (pos = str.find( "ftransform", pos ) ) != string::npos ) {
int i = 0;
// make sure ftransform is followed by "("
while ( (str[ pos + 10 + i ] == ' ') && (pos != string::npos) )
i++;
if ( str[ pos + 10 + i ] != '(' )
break;
str.replace( pos, 10, "rgl_ftform", 10 );
pos += 10;
return true;
}
return false;
}
void Iff::ShaderSource( RegalContext *ctx, GLuint shader, GLsizei count, const GLchar * const * srcstr, const GLint *length)
{
string src;
for ( int i = 0; i < count; i++ ) {
src += length != NULL ? string( srcstr[i], length[i] ) : string( srcstr[i] );
}
// comment out version in-place
int version = -1;
version = remove_version( src );
// if used, replace ftransform with rgl_ftform
bool uses_ftransform = replace_ftransform( src );
if ( legacy ) {
remove_precision( src );
}
// Preamble
string_list ss;
if (gles) {
// hack around #version 100 on x86 failing compilation
if ( ctx->info->gl_version_major >= 3 ) {
ss << "#version 120\n";
ss << "#define precision\n";
} else {
#if REGAL_FORCE_DESKTOP_GLSL
ss << "#version 140\n";
#else
ss << "#version 100\n";
ss << "#extension GL_EXT_shadow_samplers : enable\n";
ss << "#define shadow2D(a,b) vec4(shadow2DEXT(a,b))\n";
#endif
}
} else if (legacy) {
ss << "#version 120\n";
ss << "#define precision\n";
} else {
if (version > 0) {
// We should honor the version in the original shader if we can, but leave out for now.
//ss << "#version " << version << "\n";
if (shaderTypeMap[ shader ] == GL_VERTEX_SHADER) {
ss << "#define in attribute\n";
ss << "#define out varying\n";
} else {
ss << "#define in varying\n";
}
} else {
ss << "#version 140\n";
}
}
if (gles || legacy) {
if (shaderTypeMap[ shader ] == GL_VERTEX_SHADER) {
ss << "#define in attribute\n";
ss << "#define out varying\n";
} else {
ss << "#define in varying\n";
}
} else {
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER ) {
} else {
ss << "#define gl_FragColor rglFragColor\n";
ss << "out vec4 rglFragColor;\n";
ss << "#define texture1D texture\n";
ss << "#define texture2D texture\n";
ss << "#define textureCube texture\n";
}
}
if ( gles ) {
ss << "precision highp float;\n";
}
ss << "#define centroid \n";
ss << "#define gl_FogFragCoord rglFogFragCoord\n";
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER ) {
ss << "#define gl_Vertex rglVertex\n";
ss << "#define gl_Color rglColor\n";
ss << "#define gl_Normal rglNormal\n";
} else {
ss << "#define gl_Color rglFrontColor\n";
}
ss << "#define gl_FrontColor rglFrontColor\n";
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER ) {
ss << "#define gl_SecondaryColor rglSecondaryColor\n";
} else {
ss << "#define gl_SecondaryColor rglFrontSecondaryColor\n";
}
ss << "#define gl_FrontSecondaryColor rglFrontSecondaryColor\n";
ss << "#define gl_ClipVertex rglClipVertex\n";
ss << "#define gl_FragDepth rglFragDepth\n";
if ( gles ) {
ss << "#define texture3d texture2d\n";
ss << "#define texture3D(a,b) texture2D(a,b.xy)\n";
ss << "#define sampler3D sampler2D\n";
}
if ( src.find( "gl_FogFragCoord" ) != string::npos ) {
ss << "out float rglFogFragCoord;\n";
}
// NOTE: ES 2.0 does not support gl_FragDepth, so we just discard it, for now
// See: http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf
if ( src.find( "gl_FragDepth" ) != string::npos ) {
ss << (shaderTypeMap[shader]==GL_VERTEX_SHADER ? "out" : " ") << " float rglFragDepth;\n";
}
if ( uses_ftransform || (src.find( "gl_Vertex" ) != string::npos) ) {
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER ) {
ss << "in vec4 rglVertex;\n";
}
}
if ( src.find( "gl_Normal" ) != string::npos ) {
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER ) {
ss << "in vec3 rglNormal;\n";
}
}
if ( src.find( "gl_Color" ) != string::npos ) {
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER ) {
ss << "in vec4 rglColor;\n";
} else {
ss << "in vec4 rglFrontColor;\n";
}
}
if ( src.find( "gl_FrontColor" ) != string::npos ) {
ss << "out vec4 rglFrontColor;\n";
}
// NOTE: gl_SecondaryColor can be a vertex shader output, or a fragment shader input.
// See: http://www.opengl.org/registry/doc/GLSLangSpec.4.30.7.pdf
if ( src.find( "gl_SecondaryColor" ) != string::npos ) {
if ( shaderTypeMap[ shader ] == GL_VERTEX_SHADER )
{
ss << "in vec4 rglSecondaryColor;\n";
}
else
{
ss << "in vec4 rglFrontSecondaryColor;\n";
}
}
if ( src.find( "gl_FrontSecondaryColor" ) != string::npos ) {
ss << "out vec4 rglFrontSecondaryColor;\n";
}
if ( src.find( "gl_ClipVertex" ) != string::npos ) {
// should be "out", but temporarily disabled due to register pressure concerns
// ss << "out vec4 rglClipVertex;\n";
ss << "vec4 rglClipVertex;\n";
}
// the below needs to be automated instead of done ad hoc like this - Cass
// and it needs to handle "Inverse", "Transpose", and "InverseTranspose".
if ( src.find( "gl_NormalMatrix" ) != string::npos ) {
ss << "uniform mat3 rglNormalMatrix;\n";
}
if( src.find( "gl_ModelViewMatrix" ) != string::npos || uses_ftransform ) {
ss << "uniform mat4 rglModelViewMatrix;\n";
}
if( src.find( "gl_ProjectionMatrix" ) != string::npos || uses_ftransform ) {
ss << "uniform mat4 rglProjectionMatrix;\n";
}
if( src.find( "gl_ModelViewProjectionMatrix" ) != string::npos ) {
ss << "uniform mat4 rglModelViewProjectionMatrix;\n";
}
const char *nums = "01234567";
for( int i = 0; i < 8; i++ ) {
if( src.find( string("gl_MultiTexCoord") + string( nums + i, 1 )) != string::npos ) {
ss << "#define gl_MultiTexCoord" << i << " rglMultiTexCoord" << i << "\n";
ss << "in vec4 rglMultiTexCoord" << i << ";\n";
}
}
const char * matrixSuffix[] = { "", "Inverse", "Transpose", "InverseTranspose" };
for( int i = 0; i < 4; i++ ) {
ss << "#define gl_ModelViewMatrix" << matrixSuffix[i] << " rglModelViewMatrix" << matrixSuffix[i] << "\n";
ss << "#define gl_ProjectionMatrix" << matrixSuffix[i] << " rglProjectionMatrix" << matrixSuffix[i] << "\n";
ss << "#define gl_ModelViewProjectionMatrix" << matrixSuffix[i] << " rglModelViewProjectionMatrix" << matrixSuffix[i] << "\n";
ss << "#define gl_TextureMatrix0" << matrixSuffix[i] << " rglTextureMatrix0" << matrixSuffix[i] << "\n";
ss << "\n";
}
ss << "#define gl_NormalMatrix rglNormalMatrix\n";
ss << "#define gl_Sampler0 rglSampler0\n\n";
if ( uses_ftransform )
{
ss << "vec4 rgl_ftform() { return gl_ProjectionMatrix * gl_ModelViewMatrix * rglVertex; }\n\n";
}
string preamble = ss.str();
src = preamble + src;
const GLchar * dumb = static_cast<const GLchar *>( src.c_str() );
const GLchar ** dumber = & dumb;
ctx->dispatcher.emulation.glShaderSource( shader, 1, dumber, NULL );
}
void Iff::LinkProgram( RegalContext *ctx, GLuint program )
{
// need to bind attribs just before link for Regal-generated attribs
if ( program != 0 )
{
if ( shprogmap.count( program ) == 0 )
{
ffstate.Process( this );
ver.Reset();
Program & p = shprogmap[ program ];
p.pg = program;
p.UserShaderModeAttribs(ctx);
}
}
DispatchTableGL & tbl = ctx->dispatcher.emulation;
tbl.glLinkProgram( program );
Program & p = shprogmap[ program ];
tbl.call(&tbl.glUseProgram)( program );
p.Samplers( ctx, tbl );
p.Uniforms( ctx, tbl );
if( this->program != 0 ) {
tbl.call(&tbl.glUseProgram)( this->program );
}
}
void Iff::Uniform( RegalContext *ctx, GLenum type, int vecSize, GLint loc, GLsizei count, const void * data ) {
UNUSED_PARAMETER(ctx);
UNUSED_PARAMETER(type);
UNUSED_PARAMETER(vecSize);
ShaderInstance::Program & p = currinst->program;
p.UpdateUniformStore( loc, count, data );
}
void Iff::Uniform( RegalContext *ctx, GLenum type, int cols, int rows, GLint loc, GLsizei count, const void * data ) {
UNUSED_PARAMETER(ctx);
UNUSED_PARAMETER(type);
UNUSED_PARAMETER(cols);
UNUSED_PARAMETER(rows);
ShaderInstance::Program & p = currinst->program;
p.UpdateUniformStore( loc, count, data );
}
}; // namespace Emu
REGAL_NAMESPACE_END
#endif // REGAL_EMULATION