;------------------------------------ ; RULES FOR AUTHORING VERTEX SHADERS: ;------------------------------------ ; - never use "def" . . .set constants in code instead. . our constant shadowing will break otherwise. ; (same goes for pixel shaders) ; - use cN notation instead of c[N] notation. .makes grepping for registers easier. ; The only exception is c[a0.x+blah] where you have no choice. $g_NumRegisters = 12; ; NOTE: These must match the same values in vsh_prep.pl! $vPos = "v0"; $vBoneWeights = "v1"; $vBoneIndices = "v2"; $vNormal = "v3"; if ( $g_xbox ) { $vPosFlex = "v4"; $vNormalFlex = "v13"; } $vColor = "v5"; $vSpecular = "v6"; $vTexCoord0 = "v7"; $vTexCoord1 = "v8"; $vTexCoord2 = "v9"; $vTexCoord3 = "v10"; $vTangentS = "v11"; $vTangentT = "v12"; $vUserData = "v14"; if( $g_dx9 ) { if( $g_usesPos ) { dcl_position $vPos; } if ( $g_xbox ) { if ( $g_usesPosFlex ) { dcl_position1 $vPosFlex; } if( $g_usesNormalFlex ) { dcl_normal1 $vNormalFlex; } } if( $g_usesBoneWeights ) { dcl_blendweight $vBoneWeights; } if( $g_usesBoneIndices ) { dcl_blendindices $vBoneIndices; } if( $g_usesNormal ) { dcl_normal $vNormal; } if( $g_usesColor ) { dcl_color0 $vColor; } if( $g_usesSpecular ) { dcl_color1 $vSpecular; } if( $g_usesTexCoord0 ) { dcl_texcoord0 $vTexCoord0; } if( $g_usesTexCoord1 ) { dcl_texcoord1 $vTexCoord1; } if( $g_usesTexCoord2 ) { dcl_texcoord2 $vTexCoord2; } if( $g_usesTexCoord3 ) { dcl_texcoord3 $vTexCoord3; } if( $g_usesTangentS ) { dcl_tangent $vTangentS; } if( $g_usesTangentT ) { dcl_binormal0 $vTangentT; } if( $g_usesUserData ) { dcl_tangent $vUserData; } } # NOTE: These should match g_LightCombinations in vertexshaderdx8.cpp! # NOTE: Leave this on single lines or shit might blow up. @g_staticLightTypeArray = ( "none", "static", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "static", "static", "static", "static", "static", "static", "static", "static", "static", "static" ); @g_ambientLightTypeArray = ( "none", "none", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient", "ambient" ); @g_localLightType1Array = ( "none", "none", "none", "spot", "point", "directional", "spot", "spot", "spot", "point", "point", "directional", "none", "spot", "point", "directional", "spot", "spot", "spot", "point", "point", "directional" ); @g_localLightType2Array = ( "none", "none", "none", "none", "none", "none", "spot", "point", "directional", "point", "directional", "directional", "none", "none", "none", "none", "spot", "point", "directional", "point", "directional", "directional" ); $cConstants0 = "c0"; $cZero = "c0.x"; $cOne = "c0.y"; $cTwo = "c0.z"; $cHalf = "c0.w"; $cConstants1 = "c1"; $cOOGamma = "c1.x"; # 1/2.2 $cOtherOverbrightFactor = "c1.y"; # overbright $cOneThird = "c1.z"; # 1/3 $cOverbrightFactor = "c1.w"; # 1/overbright $cEyePos = "c2"; $cWaterZ = "c2.w"; $cEyePosWaterZ = "c2"; $cLightIndex = "c3"; $cLight0Offset = "c3.x"; # 27 $cLight1Offset = "c3.y"; # 32 $cColorToIntScale = "c3.z"; # matrix array offset = 3.0f * 255.0f + 0.01 (epsilon ensures floor yields desired result) $cModel0Index = "c3.w"; # base for start of skinning matrices ; NOTE: These must match the same values in vsh_prep.pl! $cModelViewProj0 = "c4"; $cModelViewProj1 = "c5"; $cModelViewProj2 = "c6"; $cModelViewProj3 = "c7"; $cViewProj0 = "c8"; $cViewProj1 = "c9"; $cViewProj2 = "c10"; $cViewProj3 = "c11"; $SHADER_HALFLAMBERT = "c12.x"; $SHADER_FLEXSCALE = "c13.x"; ; currently unused ; c14, c15 $cFogParams = "c16"; $cFogEndOverFogRange = "c16.x"; $cFogOne = "c16.y"; ; NOTE: c16.z is unused $cOOFogRange = "c16.w"; # (1/(fogEnd-fogStart)) $cViewModel0 = "c17"; $cViewModel1 = "c18"; $cViewModel2 = "c19"; $cViewModel3 = "c20"; $cAmbientColorPosX = "c21"; $cAmbientColorNegX = "c22"; $cAmbientColorPosY = "c23"; $cAmbientColorNegY = "c24"; $cAmbientColorPosZ = "c25"; $cAmbientColorNegZ = "c26"; $cAmbientColorPosXOffset = "21"; $cAmbientColorPosYOffset = "23"; $cAmbientColorPosZOffset = "25"; $cLight0DiffColor = "c27"; $cLight0Dir = "c28"; $cLight0Pos = "c29"; $cLight0SpotParams = "c30"; # [ exponent, stopdot, stopdot2, 1 / (stopdot - stopdot2) $cLight0Atten = "c31"; # [ constant, linear, quadratic, 0.0f ] $cLight1DiffColor = "c32"; $cLight1Dir = "c33"; $cLight1Pos = "c34"; $cLight1SpotParams = "c35"; # [ exponent, stopdot, stopdot2, 1 / (stopdot - stopdot2) $cLight1Atten = "c36"; # [ constant, linear, quadratic, 0.0f ] $cModulationColor = "c37"; $SHADER_SPECIFIC_CONST_0 = "c38"; $SHADER_SPECIFIC_CONST_1 = "c39"; $SHADER_SPECIFIC_CONST_2 = "c40"; $SHADER_SPECIFIC_CONST_3 = "c41"; $SHADER_SPECIFIC_CONST_4 = "c42"; $SHADER_SPECIFIC_CONST_5 = "c43"; $SHADER_SPECIFIC_CONST_6 = "c44"; $SHADER_SPECIFIC_CONST_7 = "c45"; $SHADER_SPECIFIC_CONST_8 = "c46"; $SHADER_SPECIFIC_CONST_9 = "c47"; ; xbox reserved contants for viewport transform ; enabled extended range [-96..-1] for use by matrix palette skinning ; using extended range forced the clobbering of xbox viewport constants c-38,c-37 ; force xbox reserved to top of range to allow matix skinning a contiguous range $SHADER_VIEWPORT_CONST_OFFSET = "c-1"; $SHADER_VIEWPORT_CONST_SCALE = "c-2"; ; There are 16 model matrices for skinning ; NOTE: These must match the same values in vsh_prep.pl! $cModel0 = "c48"; $cModel1 = "c49"; $cModel2 = "c50"; sub OutputUsedRegisters { local( $i ); ; USED REGISTERS for( $i = 0; $i < $g_NumRegisters; $i++ ) { if( $g_allocated[$i] ) { ; $g_allocatedname[$i] = r$i } } ; } sub AllocateRegister { local( *reg ) = shift; local( $regname ) = shift; local( $i ); for( $i = 0; $i < $g_NumRegisters; $i++ ) { if( !$g_allocated[$i] ) { $g_allocated[$i] = 1; $g_allocatedname[$i] = $regname; ; AllocateRegister $regname = r$i $reg = "r$i"; &OutputUsedRegisters(); return; } } ; Out of registers allocating $regname! $reg = "rERROR_OUT_OF_REGISTERS"; &OutputUsedRegisters(); } ; pass in a reference to a var that contains a register. . ie \$var where var will constain "r1", etc sub FreeRegister { local( *reg ) = shift; local( $regname ) = shift; ; FreeRegister $regname = $reg if( $reg =~ m/rERROR_DEALLOCATED/ ) { ; $regname already deallocated ; $reg = "rALREADY_DEALLOCATED"; &OutputUsedRegisters(); return; } ; if( $regname ne g_allocatedname[$reg] ) ; { ; ; Error freeing $reg ; mov compileerror, freed unallocated register $regname ; } if( ( $reg =~ m/r(.*)/ ) ) { $g_allocated[$1] = 0; } $reg = "rERROR_DEALLOCATED"; &OutputUsedRegisters(); } sub CheckUnfreedRegisters() { local( $i ); for( $i = 0; $i < $g_NumRegisters; $i++ ) { if( $g_allocated[$i] ) { print "ERROR: r$i allocated to $g_allocatedname[$i] at end of program\n"; $g_allocated[$i] = 0; } } } sub Normalize { local( $r ) = shift; dp3 $r.w, $r, $r rsq $r.w, $r.w mul $r, $r, $r.w } sub Cross { local( $result ) = shift; local( $a ) = shift; local( $b ) = shift; mul $result.xyz, $a.yzx, $b.zxy mad $result.xyz, -$b.yzx, $a.zxy, $result } sub RangeFog { local( $projPos ) = shift; ;------------------------------ ; Regular range fog ;------------------------------ ; oFog.x = 1.0f = no fog ; oFog.x = 0.0f = full fog ; compute fog factor f = (fog_end - dist)*(1/(fog_end-fog_start)) ; this is == to: (fog_end/(fog_end-fog_start) - dist/(fog_end-fog_start) ; which can be expressed with a single mad instruction! ; Compute |projPos| local( $tmp ); &AllocateRegister( \$tmp ); dp3 $tmp.x, $projPos.xyw, $projPos.xyw rsq $tmp.x, $tmp.x rcp $tmp.x, $tmp.x if( $g_dx9 ) { mad $tmp, -$tmp.x, $cOOFogRange, $cFogEndOverFogRange min $tmp, $tmp, $cOne max oFog, $tmp.x, $cZero } else { mad $tmp, -$tmp.x, $cOOFogRange, $cFogEndOverFogRange min $tmp, $tmp, $cOne max oFog.x, $tmp.x, $cZero } &FreeRegister( \$tmp ); } sub DepthFog { local( $projPos ) = shift; local( $dest ) = shift; if ( $dest eq "" ) { $dest = "oFog"; } ;------------------------------ ; Regular range fog ;------------------------------ ; oFog.x = 1.0f = no fog ; oFog.x = 0.0f = full fog ; compute fog factor f = (fog_end - dist)*(1/(fog_end-fog_start)) ; this is == to: (fog_end/(fog_end-fog_start) - dist/(fog_end-fog_start) ; which can be expressed with a single mad instruction! ; Compute |projPos| local( $tmp ); &AllocateRegister( \$tmp ); if( $g_dx9 ) { mad $tmp, -$projPos.w, $cOOFogRange, $cFogEndOverFogRange min $tmp, $tmp, $cOne max $dest, $tmp.x, $cZero } else { mad $tmp, -$projPos.w, $cOOFogRange, $cFogEndOverFogRange min $tmp, $tmp, $cOne max $dest.x, $tmp.x, $cZero } &FreeRegister( \$tmp ); } sub WaterRangeFog { ; oFog.x = 1.0f = no fog ; oFog.x = 0.0f = full fog ; only $worldPos.z is used out of worldPos local( $worldPos ) = shift; local( $projPos ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); ; This is simple similar triangles. Imagine a line passing from the point directly vertically ; and another line passing from the point to the eye position. ; Let d = total distance from point to the eye ; Let h = vertical distance from the point to the eye ; Let hw = vertical distance from the point to the water surface ; Let dw = distance from the point to a point on the water surface that lies along the ray from point to eye ; Therefore d/h = dw/hw by similar triangles, or dw = d * hw / h. ; d = |projPos|, h = eyepos.z - worldPos.z, hw = waterheight.z - worldPos.z, dw = what we solve for ; Now, tmp.x = hw, and tmp.y = h add $tmp.xy, $cEyePosWaterZ.wz, -$worldPos.z ; if $tmp.x < 0, then set it to 0 ; This is the equivalent of moving the vert to the water surface if it's above the water surface max $tmp.x, $tmp.x, $cZero ; Compute 1 / |projPos| = 1/d dp3 $tmp.z, $projPos.xyw, $projPos.xyw rsq $tmp.z, $tmp.z ; Now we have h/d mul $tmp.z, $tmp.z, $tmp.y ; Now we have d/h rcp $tmp.w, $tmp.z ; We finally have d * hw / h ; $tmp.w is now the distance that we see through water. mul $tmp.w, $tmp.x, $tmp.w if( $g_dx9 ) { mad $tmp, -$tmp.w, $cOOFogRange, $cFogOne min $tmp, $tmp, $cOne max oFog, $tmp.x, $cZero } else { mad $tmp, -$tmp.w, $cOOFogRange, $cFogOne min $tmp, $tmp, $cOne max oFog.x, $tmp.x, $cZero } &FreeRegister( \$tmp ); } sub WaterDepthFog { ; oFog.x = 1.0f = no fog ; oFog.x = 0.0f = full fog ; only $worldPos.z is used out of worldPos local( $worldPos ) = shift; local( $projPos ) = shift; local( $dest ) = shift; if ( $dest eq "" ) { $dest = "oFog"; } local( $tmp ); &AllocateRegister( \$tmp ); ; This is simple similar triangles. Imagine a line passing from the point directly vertically ; and another line passing from the point to the eye position. ; Let d = total distance from point to the eye ; Let h = vertical distance from the point to the eye ; Let hw = vertical distance from the point to the water surface ; Let dw = distance from the point to a point on the water surface that lies along the ray from point to eye ; Therefore d/h = dw/hw by similar triangles, or dw = d * hw / h. ; d = projPos.w, h = eyepos.z - worldPos.z, hw = waterheight.z - worldPos.z, dw = what we solve for ; Now, tmp.x = hw, and tmp.y = h add $tmp.xy, $cEyePosWaterZ.wz, -$worldPos.z ; if $tmp.x < 0, then set it to 0 ; This is the equivalent of moving the vert to the water surface if it's above the water surface max $tmp.x, $tmp.x, $cZero ; Now we have 1/h rcp $tmp.z, $tmp.y ; Now we have d/h mul $tmp.w, $projPos.w, $tmp.z ; We finally have d * hw / h ; $tmp.w is now the distance that we see through water. mul $tmp.w, $tmp.x, $tmp.w if( $g_dx9 ) { mad $tmp, -$tmp.w, $cOOFogRange, $cFogOne min $tmp, $tmp, $cOne max $dest, $tmp.x, $cZero } else { mad $tmp, -$tmp.w, $cOOFogRange, $cFogOne min $tmp, $tmp, $cOne max $dest.x, $tmp.x, $cZero } &FreeRegister( \$tmp ); } ;------------------------------------------------------------------------------ ; Main fogging routine ;------------------------------------------------------------------------------ sub CalcFog { if( !defined $DOWATERFOG ) { die "CalcFog called without using \$DOWATERFOG\n"; } my $fogType; if( $DOWATERFOG == 0 ) { $fogType = "rangefog"; } else { $fogType = "heightfog"; } # print "\$fogType = $fogType\n"; ; CalcFog local( $worldPos ) = shift; local( $projPos ) = shift; local( $dest ) = shift; if ( $dest eq "" ) { $dest = "oFog"; } if( $fogType eq "rangefog" ) { &DepthFog( $projPos, $dest ); } elsif( $fogType eq "heightfog" ) { &WaterDepthFog( $worldPos, $projPos, $dest ); } else { die; } } sub CalcRangeFog { ; CalcFog local( $worldPos ) = shift; local( $projPos ) = shift; if( $DOWATERFOG == 0 ) { &RangeFog( $projPos ); } elsif( $DOWATERFOG == 1 ) { &WaterRangeFog( $worldPos, $projPos ); } else { die; } } sub GammaToLinear { local( $gamma ) = shift; local( $linear ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); ; Is rcp more expensive than just storing 2.2 somewhere and doing a mov? rcp $gamma.w, $cOOGamma ; $gamma.w = 2.2 lit $linear.z, $gamma.zzzw ; r0.z = linear blue lit $tmp.z, $gamma.yyyw ; r2.z = linear green mov $linear.y, $tmp.z ; r0.y = linear green lit $tmp.z, $gamma.xxxw ; r2.z = linear red mov $linear.x, $tmp.z ; r0.x = linear red &FreeRegister( \$tmp ); } sub LinearToGamma { local( $linear ) = shift; local( $gamma ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); mov $linear.w, $cOOGamma ; $linear.w = 1.0/2.2 lit $gamma.z, $linear.zzzw ; r0.z = gamma blue lit $tmp.z, $linear.yyyw ; r2.z = gamma green mov $gamma.y, $tmp.z ; r0.y = gamma green lit $tmp.z, $linear.xxxw ; r2.z = gamma red mov $gamma.x, $tmp.z ; r0.x = gamma red &FreeRegister( \$tmp ); } sub ComputeReflectionVector { local( $worldPos ) = shift; local( $worldNormal ) = shift; local( $reflectionVector ) = shift; local( $vertToEye ); &AllocateRegister( \$vertToEye ); local( $tmp ); &AllocateRegister( \$tmp ); ; compute reflection vector r = 2 * (n dot v) n - v sub $vertToEye.xyz, $cEyePos.xyz, $worldPos ; $tmp1 = v = c - p dp3 $tmp, $worldNormal, $vertToEye ; $tmp = n dot v mul $tmp.xyz, $tmp.xyz, $worldNormal ; $tmp = (n dot v ) n mad $reflectionVector.xyz, $tmp, $cTwo, -$vertToEye &FreeRegister( \$vertToEye ); &FreeRegister( \$tmp ); } sub ComputeSphereMapTexCoords { local( $reflectionVector ) = shift; local( $sphereMapTexCoords ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); ; transform reflection vector into view space dp3 $tmp.x, $reflectionVector, $cViewModel0 dp3 $tmp.y, $reflectionVector, $cViewModel1 dp3 $tmp.z, $reflectionVector, $cViewModel2 ; generate add $tmp.z, $tmp.z, $cOne ; find 1 / the length of r2 dp3 $tmp.w, $tmp, $tmp rsq $tmp.w, $tmp.w ; r1 = r2/|r2| + 1 mad $tmp.xy, $tmp.w, $tmp, $cOne mul $sphereMapTexCoords.xy, $tmp.xy, $cHalf &FreeRegister( \$tmp ); } sub FixupXboxBoneIndex_1Bone { local( $boneIndices ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); ; $boneIndices.z = $boneIndices.z - ( $boneIndices.z >= 96 ? 192 : 0 ) add $tmp.x, $cModel0Index, $cModel0Index sge $tmp.y, $boneIndices.z, $tmp.x mul $tmp.y, $tmp.y, $tmp.x mad $boneIndices.z, $tmp.y, -$cTwo, $boneIndices.z &FreeRegister( \$tmp ); } sub FixupXboxBoneIndex_2Bone { local( $boneIndices ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); ; $boneIndices.z = $boneIndices.z - ( $boneIndices.z >= 96 ? 192 : 0 ) add $tmp.x, $cModel0Index, $cModel0Index sge $tmp.y, $boneIndices.z, $tmp.x mul $tmp.y, $tmp.y, $tmp.x mad $boneIndices.z, $tmp.y, -$cTwo, $boneIndices.z ; $boneIndices.y = $boneIndices.y - ( $boneIndices.y >= 96 ? 192 : 0 ) sge $tmp.y, $boneIndices.y, $tmp.x mul $tmp.y, $tmp.y, $tmp.x mad $boneIndices.y, $tmp.y, -$cTwo, $boneIndices.y &FreeRegister( \$tmp ); } sub FixupXboxBoneIndex_3Bone { local( $boneIndices ) = shift; local( $tmp ); &AllocateRegister( \$tmp ); ; $boneIndices.z = $boneIndices.z - ( $boneIndices.z >= 96 ? 192 : 0 ) add $tmp.x, $cModel0Index, $cModel0Index sge $tmp.y, $boneIndices.z, $tmp.x mul $tmp.y, $tmp.y, $tmp.x mad $boneIndices.z, $tmp.y, -$cTwo, $boneIndices.z ; $boneIndices.y = $boneIndices.y - ( $boneIndices.y >= 96 ? 192 : 0 ) sge $tmp.y, $boneIndices.y, $tmp.x mul $tmp.y, $tmp.y, $tmp.x mad $boneIndices.y, $tmp.y, -$cTwo, $boneIndices.y ; $boneIndices.x = $boneIndices.x - ( $boneIndices.x >= 96 ? 192 : 0 ) sge $tmp.y, $boneIndices.x, $tmp.x mul $tmp.y, $tmp.y, $tmp.x mad $boneIndices.x, $tmp.y, -$cTwo, $boneIndices.x &FreeRegister( \$tmp ); } sub SkinPosition { # print "\$NUM_BONES = $NUM_BONES\n"; local( $worldPos ) = shift; if( !defined $NUM_BONES && !defined $SKINNING ) { die "using \$NUM_BONES or \$SKINNING without defining.\n"; } if ( defined $SKINNING ) { if( $SKINNING == 0 ) { $NUM_BONES = 0; } else { $NUM_BONES = 3; } } if( $NUM_BONES == 0 ) { ; ; 0 bone skinning (4 instructions) ; ; Transform position into world space ; position dp4 $worldPos.x, $vPos, $cModel0 dp4 $worldPos.y, $vPos, $cModel1 dp4 $worldPos.z, $vPos, $cModel2 mov $worldPos.w, $cOne } elsif( $NUM_BONES == 1 ) { ; ; 1 bone skinning (6 instructions) ; local( $boneIndices ); &AllocateRegister( \$boneIndices ); ; Perform 1 bone skinning ; Transform position into world space ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_1Bone( $boneIndices ); } mov a0.x, $boneIndices.z ; position dp4 $worldPos.x, $vPos, c[a0.x] dp4 $worldPos.y, $vPos, c[a0.x + 1] dp4 $worldPos.z, $vPos, c[a0.x + 2] mov $worldPos.w, $cOne &FreeRegister( \$boneIndices ); } elsif( $NUM_BONES == 2 ) { ; ; 2 bone skinning (13 instructions) ; local( $boneIndices ); local( $blendedMatrix0 ); local( $blendedMatrix1 ); local( $blendedMatrix2 ); &AllocateRegister( \$boneIndices ); &AllocateRegister( \$blendedMatrix0 ); &AllocateRegister( \$blendedMatrix1 ); &AllocateRegister( \$blendedMatrix2 ); ; Transform position into world space using all bones ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_2Bone( $boneIndices ); } ; r11 = boneindices at this point ; first matrix mov a0.x, $boneIndices.z mul $blendedMatrix0, $vBoneWeights.x, c[a0.x] mul $blendedMatrix1, $vBoneWeights.x, c[a0.x+1] mul $blendedMatrix2, $vBoneWeights.x, c[a0.x+2] ; second matrix mov a0.x, $boneIndices.y mad $blendedMatrix0, $vBoneWeights.y, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $vBoneWeights.y, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $vBoneWeights.y, c[a0.x+2], $blendedMatrix2 ; position dp4 $worldPos.x, $vPos, $blendedMatrix0 dp4 $worldPos.y, $vPos, $blendedMatrix1 dp4 $worldPos.z, $vPos, $blendedMatrix2 mov $worldPos.w, $cOne &FreeRegister( \$boneIndices ); &FreeRegister( \$blendedMatrix0 ); &FreeRegister( \$blendedMatrix1 ); &FreeRegister( \$blendedMatrix2 ); } elsif( $NUM_BONES == 3 ) { ; ; 3 bone skinning (19 instructions) ; local( $boneIndices ); local( $blendedMatrix0 ); local( $blendedMatrix1 ); local( $blendedMatrix2 ); local( $localPos ); &AllocateRegister( \$boneIndices ); &AllocateRegister( \$blendedMatrix0 ); &AllocateRegister( \$blendedMatrix1 ); &AllocateRegister( \$blendedMatrix2 ); ; Transform position into world space using all bones ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_3Bone( $boneIndices ); &AllocateRegister( \$localPos ); mov $localPos, $vPos mad $localPos.xyz, $SHADER_FLEXSCALE, $vPosFlex.xyz, $localPos.xyz } ; r11 = boneindices at this point ; first matrix mov a0.x, $boneIndices.z mul $blendedMatrix0, $vBoneWeights.x, c[a0.x] mul $blendedMatrix1, $vBoneWeights.x, c[a0.x+1] mul $blendedMatrix2, $vBoneWeights.x, c[a0.x+2] ; second matrix mov a0.x, $boneIndices.y mad $blendedMatrix0, $vBoneWeights.y, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $vBoneWeights.y, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $vBoneWeights.y, c[a0.x+2], $blendedMatrix2 ; Calculate third weight ; compute 1-(weight1+weight2) to calculate weight2 ; Use $boneIndices.w as a temp since we aren't using it for anything. add $boneIndices.w, $vBoneWeights.x, $vBoneWeights.y sub $boneIndices.w, $cOne, $boneIndices.w ; third matrix mov a0.x, $boneIndices.x mad $blendedMatrix0, $boneIndices.w, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $boneIndices.w, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $boneIndices.w, c[a0.x+2], $blendedMatrix2 if ( $g_xbox ) { dp4 $worldPos.x, $localPos, $blendedMatrix0 dp4 $worldPos.y, $localPos, $blendedMatrix1 dp4 $worldPos.z, $localPos, $blendedMatrix2 mov $worldPos.w, $cOne &FreeRegister( \$localPos ); } else { dp4 $worldPos.x, $vPos, $blendedMatrix0 dp4 $worldPos.y, $vPos, $blendedMatrix1 dp4 $worldPos.z, $vPos, $blendedMatrix2 mov $worldPos.w, $cOne } &FreeRegister( \$boneIndices ); &FreeRegister( \$blendedMatrix0 ); &FreeRegister( \$blendedMatrix1 ); &FreeRegister( \$blendedMatrix2 ); } } sub SkinPositionAndNormal { # print "\$NUM_BONES = $NUM_BONES\n"; local( $worldPos ) = shift; local( $worldNormal ) = shift; if( !defined $NUM_BONES && !defined $SKINNING ) { die "using \$NUM_BONES or \$SKINNING without defining.\n"; } if ( defined $SKINNING ) { if( $SKINNING == 0 ) { $NUM_BONES = 0; } else { $NUM_BONES = 3; } } if( $NUM_BONES == 0 ) { ; ; 0 bone skinning (13 instructions) ; ; Transform position + normal + tangentS + tangentT into world space ; position dp4 $worldPos.x, $vPos, $cModel0 dp4 $worldPos.y, $vPos, $cModel1 dp4 $worldPos.z, $vPos, $cModel2 mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, $cModel0 dp3 $worldNormal.y, $vNormal, $cModel1 dp3 $worldNormal.z, $vNormal, $cModel2 } elsif( $NUM_BONES == 1 ) { ; ; 1 bone skinning (17 instructions) ; local( $boneIndices ); &AllocateRegister( \$boneIndices ); ; Perform 1 bone skinning ; Transform position into world space ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_1Bone( $boneIndices ); } mov a0.x, $boneIndices.z ; position dp4 $worldPos.x, $vPos, c[a0.x] dp4 $worldPos.y, $vPos, c[a0.x + 1] dp4 $worldPos.z, $vPos, c[a0.x + 2] mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, c[a0.x] dp3 $worldNormal.y, $vNormal, c[a0.x + 1] dp3 $worldNormal.z, $vNormal, c[a0.x + 2] &FreeRegister( \$boneIndices ); } elsif( $NUM_BONES == 2 ) { ; ; 2 bone skinning (16 instructions) ; local( $boneIndices ); local( $blendedMatrix0 ); local( $blendedMatrix1 ); local( $blendedMatrix2 ); &AllocateRegister( \$boneIndices ); &AllocateRegister( \$blendedMatrix0 ); &AllocateRegister( \$blendedMatrix1 ); &AllocateRegister( \$blendedMatrix2 ); ; Transform position into world space using all bones ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_2Bone( $boneIndices ); } ; r11 = boneindices at this point ; first matrix mov a0.x, $boneIndices.z mul $blendedMatrix0, $vBoneWeights.x, c[a0.x] mul $blendedMatrix1, $vBoneWeights.x, c[a0.x+1] mul $blendedMatrix2, $vBoneWeights.x, c[a0.x+2] ; second matrix mov a0.x, $boneIndices.y mad $blendedMatrix0, $vBoneWeights.y, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $vBoneWeights.y, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $vBoneWeights.y, c[a0.x+2], $blendedMatrix2 ; position dp4 $worldPos.x, $vPos, $blendedMatrix0 dp4 $worldPos.y, $vPos, $blendedMatrix1 dp4 $worldPos.z, $vPos, $blendedMatrix2 mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, $blendedMatrix0 dp3 $worldNormal.y, $vNormal, $blendedMatrix1 dp3 $worldNormal.z, $vNormal, $blendedMatrix2 &FreeRegister( \$boneIndices ); &FreeRegister( \$blendedMatrix0 ); &FreeRegister( \$blendedMatrix1 ); &FreeRegister( \$blendedMatrix2 ); } elsif( $NUM_BONES == 3 ) { local( $boneIndices ); local( $blendedMatrix0 ); local( $blendedMatrix1 ); local( $blendedMatrix2 ); local( $localPos ); local( $localNormal ); local( $normalLength ); local( $ooNormalLength ); &AllocateRegister( \$boneIndices ); &AllocateRegister( \$blendedMatrix0 ); &AllocateRegister( \$blendedMatrix1 ); &AllocateRegister( \$blendedMatrix2 ); ; Transform position into world space using all bones ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_3Bone( $boneIndices ); &AllocateRegister( \$localPos ); mov $localPos, $vPos mad $localPos.xyz, $SHADER_FLEXSCALE, $vPosFlex.xyz, $localPos.xyz &AllocateRegister( \$localNormal ); mov $localNormal, $vNormal mad $localNormal.xyz, $SHADER_FLEXSCALE, $vNormalFlex.xyz, $localNormal.xyz } ; r11 = boneindices at this point ; first matrix mov a0.x, $boneIndices.z mul $blendedMatrix0, $vBoneWeights.x, c[a0.x] mul $blendedMatrix1, $vBoneWeights.x, c[a0.x+1] mul $blendedMatrix2, $vBoneWeights.x, c[a0.x+2] ; second matrix mov a0.x, $boneIndices.y mad $blendedMatrix0, $vBoneWeights.y, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $vBoneWeights.y, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $vBoneWeights.y, c[a0.x+2], $blendedMatrix2 ; Calculate third weight ; compute 1-(weight1+weight2) to calculate weight2 ; Use $boneIndices.w as a temp since we aren't using it for anything. add $boneIndices.w, $vBoneWeights.x, $vBoneWeights.y sub $boneIndices.w, $cOne, $boneIndices.w ; third matrix mov a0.x, $boneIndices.x mad $blendedMatrix0, $boneIndices.w, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $boneIndices.w, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $boneIndices.w, c[a0.x+2], $blendedMatrix2 if ( $g_xbox ) { dp4 $worldPos.x, $localPos, $blendedMatrix0 dp4 $worldPos.y, $localPos, $blendedMatrix1 dp4 $worldPos.z, $localPos, $blendedMatrix2 mov $worldPos.w, $cOne &FreeRegister( \$localPos ); ; normal dp3 $worldNormal.x, $localNormal, $blendedMatrix0 dp3 $worldNormal.y, $localNormal, $blendedMatrix1 dp3 $worldNormal.z, $localNormal, $blendedMatrix2 ; renormalize after flex &FreeRegister( \$localNormal ); &AllocateRegister( \$normalLength ); &AllocateRegister( \$ooNormalLength ); dp3 $normalLength, $worldNormal, $worldNormal rsq $ooNormalLength.x, $normalLength.x mul $worldNormal.xyz, $worldNormal.xyz, $ooNormalLength.x &FreeRegister( \$normalLength ); &FreeRegister( \$ooNormalLength ); } else { dp4 $worldPos.x, $vPos, $blendedMatrix0 dp4 $worldPos.y, $vPos, $blendedMatrix1 dp4 $worldPos.z, $vPos, $blendedMatrix2 mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, $blendedMatrix0 dp3 $worldNormal.y, $vNormal, $blendedMatrix1 dp3 $worldNormal.z, $vNormal, $blendedMatrix2 } &FreeRegister( \$boneIndices ); &FreeRegister( \$blendedMatrix0 ); &FreeRegister( \$blendedMatrix1 ); &FreeRegister( \$blendedMatrix2 ); } } sub SkinPositionNormalAndTangentSpace { # print "\$NUM_BONES = $NUM_BONES\n"; local( $worldPos ) = shift; local( $worldNormal ) = shift; local( $worldTangentS ) = shift; local( $worldTangentT ) = shift; local( $userData ); local( $localPos ); local( $localNormal ); local( $normalLength ); local( $ooNormalLength ); if( !defined $NUM_BONES && !defined $SKINNING ) { die "using \$NUM_BONES or \$SKINNING without defining.\n"; } if ( defined $SKINNING ) { if( $SKINNING == 0 ) { $NUM_BONES = 0; } else { $NUM_BONES = 3; } } if ( $g_xbox ) { &AllocateRegister( \$userData ); ; remap compressed range [0..1] to [-1..1] mad $userData, $vUserData, $cTwo, -$cOne } if( $NUM_BONES == 0 ) { ; ; 0 bone skinning (13 instructions) ; ; Transform position + normal + tangentS + tangentT into world space if ( $g_xbox ) { &AllocateRegister( \$localPos ); mov $localPos, $vPos mad $localPos.xyz, $SHADER_FLEXSCALE, $vPosFlex.xyz, $localPos.xyz dp4 $worldPos.x, $localPos, $cModel0 dp4 $worldPos.y, $localPos, $cModel1 dp4 $worldPos.z, $localPos, $cModel2 mov $worldPos.w, $cOne &FreeRegister( \$localPos ); } else { dp4 $worldPos.x, $vPos, $cModel0 dp4 $worldPos.y, $vPos, $cModel1 dp4 $worldPos.z, $vPos, $cModel2 mov $worldPos.w, $cOne } ; normal dp3 $worldNormal.x, $vNormal, $cModel0 dp3 $worldNormal.y, $vNormal, $cModel1 dp3 $worldNormal.z, $vNormal, $cModel2 if ( $g_xbox ) { ; tangents dp3 $worldTangentS.x, $userData, $cModel0 dp3 $worldTangentS.y, $userData, $cModel1 dp3 $worldTangentS.z, $userData, $cModel2 ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $userData.w, $worldTangentT.xyz } else { ; tangents dp3 $worldTangentS.x, $vUserData, $cModel0 dp3 $worldTangentS.y, $vUserData, $cModel1 dp3 $worldTangentS.z, $vUserData, $cModel2 ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $vUserData.w, $worldTangentT.xyz } } elsif( $NUM_BONES == 1 ) { ; ; 1 bone skinning (17 instructions) ; local( $boneIndices ); &AllocateRegister( \$boneIndices ); ; Perform 1 bone skinning ; Transform position into world space ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_1Bone( $boneIndices ); } mov a0.x, $boneIndices.z ; position dp4 $worldPos.x, $vPos, c[a0.x] dp4 $worldPos.y, $vPos, c[a0.x + 1] dp4 $worldPos.z, $vPos, c[a0.x + 2] mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, c[a0.x] dp3 $worldNormal.y, $vNormal, c[a0.x + 1] dp3 $worldNormal.z, $vNormal, c[a0.x + 2] if ( $g_xbox ) { ; tangents dp3 $worldTangentS.x, $userData, c[a0.x] dp3 $worldTangentS.y, $userData, c[a0.x + 1] dp3 $worldTangentS.z, $userData, c[a0.x + 2] ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $userData.w, $worldTangentT.xyz } else { ; tangents dp3 $worldTangentS.x, $vUserData, c[a0.x] dp3 $worldTangentS.y, $vUserData, c[a0.x + 1] dp3 $worldTangentS.z, $vUserData, c[a0.x + 2] ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $vUserData.w, $worldTangentT.xyz } &FreeRegister( \$boneIndices ); } elsif( $NUM_BONES == 2 ) { ; ; 2 bone skinning (22 instructions) ; local( $boneIndices ); local( $blendedMatrix0 ); local( $blendedMatrix1 ); local( $blendedMatrix2 ); &AllocateRegister( \$boneIndices ); &AllocateRegister( \$blendedMatrix0 ); &AllocateRegister( \$blendedMatrix1 ); &AllocateRegister( \$blendedMatrix2 ); ; Transform position into world space using all bones ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_2Bone( $boneIndices ); } ; r11 = boneindices at this point ; first matrix mov a0.x, $boneIndices.z mul $blendedMatrix0, $vBoneWeights.x, c[a0.x] mul $blendedMatrix1, $vBoneWeights.x, c[a0.x+1] mul $blendedMatrix2, $vBoneWeights.x, c[a0.x+2] ; second matrix mov a0.x, $boneIndices.y mad $blendedMatrix0, $vBoneWeights.y, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $vBoneWeights.y, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $vBoneWeights.y, c[a0.x+2], $blendedMatrix2 ; position dp4 $worldPos.x, $vPos, $blendedMatrix0 dp4 $worldPos.y, $vPos, $blendedMatrix1 dp4 $worldPos.z, $vPos, $blendedMatrix2 mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, $blendedMatrix0 dp3 $worldNormal.y, $vNormal, $blendedMatrix1 dp3 $worldNormal.z, $vNormal, $blendedMatrix2 if ( $g_xbox ) { ; tangents dp3 $worldTangentS.x, $userData, $blendedMatrix0 dp3 $worldTangentS.y, $userData, $blendedMatrix1 dp3 $worldTangentS.z, $userData, $blendedMatrix2 ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $userData.w, $worldTangentT.xyz } else { ; tangents dp3 $worldTangentS.x, $vUserData, $blendedMatrix0 dp3 $worldTangentS.y, $vUserData, $blendedMatrix1 dp3 $worldTangentS.z, $vUserData, $blendedMatrix2 ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $vUserData.w, $worldTangentT.xyz } &FreeRegister( \$boneIndices ); &FreeRegister( \$blendedMatrix0 ); &FreeRegister( \$blendedMatrix1 ); &FreeRegister( \$blendedMatrix2 ); } elsif( $NUM_BONES == 3 ) { local( $boneIndices ); local( $blendedMatrix0 ); local( $blendedMatrix1 ); local( $blendedMatrix2 ); &AllocateRegister( \$boneIndices ); &AllocateRegister( \$blendedMatrix0 ); &AllocateRegister( \$blendedMatrix1 ); &AllocateRegister( \$blendedMatrix2 ); if ( $g_xbox ) { &AllocateRegister( \$localPos ); mov $localPos, $vPos mad $localPos.xyz, $SHADER_FLEXSCALE, $vPosFlex.xyz, $localPos.xyz &AllocateRegister( \$localNormal ); mov $localNormal, $vNormal mad $localNormal.xyz, $SHADER_FLEXSCALE, $vNormalFlex.xyz, $localNormal.xyz } ; Transform position into world space using all bones ; denormalize d3dcolor to matrix index mad $boneIndices, $vBoneIndices, $cColorToIntScale, $cModel0Index if ( $g_xbox ) { &FixupXboxBoneIndex_3Bone( $boneIndices ); } ; r11 = boneindices at this point ; first matrix mov a0.x, $boneIndices.z mul $blendedMatrix0, $vBoneWeights.x, c[a0.x] mul $blendedMatrix1, $vBoneWeights.x, c[a0.x+1] mul $blendedMatrix2, $vBoneWeights.x, c[a0.x+2] ; second matrix mov a0.x, $boneIndices.y mad $blendedMatrix0, $vBoneWeights.y, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $vBoneWeights.y, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $vBoneWeights.y, c[a0.x+2], $blendedMatrix2 ; Calculate third weight ; compute 1-(weight1+weight2) to calculate weight2 ; Use $boneIndices.w as a temp since we aren't using it for anything. add $boneIndices.w, $vBoneWeights.x, $vBoneWeights.y sub $boneIndices.w, $cOne, $boneIndices.w ; third matrix mov a0.x, $boneIndices.x mad $blendedMatrix0, $boneIndices.w, c[a0.x], $blendedMatrix0 mad $blendedMatrix1, $boneIndices.w, c[a0.x+1], $blendedMatrix1 mad $blendedMatrix2, $boneIndices.w, c[a0.x+2], $blendedMatrix2 ; position if ( $g_xbox ) { dp4 $worldPos.x, $localPos, $blendedMatrix0 dp4 $worldPos.y, $localPos, $blendedMatrix1 dp4 $worldPos.z, $localPos, $blendedMatrix2 mov $worldPos.w, $cOne &FreeRegister( \$localPos ); ; normal dp3 $worldNormal.x, $localNormal, $blendedMatrix0 dp3 $worldNormal.y, $localNormal, $blendedMatrix1 dp3 $worldNormal.z, $localNormal, $blendedMatrix2 ; renormalize after flex &FreeRegister( \$localNormal ); &AllocateRegister( \$normalLength ); &AllocateRegister( \$ooNormalLength ); dp3 $normalLength, $worldNormal, $worldNormal rsq $ooNormalLength.x, $normalLength.x mul $worldNormal.xyz, $worldNormal.xyz, $ooNormalLength.x &FreeRegister( \$normalLength ); &FreeRegister( \$ooNormalLength ); } else { dp4 $worldPos.x, $vPos, $blendedMatrix0 dp4 $worldPos.y, $vPos, $blendedMatrix1 dp4 $worldPos.z, $vPos, $blendedMatrix2 mov $worldPos.w, $cOne ; normal dp3 $worldNormal.x, $vNormal, $blendedMatrix0 dp3 $worldNormal.y, $vNormal, $blendedMatrix1 dp3 $worldNormal.z, $vNormal, $blendedMatrix2 } if ( $g_xbox ) { ; tangents dp3 $worldTangentS.x, $userData, $blendedMatrix0 dp3 $worldTangentS.y, $userData, $blendedMatrix1 dp3 $worldTangentS.z, $userData, $blendedMatrix2 ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $userData.w, $worldTangentT.xyz } else { ; tangents dp3 $worldTangentS.x, $vUserData, $blendedMatrix0 dp3 $worldTangentS.y, $vUserData, $blendedMatrix1 dp3 $worldTangentS.z, $vUserData, $blendedMatrix2 ; calculate tangent t via cross( N, S ) * S[3] &Cross( $worldTangentT, $worldNormal, $worldTangentS ); mul $worldTangentT.xyz, $vUserData.w, $worldTangentT.xyz } &FreeRegister( \$boneIndices ); &FreeRegister( \$blendedMatrix0 ); &FreeRegister( \$blendedMatrix1 ); &FreeRegister( \$blendedMatrix2 ); } if ( $g_xbox ) { &FreeRegister( \$userData ); } } sub ColorClamp { ; ColorClamp; stomps $color.w local( $color ) = shift; local( $dst ) = shift; ; Get the max of RGB and stick it in W max $color.w, $color.x, $color.y max $color.w, $color.w, $color.z ; get the greater of one and the max color. max $color.w, $color.w, $cOne rcp $color.w, $color.w mul $dst.xyz, $color.w, $color.xyz } sub AmbientLight { local( $worldNormal ) = shift; local( $linearColor ) = shift; local( $add ) = shift; ; Ambient lighting &AllocateRegister( \$nSquared ); &AllocateRegister( \$isNegative ); mul $nSquared.xyz, $worldNormal.xyz, $worldNormal.xyz ; compute n times n slt $isNegative.xyz, $worldNormal.xyz, $cZero ; Figure out whether each component is >0 mov a0.x, $isNegative.x if( $add ) { mad $linearColor.xyz, $nSquared.x, c[a0.x + $cAmbientColorPosXOffset], $linearColor ; $linearColor = normal[0]*normal[0] * box color of appropriate x side } else { mul $linearColor.xyz, $nSquared.x, c[a0.x + $cAmbientColorPosXOffset] ; $linearColor = normal[0]*normal[0] * box color of appropriate x side } mov a0.x, $isNegative.y mad $linearColor.xyz, $nSquared.y, c[a0.x + $cAmbientColorPosYOffset], $linearColor mov a0.x, $isNegative.z mad $linearColor.xyz, $nSquared.z, c[a0.x + $cAmbientColorPosZOffset], $linearColor &FreeRegister( \$isNegative ); &FreeRegister( \$nSquared ); } sub DirectionalLight { local( $worldNormal ) = shift; local( $linearColor ) = shift; local( $add ) = shift; &AllocateRegister( \$nDotL ); # FIXME: This only needs to be a scalar ; NOTE: Gotta use -l here, since light direction = -l ; DIRECTIONAL LIGHT ; compute n dot l dp3 $nDotL.x, -c[a0.x + 1], $worldNormal if ( $g_xbox ) { ; HALF LAMBERT mad $nDotL.y, $nDotL.x, $cHalf, $cHalf ; dot = (dot * 0.5 + 0.5)^2 mul $nDotL.y, $nDotL.y, $nDotL.y max $nDotL.x, $nDotL.x, $cZero ; clamp to zero sub $nDotL.z, $nDotL.y, $nDotL.x mad $nDotL.x, $SHADER_HALFLAMBERT, $nDotL.z, $nDotL.x } else { if ( $HALF_LAMBERT == 0 ) { ; lambert max $nDotL.x, $nDotL.x, c0.x ; Clamp to zero } elsif ( $HALF_LAMBERT == 1 ) { ; half-lambert mad $nDotL.x, $nDotL.x, $cHalf, $cHalf ; dot = (dot * 0.5 + 0.5)^2 mul $nDotL.x, $nDotL.x, $nDotL.x } else { die "\$HALF_LAMBERT is hosed\n"; } } if( $add ) { mad $linearColor.xyz, c[a0.x], $nDotL.x, $linearColor } else { mul $linearColor.xyz, c[a0.x], $nDotL.x } &FreeRegister( \$nDotL ); } sub PointLight { local( $worldPos ) = shift; local( $worldNormal ) = shift; local( $linearColor ) = shift; local( $add ) = shift; local( $lightDir ); &AllocateRegister( \$lightDir ); ; POINT LIGHT ; compute light direction sub $lightDir, c[a0.x+2], $worldPos local( $lightDistSquared ); local( $ooLightDist ); &AllocateRegister( \$lightDistSquared ); &AllocateRegister( \$ooLightDist ); ; normalize light direction, maintain temporaries for attenuation dp3 $lightDistSquared, $lightDir, $lightDir rsq $ooLightDist, $lightDistSquared.x mul $lightDir, $lightDir, $ooLightDist.x local( $attenuationFactors ); &AllocateRegister( \$attenuationFactors ); ; compute attenuation amount (r2 = 'd*d d*d d*d d*d', r3 = '1/d 1/d 1/d 1/d') dst $attenuationFactors, $lightDistSquared, $ooLightDist ; r4 = ( 1, d, d*d, 1/d ) &FreeRegister( \$lightDistSquared ); &FreeRegister( \$ooLightDist ); local( $attenuation ); &AllocateRegister( \$attenuation ); dp3 $attenuation, $attenuationFactors, c[a0.x+4] ; r3 = atten0 + d * atten1 + d*d * atten2 rcp $lightDir.w, $attenuation ; $lightDir.w = 1 / (atten0 + d * atten1 + d*d * atten2) &FreeRegister( \$attenuationFactors ); &FreeRegister( \$attenuation ); local( $tmp ); &AllocateRegister( \$tmp ); # FIXME : really only needs to be a scalar ; compute n dot l, fold in distance attenutation dp3 $tmp.x, $lightDir, $worldNormal if ( $g_xbox ) { ; HALF LAMBERT mad $tmp.y, $tmp.x, $cHalf, $cHalf ; dot = (dot * 0.5 + 0.5)^2 mul $tmp.y, $tmp.y, $tmp.y max $tmp.x, $tmp.x, $cZero ; clamp to zero sub $tmp.z, $tmp.y, $tmp.x mad $tmp.x, $SHADER_HALFLAMBERT, $tmp.z, $tmp.x } else { if ( $HALF_LAMBERT == 0 ) { ; lambert max $tmp.x, $tmp.x, c0.x ; Clamp to zero } elsif ( $HALF_LAMBERT == 1 ) { ; half-lambert mad $tmp.x, $tmp.x, $cHalf, $cHalf ; dot = (dot * 0.5 + 0.5)^2 mul $tmp.x, $tmp.x, $tmp.x } else { die "\$HALF_LAMBERT is hosed\n"; } } mul $tmp.x, $tmp.x, $lightDir.w if( $add ) { mad $linearColor.xyz, c[a0.x], $tmp.x, $linearColor } else { mul $linearColor.xyz, c[a0.x], $tmp.x } &FreeRegister( \$lightDir ); &FreeRegister( \$tmp ); # FIXME : really only needs to be a scalar } sub SpotLight { local( $worldPos ) = shift; local( $worldNormal ) = shift; local( $linearColor ) = shift; local( $add ) = shift; local( $lightDir ); &AllocateRegister( \$lightDir ); ; SPOTLIGHT ; compute light direction sub $lightDir, c[a0.x+2], $worldPos local( $lightDistSquared ); local( $ooLightDist ); &AllocateRegister( \$lightDistSquared ); &AllocateRegister( \$ooLightDist ); ; normalize light direction, maintain temporaries for attenuation dp3 $lightDistSquared, $lightDir, $lightDir rsq $ooLightDist, $lightDistSquared.x mul $lightDir, $lightDir, $ooLightDist.x local( $attenuationFactors ); &AllocateRegister( \$attenuationFactors ); ; compute attenuation amount (r2 = 'd*d d*d d*d d*d', r3 = '1/d 1/d 1/d 1/d') dst $attenuationFactors, $lightDistSquared, $ooLightDist ; r4 = ( 1, d, d*d, 1/d ) &FreeRegister( \$lightDistSquared ); &FreeRegister( \$ooLightDist ); local( $attenuation ); &AllocateRegister( \$attenuation ); dp3 $attenuation, $attenuationFactors, c[a0.x+4] ; r3 = atten0 + d * atten1 + d*d * atten2 rcp $lightDir.w, $attenuation ; r1.w = 1 / (atten0 + d * atten1 + d*d * atten2) &FreeRegister( \$attenuationFactors ); &FreeRegister( \$attenuation ); local( $litSrc ); &AllocateRegister( \$litSrc ); local( $tmp ); &AllocateRegister( \$tmp ); # FIXME - only needs to be scalar ; compute n dot l dp3 $litSrc.x, $worldNormal, $lightDir if ( $g_xbox ) { ; HALF LAMBERT mad $litSrc.y, $litSrc.x, $cHalf, $cHalf ; dot = (dot * 0.5 + 0.5)^2 mul $litSrc.y, $litSrc.y, $litSrc.y max $litSrc.x, $litSrc.x, $cZero ; clamp to zero sub $litSrc.z, $litSrc.y, $litSrc.x mad $litSrc.x, $SHADER_HALFLAMBERT, $litSrc.z, $litSrc.x } else { if ( $HALF_LAMBERT == 0 ) { ; lambert max $litSrc.x, $litSrc.x, c0.x ; Clamp to zero } elsif ( $HALF_LAMBERT == 1 ) { ; half-lambert mad $litSrc.x, $litSrc.x, $cHalf, $cHalf ; dot = (dot * 0.5 + 0.5) ^ 2 mul $litSrc.x, $litSrc.x, $litSrc.x } else { die "\$HALF_LAMBERT is hosed\n"; } } ; compute angular attenuation dp3 $tmp.x, c[a0.x+1], -$lightDir ; dot = -delta * spot direction sub $litSrc.y, $tmp.x, c[a0.x+3].z ; r2.y = dot - stopdot2 &FreeRegister( \$tmp ); mul $litSrc.y, $litSrc.y, c[a0.x+3].w ; r2.y = (dot - stopdot2) / (stopdot - stopdot2) mov $litSrc.w, c[a0.x+3].x ; r2.w = exponent local( $litDst ); &AllocateRegister( \$litDst ); lit $litDst, $litSrc ; r3.y = N dot L or 0, whichever is bigger &FreeRegister( \$litSrc ); ; r3.z = pow((dot - stopdot2) / (stopdot - stopdot2), exponent) min $litDst.z, $litDst.z, $cOne ; clamp pow() to 1 local( $tmp1 ); &AllocateRegister( \$tmp1 ); local( $tmp2 ); &AllocateRegister( \$tmp2 ); # FIXME - could be scalar ; fold in distance attenutation with other factors mul $tmp1, c[a0.x], $lightDir.w mul $tmp2.x, $litDst.y, $litDst.z if( $add ) { mad $linearColor.xyz, $tmp1, $tmp2.x, $linearColor } else { mul $linearColor.xyz, $tmp1, $tmp2.x } &FreeRegister( \$lightDir ); &FreeRegister( \$litDst ); &FreeRegister( \$tmp1 ); &FreeRegister( \$tmp2 ); } sub DoLight { local( $lightType ) = shift; local( $worldPos ) = shift; local( $worldNormal ) = shift; local( $linearColor ) = shift; local( $add ) = shift; if( $lightType eq "spot" ) { &SpotLight( $worldPos, $worldNormal, $linearColor, $add ); } elsif( $lightType eq "point" ) { &PointLight( $worldPos, $worldNormal, $linearColor, $add ); } elsif( $lightType eq "directional" ) { &DirectionalLight( $worldNormal, $linearColor, $add ); } else { die "don't know about light type \"$lightType\"\n"; } } sub DoLighting { if( !defined $LIGHT_COMBO ) { die "DoLighting called without using \$LIGHT_COMBO\n"; } if ( !$g_xbox && !defined $HALF_LAMBERT ) { die "DoLighting called without using \$HALF_LAMBERT\n"; } my $staticLightType = $g_staticLightTypeArray[$LIGHT_COMBO]; my $ambientLightType = $g_ambientLightTypeArray[$LIGHT_COMBO]; my $localLightType1 = $g_localLightType1Array[$LIGHT_COMBO]; my $localLightType2 = $g_localLightType2Array[$LIGHT_COMBO]; # print "\$staticLightType = $staticLightType\n"; # print "\$ambientLightType = $ambientLightType\n"; # print "\$localLightType1 = $localLightType1\n"; # print "\$localLightType2 = $localLightType2\n"; local( $worldPos ) = shift; local( $worldNormal ) = shift; ; special case for no lighting if( $staticLightType eq "none" && $ambientLightType eq "none" && $localLightType1 eq "none" && $localLightType2 eq "none" ) { return; } ; special case for static lighting only ; Don't need to bother converting to linear space in this case. if( $staticLightType eq "static" && $ambientLightType eq "none" && $localLightType1 eq "none" && $localLightType2 eq "none" ) { mov oD0, $vSpecular return; } alloc $linearColor alloc $gammaColor local( $add ) = 0; if( $staticLightType eq "static" ) { ; The static lighting comes in in gamma space and has also been premultiplied by $cOverbrightFactor ; need to get it into ; linear space so that we can do adds. rcp $gammaColor.w, $cOverbrightFactor mul $gammaColor.xyz, $vSpecular, $gammaColor.w &GammaToLinear( $gammaColor, $linearColor ); $add = 1; } if( $ambientLightType eq "ambient" ) { &AmbientLight( $worldNormal, $linearColor, $add ); $add = 1; } if( $localLightType1 ne "none" ) { mov a0.x, $cLight0Offset &DoLight( $localLightType1, $worldPos, $worldNormal, $linearColor, $add ); $add = 1; } if( $localLightType2 ne "none" ) { mov a0.x, $cLight1Offset &DoLight( $localLightType2, $worldPos, $worldNormal, $linearColor, $add ); $add = 1; } ;------------------------------------------------------------------------------ ; Output color (gamma correction) ;------------------------------------------------------------------------------ &LinearToGamma( $linearColor, $gammaColor ); if( 0 ) { mul oD0.xyz, $gammaColor.xyz, $cOverbrightFactor } else { mul $gammaColor.xyz, $gammaColor.xyz, $cOverbrightFactor &ColorClamp( $gammaColor, "oD0" ); } ; mov oD0.xyz, $linearColor mov oD0.w, $cOne ; make sure all components are defined free $linearColor free $gammaColor } sub DoDynamicLightingToLinear { local( $worldPos ) = shift; local( $worldNormal ) = shift; local( $linearColor ) = shift; if( !defined $LIGHT_COMBO ) { die "DoLighting called without using \$LIGHT_COMBO\n"; } if ( !g_xbox && !defined $HALF_LAMBERT ) { die "DoLighting called without using \$HALF_LAMBERT\n"; } my $staticLightType = $g_staticLightTypeArray[$LIGHT_COMBO]; my $ambientLightType = $g_ambientLightTypeArray[$LIGHT_COMBO]; my $localLightType1 = $g_localLightType1Array[$LIGHT_COMBO]; my $localLightType2 = $g_localLightType2Array[$LIGHT_COMBO]; # No lights at all. . note that we don't even consider static lighting here. if( $ambientLightType eq "none" && $localLightType1 eq "none" && $localLightType2 eq "none" ) { mov $linearColor, $cZero return; } local( $add ) = 0; if( $ambientLightType eq "ambient" ) { &AmbientLight( $worldNormal, $linearColor, $add ); $add = 1; } if( $localLightType1 ne "none" ) { mov a0.x, $cLight0Offset &DoLight( $localLightType1, $worldPos, $worldNormal, $linearColor, $add ); $add = 1; } if( $localLightType2 ne "none" ) { mov a0.x, $cLight1Offset &DoLight( $localLightType2, $worldPos, $worldNormal, $linearColor, $add ); $add = 1; } } sub NotImplementedYet { &AllocateRegister( \$projPos ); dp4 $projPos.x, $worldPos, $cViewProj0 dp4 $projPos.y, $worldPos, $cViewProj1 dp4 $projPos.z, $worldPos, $cViewProj2 dp4 $projPos.w, $worldPos, $cViewProj3 mov oPos, $projPos &FreeRegister( \$projPos ); exit; }