353 lines
10 KiB
C#
353 lines
10 KiB
C#
using Intromat.Views;
|
|
using ReactiveUI;
|
|
using Splat;
|
|
|
|
namespace Intromat.ViewModels
|
|
{
|
|
public sealed class ShaderFileViewModel : FileViewModel
|
|
{
|
|
private string _source = string.Empty;
|
|
|
|
static ShaderFileViewModel()
|
|
{
|
|
Locator.CurrentMutable.Register(() => new ShaderFileView(), typeof(IViewFor<ShaderFileViewModel>));
|
|
}
|
|
|
|
public ShaderFileViewModel(MainViewModel mainVm, ModuleViewModel module, FolderViewModel parent, string name)
|
|
: base(module, parent, name, "hlsl")
|
|
{
|
|
}
|
|
|
|
public static ShaderFileViewModel CreateDefault(MainViewModel mainVm, ModuleViewModel module, FolderViewModel parent, string name)
|
|
{
|
|
var shaderVm = new ShaderFileViewModel(mainVm, module, parent, name);
|
|
shaderVm.Source = @"
|
|
// Pixel UberShader
|
|
int idot(int3 x, int3 y)
|
|
{
|
|
int3 tmp = x * y;
|
|
return tmp.x + tmp.y + tmp.z;
|
|
}
|
|
int idot(int4 x, int4 y)
|
|
{
|
|
int4 tmp = x * y;
|
|
return tmp.x + tmp.y + tmp.z + tmp.w;
|
|
}
|
|
|
|
int iround(float x) { return int (round(x)); }
|
|
int2 iround(float2 x) { return int2(round(x)); }
|
|
int3 iround(float3 x) { return int3(round(x)); }
|
|
int4 iround(float4 x) { return int4(round(x)); }
|
|
|
|
int itrunc(float x) { return int (trunc(x)); }
|
|
int2 itrunc(float2 x) { return int2(trunc(x)); }
|
|
int3 itrunc(float3 x) { return int3(trunc(x)); }
|
|
int4 itrunc(float4 x) { return int4(trunc(x)); }
|
|
|
|
SamplerState samp[8] : register(s0);
|
|
|
|
Texture2DArray Tex[8] : register(t0);
|
|
|
|
cbuffer PSBlock : register(b0) {
|
|
int4 color[4];
|
|
int4 k[4];
|
|
int4 alphaRef;
|
|
float4 texdim[8];
|
|
int4 czbias[2];
|
|
int4 cindscale[2];
|
|
int4 cindmtx[6];
|
|
int4 cfogcolor;
|
|
int4 cfogi;
|
|
float4 cfogf[2];
|
|
float4 czslope;
|
|
float4 cefbscale;
|
|
};
|
|
struct VS_OUTPUT {
|
|
float4 pos : POSITION;
|
|
float4 colors_0 : COLOR0;
|
|
float4 colors_1 : COLOR1;
|
|
float3 tex[8] : TEXCOORD0;
|
|
float4 clipPos : TEXCOORD8;
|
|
};
|
|
cbuffer UBERBlock : register(b4) {
|
|
uint bpmem_genmode;
|
|
uint bpmem_tevorder[8];
|
|
uint2 bpmem_combiners[16];
|
|
uint bpmem_tevksel[8];
|
|
int4 konstLookup[32];
|
|
float4 debug;
|
|
};
|
|
uint bitfieldExtract(uint val, int off, int size) {
|
|
// This built-in function is only support in OpenGL 4.0 and ES 3.1
|
|
// Hopefully the shader compiler will get our meaning and emit the right instruction
|
|
uint mask = uint((1 << size) - 1);
|
|
return uint(val >> off) & mask;
|
|
}
|
|
|
|
int4 sampleTexture(uint sampler_num, float2 uv) {
|
|
// This is messy, but DirectX, OpenGl 3.3 and Opengl ES 3.0 doesn't support dynamic indexing of the sampler array
|
|
// With any luck the shader compiler will optimise this if the hardware supports dynamic indexing.
|
|
switch(sampler_num & 0x7u) {
|
|
case 0u: return int4(Tex[0].Sample(samp[0], float3(uv, 0.0)) * 255.0);
|
|
case 1u: return int4(Tex[1].Sample(samp[1], float3(uv, 0.0)) * 255.0);
|
|
case 2u: return int4(Tex[2].Sample(samp[2], float3(uv, 0.0)) * 255.0);
|
|
case 3u: return int4(Tex[3].Sample(samp[3], float3(uv, 0.0)) * 255.0);
|
|
case 4u: return int4(Tex[4].Sample(samp[4], float3(uv, 0.0)) * 255.0);
|
|
case 5u: return int4(Tex[5].Sample(samp[5], float3(uv, 0.0)) * 255.0);
|
|
case 6u: return int4(Tex[6].Sample(samp[6], float3(uv, 0.0)) * 255.0);
|
|
case 7u: return int4(Tex[7].Sample(samp[7], float3(uv, 0.0)) * 255.0);
|
|
}
|
|
}
|
|
|
|
void main(
|
|
out float4 ocol0 : SV_Target0,
|
|
in float4 rawpos : SV_Position,
|
|
in float4 colors_0 : COLOR0,
|
|
in float4 colors_1 : COLOR1
|
|
,
|
|
in float3 tex[8] : TEXCOORD0,
|
|
in float4 clipPos : TEXCOORD8 ) {
|
|
int3 ColorInput[16];
|
|
// ColorInput initial state:
|
|
ColorInput[0] = color[0].rgb;
|
|
ColorInput[1] = color[0].aaa;
|
|
ColorInput[2] = color[1].rgb;
|
|
ColorInput[3] = color[1].aaa;
|
|
ColorInput[4] = color[2].rgb;
|
|
ColorInput[5] = color[2].aaa;
|
|
ColorInput[6] = color[3].rgb;
|
|
ColorInput[7] = color[3].aaa;
|
|
ColorInput[8] = int3(0, 0, 0); // TexColor.rgb (uninitilized)
|
|
ColorInput[9] = int3(0, 0, 0); // TexColor.aaa (uninitilized)
|
|
ColorInput[10] = int3(0, 0, 0); // RasColor.rgb (uninitilized)
|
|
ColorInput[11] = int3(0, 0, 0); // RasColor.aaa (uninitilized)
|
|
ColorInput[12] = int3(255, 255, 255); // One constant
|
|
ColorInput[13] = int3(128, 128, 128); // Half constant
|
|
ColorInput[14] = int3(0, 0, 0); // KonstColor.rgb (unititilized)
|
|
ColorInput[15] = int3(0, 0, 0); // Zero constant
|
|
|
|
int AlphaInput[8];
|
|
// AlphaInput's intial state:
|
|
AlphaInput[0] = color[0].a;
|
|
AlphaInput[1] = color[1].a;
|
|
AlphaInput[2] = color[2].a;
|
|
AlphaInput[3] = color[3].a;
|
|
AlphaInput[4] = 0; // TexColor.a (uninitilized)
|
|
AlphaInput[5] = 0; // RasColor.a (uninitilized)
|
|
AlphaInput[6] = 0; // KostColor.a (uninitilized)
|
|
AlphaInput[7] = 0; // Zero constant
|
|
|
|
int AlphaBump = 0;
|
|
int4 icolors_0 = int4(colors_0 * 255.0);
|
|
int4 icolors_1 = int4(colors_1 * 255.0);
|
|
int4 TevResult = color[0];
|
|
|
|
uint num_stages = bitfieldExtract(bpmem_genmode, 10, 4);
|
|
// Main tev loop
|
|
[loop]
|
|
for(uint stage = 0u; stage < num_stages; stage++)
|
|
{
|
|
uint cc = bpmem_combiners[stage].x;
|
|
uint ac = bpmem_combiners[stage].y;
|
|
uint order = bpmem_tevorder[stage>>1];
|
|
if ((stage & 1u) == 1u)
|
|
order = order >> 12;
|
|
|
|
// TODO: Indirect textures
|
|
|
|
// Sample texture for stage
|
|
int4 texColor;
|
|
if((order & 64u) != 0u) {
|
|
// Texture is enabled
|
|
uint sampler_num = bitfieldExtract(order, 0, 3);
|
|
uint tex_coord = bitfieldExtract(order, 3, 3);
|
|
|
|
// TODO: there is an optional perspective divide here (not to mention all of indirect)
|
|
int2 fixedPoint_uv = itrunc(tex[tex_coord].xy * texdim[sampler_num].zw * 128.0);
|
|
float2 uv = (float2(fixedPoint_uv) / 128.0) * texdim[sampler_num].xy;
|
|
|
|
texColor = sampleTexture(sampler_num, uv);
|
|
} else {
|
|
// Texture is disabled
|
|
texColor = int4(255, 255, 255, 255);
|
|
}
|
|
// TODO: color channel swapping
|
|
ColorInput[8] = texColor.rgb;
|
|
ColorInput[9] = texColor.aaa;
|
|
AlphaInput[4] = texColor.a;
|
|
|
|
// Set Konst for stage
|
|
uint tevksel = bpmem_tevksel[stage>>1];
|
|
int4 konst;
|
|
if ((stage & 1u) == 0u)
|
|
konst = int4(konstLookup[bitfieldExtract(tevksel, 4, 5)].rgb, konstLookup[bitfieldExtract(tevksel, 9, 5)].a);
|
|
else
|
|
konst = int4(konstLookup[bitfieldExtract(tevksel, 14, 5)].rgb, konstLookup[bitfieldExtract(tevksel, 19, 5)].a);
|
|
|
|
ColorInput[14] = konst.rgb;
|
|
AlphaInput[6] = konst.a;
|
|
|
|
// Set Ras for stage
|
|
int4 ras;
|
|
switch (bitfieldExtract(order, 7, 3)) {
|
|
case 0u: // Color 0
|
|
ras = icolors_0;
|
|
break;
|
|
case 1u: // Color 1
|
|
ras = icolors_1;
|
|
break;
|
|
case 5u: // Alpha Bump
|
|
ras = int4(AlphaBump, AlphaBump, AlphaBump, AlphaBump);
|
|
break;
|
|
case 6u: // Normalzied Alpha Bump
|
|
int normalized = AlphaBump | AlphaBump >> 5;
|
|
ras = int4(normalized, normalized, normalized, normalized);
|
|
break;
|
|
default:
|
|
ras = int4(0, 0, 0, 0);
|
|
break;
|
|
}
|
|
// TODO: color channel swapping
|
|
ColorInput[10] = ras.rgb;
|
|
ColorInput[11] = ras.aaa;
|
|
AlphaInput[5] = ras.a;
|
|
|
|
// Color Combiner
|
|
{
|
|
uint a = bitfieldExtract(cc, 12, 4);
|
|
uint b = bitfieldExtract(cc, 8, 4);
|
|
uint c = bitfieldExtract(cc, 4, 4);
|
|
uint d = bitfieldExtract(cc, 0, 4);
|
|
uint bias = bitfieldExtract(cc, 16, 2);
|
|
bool op = bool(bitfieldExtract(cc, 18, 1));
|
|
bool _clamp = bool(bitfieldExtract(cc, 19, 1));
|
|
uint shift = bitfieldExtract(cc, 20, 2);
|
|
uint dest = bitfieldExtract(cc, 22, 2);
|
|
|
|
int3 A = ColorInput[a] & int3(255, 255, 255);
|
|
int3 B = ColorInput[b] & int3(255, 255, 255);
|
|
int3 C = ColorInput[c] & int3(255, 255, 255);
|
|
int3 D = ColorInput[d]; // 10 bits + sign
|
|
|
|
int3 result;
|
|
if(bias != 3u) { // Normal mode
|
|
// Lerp A and B with C
|
|
C += C >> 7; // Scale C from 0..255 to 0..256
|
|
int3 lerp = (A << 8) + (B - A)*C;
|
|
if (shift != 3u) {
|
|
lerp = lerp << shift;
|
|
lerp = lerp + (op ? 127 : 128);
|
|
}
|
|
result = lerp >> 8;
|
|
|
|
// Add/Subtract D (and bias)
|
|
if (bias == 1u) result += 128;
|
|
else if (bias == 2u) result -= 128;
|
|
if(!op) // Add
|
|
result = D + result;
|
|
else // Subtract
|
|
result = D - result;
|
|
|
|
// Most of the Shift was moved inside the lerp for improved percision
|
|
// But we still do the divide by 2 here
|
|
if (shift == 3u)
|
|
result = result >> 1;
|
|
} else { // Compare mode
|
|
// Not implemented
|
|
result = int3(255, 0, 0);
|
|
}
|
|
|
|
// Clamp result
|
|
if (_clamp)
|
|
result = clamp(result, 0, 255);
|
|
else
|
|
result = clamp(result, -1024, 1023);
|
|
|
|
if (stage == num_stages) { // If this is the last stage
|
|
// Write result to output
|
|
TevResult.rgb = result;
|
|
//break;
|
|
} else {
|
|
// Write result to the correct input register of the next stage
|
|
ColorInput[dest<<1] = result;
|
|
}
|
|
}
|
|
// Alpha Combiner
|
|
{
|
|
uint a = bitfieldExtract(ac, 13, 3);
|
|
uint b = bitfieldExtract(ac, 10, 3);
|
|
uint c = bitfieldExtract(ac, 7, 3);
|
|
uint d = bitfieldExtract(ac, 4, 3);
|
|
uint bias = bitfieldExtract(ac, 16, 2);
|
|
bool op = bool(bitfieldExtract(ac, 18, 1));
|
|
bool _clamp = bool(bitfieldExtract(ac, 19, 1));
|
|
uint shift = bitfieldExtract(ac, 20, 2);
|
|
uint dest = bitfieldExtract(ac, 22, 2);
|
|
|
|
int A = AlphaInput[a] & 255;
|
|
int B = AlphaInput[b] & 255;
|
|
int C = AlphaInput[c] & 255;
|
|
int D = AlphaInput[d]; // 10 bits + sign
|
|
|
|
int result;
|
|
if(bias != 3u) { // Normal mode
|
|
// Lerp A and B with C
|
|
C += C >> 7; // Scale C from 0..255 to 0..256
|
|
int lerp = (A << 8) + (B - A)*C;
|
|
if (shift != 3u) {
|
|
lerp = lerp << shift;
|
|
lerp = lerp + (op ? 127 : 128);
|
|
}
|
|
result = lerp >> 8;
|
|
|
|
// Add/Subtract D (and bias)
|
|
if (bias == 1u) result += 128;
|
|
else if (bias == 2u) result -= 128;
|
|
if(!op) // Add
|
|
result = D + result;
|
|
else // Subtract
|
|
result = D - result;
|
|
|
|
// Most of the Shift was moved inside the lerp for improved percision
|
|
// But we still do the divide by 2 here
|
|
if (shift == 3u)
|
|
result = result >> 1;
|
|
} else { // Compare mode
|
|
// Not implemented
|
|
result = 255;
|
|
}
|
|
|
|
// Clamp result
|
|
if (_clamp)
|
|
result = clamp(result, 0, 255);
|
|
else
|
|
result = clamp(result, -1024, 1023);
|
|
|
|
if (stage == num_stages) { // If this is the last stage
|
|
// Write result to output
|
|
TevResult.a = result;
|
|
} else {
|
|
// Write result to the correct input register of the next stage
|
|
AlphaInput[dest] = result;
|
|
ColorInput[(dest << 1) + 1u] = int3(result, result, result);
|
|
}
|
|
}
|
|
} // Main tev loop
|
|
|
|
ocol0 = float4(TevResult) / 255.0;
|
|
|
|
}
|
|
";
|
|
return shaderVm;
|
|
}
|
|
|
|
public string Source
|
|
{
|
|
get => _source;
|
|
set => this.RaiseAndSetIfChanged(ref _source, value);
|
|
}
|
|
|
|
public override ReactiveObject CurrentViewModel => this;
|
|
}
|
|
}
|