// STATIC: "DUALSEQUENCE" "0..1" // DYNAMIC: "ORIENTATION" "0..2" #include "common_cbuffers_fxc.h" #include "common_vs_fxc.h" #include "spritecard_constants_fxc.h" CBUFFER_PERMODEL( register( b0 ) ) CBUFFER_PERFRAME( register( b1 ) ) CBUFFER_PERSCENE( register( b2 ) ) cbuffer SpriteCard_CBuffer : register( b3 ) { SpriteCard_t c; }; #define OLDFRM_SCALE_START (c.ScaleParms.x) #define OLDFRM_SCALE_END (c.ScaleParms.y) #define g_bZoomAnimateSeq2 (c.SpriteControls.x != 0) #define g_bExtractGreenAlpha (c.SpriteControls.y != 0) #define g_bUseInstancing (c.SpriteControls.z != 0) #define MINIMUM_SIZE_FACTOR (c.SizeParms.x) #define MAXIMUM_SIZE_FACTOR (c.SizeParms.y) #define START_FADE_SIZE_FACTOR (c.SizeParms.z) #define END_FADE_SIZE_FACTOR (c.SizeParms.w) // alpha fade w/ distance #define START_FAR_FADE ( c.SizeParms2.x ) #define FAR_FADE_FACTOR ( c.SizeParms2.y ) // alpha = 1-min(1,max(0, (dist-start_fade)*factor)) struct VS_INPUT { // This is all of the stuff that we ever use. float4 vTint : COLOR; float4 vPos : POSITION; float4 vTexCoord0 : TEXCOORD0; float4 vTexCoord1 : TEXCOORD1; float4 vParms : TEXCOORD2; // frame blend, rot, radius, yaw // FIXME: remove this vertex element for (USE_INSTANCING == 1), need to shuffle the following elements down float2 vCornerID : TEXCOORD3; // 0,0 1,0 1,1 0,1 float4 vTexCoord2 : TEXCOORD4; #if DUALSEQUENCE float4 vSeq2TexCoord0 : TEXCOORD5; float4 vSeq2TexCoord1 : TEXCOORD6; float4 vParms1 : TEXCOORD7; // second frame blend, ?,?,? #endif }; struct VS_OUTPUT { float4 projPos : SV_POSITION; float2 texCoord0 : TEXCOORD0; float2 texCoord1 : TEXCOORD1; float4 argbcolor : COLOR; float4 blendfactor0 : TEXCOORD2; float2 texCoord2 : TEXCOORD3; float4 blendfactor1 : TEXCOORD4; // for extracting green/alpha #if DUALSEQUENCE float2 vSeq2TexCoord0 : TEXCOORD5; float2 vSeq2TexCoord1 : TEXCOORD6; #endif float4 vScreenPos : TEXCOORD7; }; #define BLENDFACTOR v.vParms.x #define ROTATION v.vParms.y #define RADIUS v.vParms.z #define YAW (v.vParms.w) float getlerpscaled( float l_in, float s0, float s1, float ts ) { l_in = 2.0*(l_in-.5); l_in *= lerp(s0,s1,ts); return 0.5+0.5*l_in; } float getlerpscale_for_old_frame( float l_in, float ts ) { return getlerpscaled( l_in, OLDFRM_SCALE_START, OLDFRM_SCALE_END, ts); } float getlerpscale_for_new_frame( float l_in, float ts ) { return getlerpscaled( l_in, 1.0, OLDFRM_SCALE_START, ts ); } #ifdef DO_INSTANCING void InstancedVertexRead( inout VS_INPUT v, int index ) { // Duplicate each VB vertex 4 times (and generate vCornerID - the only thing that varies per-corner) float4 vTint; float4 vPos; float4 vTexCoord0; float4 vTexCoord1; float4 vParms; float4 vTexCoord2; float4 vSeq_TexCoord0; // NOTE: April XDK compiler barfs on var names which have a number in the middle! (i.e. vSeq2TexCoord0) float4 vSeq_TexCoord1; float4 vParms1; int spriteIndex = index / 4; int cornerIndex = index - 4*spriteIndex; asm { vfetch vTint, spriteIndex, color0; vfetch vPos, spriteIndex, position0; vfetch vTexCoord0, spriteIndex, texcoord0; vfetch vTexCoord1, spriteIndex, texcoord1; vfetch vParms, spriteIndex, texcoord2; vfetch vTexCoord2, spriteIndex, texcoord4; #if DUALSEQUENCE vfetch vSeq_TexCoord0, spriteIndex, texcoord5; vfetch vSeq_TexCoord1, spriteIndex, texcoord6; vfetch vParms1, spriteIndex, texcoord7; #endif }; v.vTint = vTint; v.vPos = vPos; v.vTexCoord0 = vTexCoord0; v.vTexCoord1 = vTexCoord1; v.vParms = vParms; v.vTexCoord2 = vTexCoord2; #if DUALSEQUENCE v.vSeq2TexCoord0 = vSeq_TexCoord0; v.vSeq2TexCoord1 = vSeq_TexCoord1; v.vParms1 = vParms1; #endif // Compute vCornerID - order is: (0,0) (1,0) (1,1) (0,1) // float2 IDs[4] = { {0,0}, {1,0}, {1,1}, {0,1} }; // v.vCornerID.xy = IDs[ cornerIndex ]; // This compiles to 2 ops on 360 (MADDs with abs/sat register read/write modifiers): v.vCornerID.xy = float2( 1.5f, 0.0f ) + cornerIndex*float2( -1.0f, 1.0f ); v.vCornerID.xy = saturate( float2(1.5f, -3.0f) + float2( -1.0f, 2.0f )*abs( v.vCornerID.xy ) ); } #endif VS_OUTPUT main( const VS_INPUT v ) { VS_OUTPUT o; #ifdef DO_INSTANCING if ( g_bUseInstancing ) { InstancedVertexRead( v, Index ); } #endif float4 tint = GammaToLinear( v.vTint ); float2 sc_yaw; sincos( YAW, sc_yaw.y, sc_yaw.x ); float2 sc; sincos( ROTATION, sc.y, sc.x ); float2 ix=2*v.vCornerID.xy-1; float x1=dot(ix,sc); float y1=sc.x*ix.y-sc.y*ix.x; float4 projPos; float3 worldPos; worldPos = mul( float4( v.vPos.xyz, 1.0f ), cModelMatrix ).xyz; float rad = RADIUS; float3 v2p = ( worldPos - cEyePos.xyz ); float l = length(v2p); rad=max(rad, MINIMUM_SIZE_FACTOR * l); // now, perform fade out if ( rad > START_FADE_SIZE_FACTOR * l ) { if ( rad > END_FADE_SIZE_FACTOR *l ) { tint = 0; rad = 0; // cull so we emit 0-sized sprite } else { tint *= 1-(rad-START_FADE_SIZE_FACTOR*l)/(END_FADE_SIZE_FACTOR*l-START_FADE_SIZE_FACTOR*l); } } // perform far fade float tscale = 1-min(1, max(0, (l-START_FAR_FADE)*FAR_FADE_FACTOR) ); tint *= tscale; if ( tscale <= 0) rad = 0; // cull so we emit 0-sized sprite rad=min(rad, MAXIMUM_SIZE_FACTOR * l); #if ORIENTATION == 0 // Screen-aligned case float3 viewPos; float4x4 modelView = mul( cModelMatrix, cViewMatrix ); viewPos = mul( v.vPos, modelView ).xyz; float3 disp=float3( -x1,y1,0); float tmpx=disp.x*sc_yaw.x+disp.z*sc_yaw.y; disp.z = disp.z*sc_yaw.x-disp.x*sc_yaw.y; disp.x=tmpx; viewPos.xyz += disp * rad; projPos = mul( float4(viewPos, 1.0f), cProjMatrix ); #endif #if ORIENTATION == 1 // Z-aligned case if (l > rad/2) { float3 up = float3(0,0,1); float3 right = normalize(cross(up, v2p)); float tmpx=right.x*sc_yaw.x+right.y*sc_yaw.y; right.y = right.y*sc_yaw.x-right.x*sc_yaw.y; right.x=tmpx; worldPos += (x1*rad)*right; worldPos.z += (y1*rad)*up.z; if (l < rad*2 ) { tint *= smoothstep(rad/2,rad,l); } } float4x4 viewProj = mul(cViewMatrix, cProjMatrix); projPos = mul( float4(worldPos, 1.0f), viewProj ); #endif #if ORIENTATION == 2 // aligned with z plane case - easy float3 wpos=v.vPos+RADIUS*float3( y1,x1,0); projPos = ComputeProjPos( wpos, cModelMatrix, cViewMatrix, cProjMatrix ); #endif o.blendfactor0 = float4( v.vParms.x, 0, 0, 0 ); o.projPos = projPos; o.texCoord0.x = lerp( v.vTexCoord0.z, v.vTexCoord0.x, v.vCornerID.x ); o.texCoord0.y = lerp( v.vTexCoord0.w, v.vTexCoord0.y, v.vCornerID.y ); o.texCoord1.x = lerp( v.vTexCoord1.z, v.vTexCoord1.x, v.vCornerID.x ); o.texCoord1.y = lerp( v.vTexCoord1.w, v.vTexCoord1.y, v.vCornerID.y ); o.texCoord2.x = lerp( v.vTexCoord2.z, v.vTexCoord2.x, v.vCornerID.x ); o.texCoord2.y = lerp( v.vTexCoord2.w, v.vTexCoord2.y, v.vCornerID.y ); #if DUALSEQUENCE float2 lerpold = v.vCornerID.xy; float2 lerpnew = v.vCornerID.xy; if ( g_bZoomAnimateSeq2 ) { lerpold.x = getlerpscale_for_old_frame( v.vCornerID.x, v.vParms1.x ); lerpold.y = getlerpscale_for_old_frame( v.vCornerID.y, v.vParms1.x ); lerpnew.x = getlerpscale_for_new_frame( v.vCornerID.x, v.vParms1.x ); lerpnew.y = getlerpscale_for_new_frame( v.vCornerID.y, v.vParms1.x ); } o.vSeq2TexCoord0.xy = lerp( v.vSeq2TexCoord0.zw, v.vSeq2TexCoord0.xy, lerpold.xy ); o.vSeq2TexCoord1.xy = lerp( v.vSeq2TexCoord1.zw, v.vSeq2TexCoord1.xy, lerpnew.xy ); o.blendfactor0.z = v.vParms1.x; #endif o.blendfactor1 = float4( 0.0f, 0.0f, 0.0f, 0.0f ); if ( g_bExtractGreenAlpha ) { // Input range Output range if ( v.vParms.x < 0.25f ) // 0.0 .. 0.25 { o.blendfactor0.a = v.vParms.x * 2 + 0.5f; // 0.5 .. 1.0 o.blendfactor0.g = 1 - o.blendfactor0.a; // 0.5 .. 0.0 } else if ( v.vParms.x < 0.75f ) // 0.25 .. 0.75 { o.blendfactor1.g = v.vParms.x * 2 - 0.5f; // 0.0 .. 1.0 o.blendfactor0.a = 1 - o.blendfactor1.g; // 1.0 .. 0.0 } else // 0.75 .. 1.0 { o.blendfactor1.a = v.vParms.x * 2 - 1.5f; // 0.0 .. 0.5 o.blendfactor1.g = 1 - o.blendfactor1.a; // 1.0 .. 0.5 } } // Map projected position to the refraction texture float2 vScreenPos; vScreenPos.x = projPos.x; vScreenPos.y = -projPos.y; // invert Y vScreenPos = (vScreenPos + projPos.w) * 0.5f; o.vScreenPos = float4(vScreenPos.x, vScreenPos.y, projPos.z, projPos.w ); o.argbcolor = tint; return o; }