-
Notifications
You must be signed in to change notification settings - Fork 200
/
IGraphicsAPI.Core.cs
269 lines (223 loc) · 8.71 KB
/
IGraphicsAPI.Core.cs
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
using System;
using OpenTK;
namespace ClassicalSharp.GraphicsAPI {
/// <summary> Abstracts a 3D graphics rendering API. </summary>
public abstract unsafe partial class IGraphicsApi {
public int defaultIb;
protected void InitCommon() {
quadVb = CreateDynamicVb(VertexFormat.P3fC4b, 4);
texVb = CreateDynamicVb(VertexFormat.P3fT2fC4b, 4);
const int maxIndices = 65536 / 4 * 6;
ushort* indices = stackalloc ushort[maxIndices];
MakeIndices(indices, maxIndices);
defaultIb = CreateIb((IntPtr)indices, maxIndices);
}
protected void DisposeCommon() {
DeleteVb(ref quadVb);
DeleteVb(ref texVb);
DeleteIb(ref defaultIb);
}
public virtual void Dispose() {
DisposeCommon();
}
public void LoseContext(string reason) {
LostContext = true;
Utils.LogDebug("Lost graphics context" + reason);
Events.RaiseContextLost();
DisposeCommon();
}
public void RecreateContext() {
LostContext = false;
Utils.LogDebug("Recreating graphics context");
Events.RaiseContextRecreated();
InitCommon();
}
/// <summary> Binds and draws the specified subset of the vertices in the current dynamic vertex buffer<br/>
/// This method also replaces the dynamic vertex buffer's data first with the given vertices before drawing. </summary>
public void UpdateDynamicVb_Lines(int vb, VertexP3fC4b[] vertices, int vCount) {
fixed (VertexP3fC4b* ptr = vertices) {
SetDynamicVbData(vb, (IntPtr)ptr, vCount);
DrawVb_Lines(vCount);
}
}
/// <summary> Binds and draws the specified subset of the vertices in the current dynamic vertex buffer<br/>
/// This method also replaces the dynamic vertex buffer's data first with the given vertices before drawing. </summary>
public void UpdateDynamicVb_IndexedTris(int vb, VertexP3fC4b[] vertices, int vCount) {
fixed (VertexP3fC4b* ptr = vertices) {
SetDynamicVbData(vb, (IntPtr)ptr, vCount);
DrawVb_IndexedTris(vCount);
}
}
/// <summary> Binds and draws the specified subset of the vertices in the current dynamic vertex buffer<br/>
/// This method also replaces the dynamic vertex buffer's data first with the given vertices before drawing. </summary>
public void UpdateDynamicVb_IndexedTris(int vb, VertexP3fT2fC4b[] vertices, int vCount) {
fixed (VertexP3fT2fC4b* ptr = vertices) {
SetDynamicVbData(vb, (IntPtr)ptr, vCount);
DrawVb_IndexedTris(vCount);
}
}
internal VertexP3fC4b[] quadVerts = new VertexP3fC4b[4];
internal int quadVb;
public virtual void Draw2DQuad(int x, int y, int width, int height,
PackedCol col) {
VertexP3fC4b[] verts = quadVerts;
VertexP3fC4b v; v.Z = 0; v.Col = col;
v.X = x; v.Y = y; verts[0] = v;
v.X = x + width; verts[1] = v;
v.Y = y + height; verts[2] = v;
v.X = x; verts[3] = v;
SetBatchFormat(VertexFormat.P3fC4b);
UpdateDynamicVb_IndexedTris(quadVb, verts, 4);
}
public virtual void Draw2DQuad(int x, int y, int width, int height,
PackedCol topCol, PackedCol bottomCol) {
VertexP3fC4b[] verts = quadVerts;
VertexP3fC4b v; v.Z = 0; v.Col = topCol;
v.X = x; v.Y = y; verts[0] = v;
v.X = x + width; verts[1] = v;
v.Col = bottomCol;
v.Y = y + height; verts[2] = v;
v.X = x; verts[3] = v;
SetBatchFormat(VertexFormat.P3fC4b);
UpdateDynamicVb_IndexedTris(quadVb, verts, 4);
}
internal VertexP3fT2fC4b[] texVerts = new VertexP3fT2fC4b[4];
internal int texVb;
public virtual void Draw2DTexture(ref Texture tex, PackedCol col) {
int index = 0;
Make2DQuad(ref tex, col, texVerts, ref index);
SetBatchFormat(VertexFormat.P3fT2fC4b);
UpdateDynamicVb_IndexedTris(texVb, texVerts, 4);
}
public static void Make2DQuad(ref Texture tex, PackedCol col,
VertexP3fT2fC4b[] vertices, ref int index) {
float x1 = tex.X, y1 = tex.Y, x2 = tex.X + tex.Width, y2 = tex.Y + tex.Height;
#if USE_DX
// NOTE: see "https://msdn.microsoft.com/en-us/library/windows/desktop/bb219690(v=vs.85).aspx",
// i.e. the msdn article called "Directly Mapping Texels to Pixels (Direct3D 9)" for why we have to do this.
x1 -= 0.5f; x2 -= 0.5f;
y1 -= 0.5f; y2 -= 0.5f;
#endif
VertexP3fT2fC4b v; v.Z = 0; v.Col = col;
v.X = x1; v.Y = y1; v.U = tex.U1; v.V = tex.V1; vertices[index++] = v;
v.X = x2; v.U = tex.U2; vertices[index++] = v;
v.Y = y2; v.V = tex.V2; vertices[index++] = v;
v.X = x1; v.U = tex.U1; vertices[index++] = v;
}
bool hadFog;
/// <summary> Updates the various matrix stacks and properties so that the graphics API state
/// is suitable for rendering 2D quads and other 2D graphics to. </summary>
public void Mode2D(int width, int height) {
SetMatrixMode(MatrixType.Projection);
Matrix4 ortho;
CalcOrthoMatrix(width, height, out ortho);
LoadMatrix(ref ortho);
SetMatrixMode(MatrixType.Modelview);
LoadIdentityMatrix();
DepthTest = false;
AlphaBlending = true;
hadFog = Fog;
if (hadFog) Fog = false;
}
/// <summary> Updates the various matrix stacks and properties so that the graphics API state
/// is suitable for rendering 3D vertices. </summary>
public void Mode3D() {
SetMatrixMode(MatrixType.Projection);
LoadMatrix(ref Projection);
SetMatrixMode(MatrixType.Modelview);
LoadMatrix(ref View);
DepthTest = true;
AlphaBlending = false;
if (hadFog) Fog = true;
}
internal unsafe void MakeIndices(ushort* indices, int iCount) {
int element = 0;
for (int i = 0; i < iCount; i += 6) {
*indices = (ushort)(element + 0); indices++;
*indices = (ushort)(element + 1); indices++;
*indices = (ushort)(element + 2); indices++;
*indices = (ushort)(element + 2); indices++;
*indices = (ushort)(element + 3); indices++;
*indices = (ushort)(element + 0); indices++;
element += 4;
}
}
const int alphaMask = unchecked((int)0xFF000000);
// Quoted from http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/
// The short version: if you want your renderer to properly handle textures with alphas when using
// bilinear interpolation or mipmapping, you need to premultiply your PNG color data by their (unassociated) alphas.
static int Average(int p1, int p2) {
int a1 = ((p1 & alphaMask) >> 24) & 0xFF;
int a2 = ((p2 & alphaMask) >> 24) & 0xFF;
int aSum = (a1 + a2);
aSum = aSum > 0 ? aSum : 1; // avoid divide by 0 below
// Convert RGB to pre-multiplied form
int r1 = ((p1 >> 16) & 0xFF) * a1, g1 = ((p1 >> 8) & 0xFF) * a1, b1 = (p1 & 0xFF) * a1;
int r2 = ((p2 >> 16) & 0xFF) * a2, g2 = ((p2 >> 8) & 0xFF) * a2, b2 = (p2 & 0xFF) * a2;
// https://stackoverflow.com/a/347376
// We need to convert RGB back from the pre-multiplied average into normal form
// ((r1 + r2) / 2) / ((a1 + a2) / 2)
// but we just cancel out the / 2
int aAve = aSum >> 1;
int rAve = (r1 + r2) / aSum;
int gAve = (g1 + g2) / aSum;
int bAve = (b1 + b2) / aSum;
return (aAve << 24) | (rAve << 16) | (gAve << 8) | bAve;
}
internal static unsafe void GenMipmaps(int width, int height, IntPtr lvlScan0, IntPtr scan0) {
int* baseSrc = (int*)scan0, baseDst = (int*)lvlScan0;
int srcWidth = width << 1;
for (int y = 0; y < height; y++) {
int srcY = (y << 1);
int* src0 = baseSrc + srcY * srcWidth, src1 = src0 + srcWidth;
int* dst = baseDst + y * width;
for (int x = 0; x < width; x++) {
int srcX = (x << 1);
int src00 = src0[srcX], src01 = src0[srcX + 1];
int src10 = src1[srcX], src11 = src1[srcX + 1];
// bilinear filter this mipmap
int ave0 = Average(src00, src01);
int ave1 = Average(src10, src11);
dst[x] = Average(ave0, ave1);
}
}
}
internal int MipmapsLevels(int width, int height) {
int lvlsWidth = Utils.Log2(width), lvlsHeight = Utils.Log2(height);
if (CustomMipmapsLevels) {
int lvls = Math.Min(lvlsWidth, lvlsHeight);
return Math.Min(lvls, 4);
} else {
return Math.Max(lvlsWidth, lvlsHeight);
}
}
}
public enum VertexFormat {
P3fC4b = 0, P3fT2fC4b = 1,
}
public enum CompareFunc {
Always = 0,
NotEqual = 1,
Never = 2,
Less = 3,
LessEqual = 4,
Equal = 5,
GreaterEqual = 6,
Greater = 7,
}
public enum BlendFunc {
Zero = 0,
One = 1,
SourceAlpha = 2,
InvSourceAlpha = 3,
DestAlpha = 4,
InvDestAlpha = 5,
}
public enum Fog {
Linear = 0, Exp = 1, Exp2 = 2,
}
public enum MatrixType {
Projection = 0, Modelview = 1, Texture = 2,
}
}