summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/LightingShaderGen.cpp
blob: 5fa2c5b977bdca474dc2e0bb6cbbbb479c2359ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "VideoCommon/LightingShaderGen.h"

#include "Common/Assert.h"
#include "Common/CommonTypes.h"

#include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/XFMemory.h"

static void GenerateLightShader(ShaderCode& object, const LightingUidData& uid_data, int index,
                                int litchan_index, bool alpha)
{
  const char* swizzle = alpha ? "a" : "rgb";
  const char* swizzle_components = (alpha) ? "" : "3";

  int attnfunc = (uid_data.attnfunc >> (2 * litchan_index)) & 0x3;
  int diffusefunc = (uid_data.diffusefunc >> (2 * litchan_index)) & 0x3;

  switch (attnfunc)
  {
  case LIGHTATTN_NONE:
  case LIGHTATTN_DIR:
    object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
    object.Write("attn = 1.0;\n");
    object.Write("if (length(ldir) == 0.0)\n\t ldir = _norm0;\n");
    break;
  case LIGHTATTN_SPEC:
    object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
    object.Write("attn = (dot(_norm0, ldir) >= 0.0) ? max(0.0, dot(_norm0, " LIGHT_DIR
                 ".xyz)) : 0.0;\n",
                 LIGHT_DIR_PARAMS(index));
    object.Write("cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index));
    object.Write("distAttn = %s(" LIGHT_DISTATT ".xyz);\n",
                 (diffusefunc == LIGHTDIF_NONE) ? "" : "normalize", LIGHT_DISTATT_PARAMS(index));
    object.Write("attn = max(0.0f, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
                 "float3(1.0, attn, attn*attn));\n");
    break;
  case LIGHTATTN_SPOT:
    object.Write("ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index));
    object.Write("dist2 = dot(ldir, ldir);\n"
                 "dist = sqrt(dist2);\n"
                 "ldir = ldir / dist;\n"
                 "attn = max(0.0, dot(ldir, " LIGHT_DIR ".xyz));\n",
                 LIGHT_DIR_PARAMS(index));
    // attn*attn may overflow
    object.Write("attn = max(0.0, " LIGHT_COSATT ".x + " LIGHT_COSATT ".y*attn + " LIGHT_COSATT
                 ".z*attn*attn) / dot(" LIGHT_DISTATT ".xyz, float3(1.0,dist,dist2));\n",
                 LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index),
                 LIGHT_DISTATT_PARAMS(index));
    break;
  }

  switch (diffusefunc)
  {
  case LIGHTDIF_NONE:
    object.Write("lacc.%s += int%s(round(attn * float%s(" LIGHT_COL ")));\n", swizzle,
                 swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
    break;
  case LIGHTDIF_SIGN:
  case LIGHTDIF_CLAMP:
    object.Write("lacc.%s += int%s(round(attn * %sdot(ldir, _norm0)) * float%s(" LIGHT_COL ")));\n",
                 swizzle, swizzle_components, diffusefunc != LIGHTDIF_SIGN ? "max(0.0," : "(",
                 swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
    break;
  default:
    ASSERT(0);
  }

  object.Write("\n");
}

// vertex shader
// lights/colors
// materials name is I_MATERIALS in vs and I_PMATERIALS in ps
// inColorName is color in vs and colors_ in ps
// dest is o.colors_ in vs and colors_ in ps
void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_data, int components,
                                const char* inColorName, const char* dest)
{
  for (unsigned int j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
  {
    object.Write("{\n");

    bool colormatsource = !!(uid_data.matsource & (1 << j));
    if (colormatsource)  // from vertex
    {
      if (components & (VB_HAS_COL0 << j))
        object.Write("int4 mat = int4(round(%s%d * 255.0));\n", inColorName, j);
      else if (components & VB_HAS_COL0)
        object.Write("int4 mat = int4(round(%s0 * 255.0));\n", inColorName);
      else
        object.Write("int4 mat = int4(255, 255, 255, 255);\n");
    }
    else  // from color
    {
      object.Write("int4 mat = %s[%d];\n", I_MATERIALS, j + 2);
    }

    if (uid_data.enablelighting & (1 << j))
    {
      if (uid_data.ambsource & (1 << j))  // from vertex
      {
        if (components & (VB_HAS_COL0 << j))
          object.Write("lacc = int4(round(%s%d * 255.0));\n", inColorName, j);
        else if (components & VB_HAS_COL0)
          object.Write("lacc = int4(round(%s0 * 255.0));\n", inColorName);
        else
          // TODO: this isn't verified. Here we want to read the ambient from the vertex,
          // but the vertex itself has no color. So we don't know which value to read.
          // Returning 1.0 is the same as disabled lightning, so this could be fine
          object.Write("lacc = int4(255, 255, 255, 255);\n");
      }
      else  // from color
      {
        object.Write("lacc = %s[%d];\n", I_MATERIALS, j);
      }
    }
    else
    {
      object.Write("lacc = int4(255, 255, 255, 255);\n");
    }

    // check if alpha is different
    bool alphamatsource = !!(uid_data.matsource & (1 << (j + 2)));
    if (alphamatsource != colormatsource)
    {
      if (alphamatsource)  // from vertex
      {
        if (components & (VB_HAS_COL0 << j))
          object.Write("mat.w = int(round(%s%d.w * 255.0));\n", inColorName, j);
        else if (components & VB_HAS_COL0)
          object.Write("mat.w = int(round(%s0.w * 255.0));\n", inColorName);
        else
          object.Write("mat.w = 255;\n");
      }
      else  // from color
      {
        object.Write("mat.w = %s[%d].w;\n", I_MATERIALS, j + 2);
      }
    }

    if (uid_data.enablelighting & (1 << (j + 2)))
    {
      if (uid_data.ambsource & (1 << (j + 2)))  // from vertex
      {
        if (components & (VB_HAS_COL0 << j))
          object.Write("lacc.w = int(round(%s%d.w * 255.0));\n", inColorName, j);
        else if (components & VB_HAS_COL0)
          object.Write("lacc.w = int(round(%s0.w * 255.0));\n", inColorName);
        else
          // TODO: The same for alpha: We want to read from vertex, but the vertex has no color
          object.Write("lacc.w = 255;\n");
      }
      else  // from color
      {
        object.Write("lacc.w = %s[%d].w;\n", I_MATERIALS, j);
      }
    }
    else
    {
      object.Write("lacc.w = 255;\n");
    }

    if (uid_data.enablelighting & (1 << j))  // Color lights
    {
      for (int i = 0; i < 8; ++i)
        if (uid_data.light_mask & (1 << (i + 8 * j)))
          GenerateLightShader(object, uid_data, i, j, false);
    }
    if (uid_data.enablelighting & (1 << (j + 2)))  // Alpha lights
    {
      for (int i = 0; i < 8; ++i)
        if (uid_data.light_mask & (1 << (i + 8 * (j + 2))))
          GenerateLightShader(object, uid_data, i, j + 2, true);
    }
    object.Write("lacc = clamp(lacc, 0, 255);\n");
    object.Write("%s%d = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n", dest, j);
    object.Write("}\n");
  }
}

void GetLightingShaderUid(LightingUidData& uid_data)
{
  for (unsigned int j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
  {
    uid_data.matsource |= xfmem.color[j].matsource << j;
    uid_data.matsource |= xfmem.alpha[j].matsource << (j + 2);
    uid_data.enablelighting |= xfmem.color[j].enablelighting << j;
    uid_data.enablelighting |= xfmem.alpha[j].enablelighting << (j + 2);

    if (uid_data.enablelighting & (1 << j))  // Color lights
    {
      uid_data.ambsource |= xfmem.color[j].ambsource << j;
      uid_data.attnfunc |= xfmem.color[j].attnfunc << (2 * j);
      uid_data.diffusefunc |= xfmem.color[j].diffusefunc << (2 * j);
      uid_data.light_mask |= xfmem.color[j].GetFullLightMask() << (8 * j);
    }
    if (uid_data.enablelighting & (1 << (j + 2)))  // Alpha lights
    {
      uid_data.ambsource |= xfmem.alpha[j].ambsource << (j + 2);
      uid_data.attnfunc |= xfmem.alpha[j].attnfunc << (2 * (j + 2));
      uid_data.diffusefunc |= xfmem.alpha[j].diffusefunc << (2 * (j + 2));
      uid_data.light_mask |= xfmem.alpha[j].GetFullLightMask() << (8 * (j + 2));
    }
  }
}