1 module buffer;
2
3 import deferred;
4
5 import gfx.core;
6 import gfx.graal;
7 import gfx.math;
8
9 struct P3N3Vertex
10 {
11 FVec3 pos;
12 FVec3 normal;
13 }
14
15 struct P2T2Vertex
16 {
17 FVec2 pos;
18 FVec2 texCoord;
19 }
20
21 /// per frame UBO for the geom pipeline
22 struct GeomFrameUbo
23 {
24 FMat4 viewProj = FMat4.identity;
25 }
26 /// per draw call UBO for the geom pipeline
27 struct GeomModelData
28 {
29 FMat4 model = FMat4.identity;
30 FVec4 color = fvec(0, 0, 0, 1);
31 float[4] pad; // 32 bytes alignment for dynamic offset
32 }
33 static assert(GeomModelData.alignof / 32 == 0);
34 /// ditto
35 struct GeomModelUbo
36 {
37 GeomModelData[3] data;
38 }
39 static assert(GeomModelUbo.alignof / 32 == 0);
40
41 /// per frame UBO for the light pipeline
42 struct LightFrameUbo
43 {
44 FVec4 viewerPos = fvec(0, 0, 0, 1);
45 }
46 /// per draw call UBO for the light pipeline
47 struct LightModelUbo
48 {
49 FMat4 modelViewProj = FMat4.identity;
50 FVec4 position = fvec(0, 0, 0, 1);
51 /// color in RGB, luminosity in A
52 FVec4 colAndLum = fvec(0, 0, 0, 1);
53 float[8] pad; // 32 bytes alignment for dynamic offset
54 }
55 static assert(LightModelUbo.alignof / 32 == 0);
56
57 struct MeshBuffer
58 {
59 Rc!Buffer indexBuf;
60 Rc!Buffer vertexBuf;
61 Interval!size_t indices;
62 Interval!size_t vertices;
63
64 @property uint indicesCount()
65 {
66 return cast(uint)indices.length / ushort.sizeof;
67 }
68
69 void bindIndex(CommandBuffer cmd)
70 {
71 cmd.bindIndexBuffer(indexBuf, indices.start, IndexType.u16);
72 }
73
74 VertexBinding vertexBinding()
75 {
76 return VertexBinding(
77 vertexBuf.obj, vertices.start,
78 );
79 }
80
81 void release()
82 {
83 indexBuf.unload();
84 vertexBuf.unload();
85 }
86 }
87
88
89 struct DynBuffer(T)
90 {
91 Rc!Buffer buffer;
92 MemoryMap mmap;
93 size_t len;
94
95 this(DeferredExample ex, size_t len, BufferUsage usage)
96 {
97 buffer = ex.createDynamicBuffer(len * T.sizeof, usage);
98 mmap = buffer.boundMemory.map();
99 this.len = len;
100 }
101
102 ~this()
103 {
104 mmap = MemoryMap.init;
105 buffer.unload();
106 }
107
108 @property T[] data()
109 {
110 return mmap.view!(T[])()[];
111 }
112
113 void addToMemorySet(ref MappedMemorySet mms)
114 {
115 mmap.addToSet(mms);
116 }
117
118 BufferDescriptor descriptor(in size_t start=0, in size_t size=0)
119 {
120 return buffer.descriptor(start*T.sizeof, size*T.sizeof);
121 }
122 }
123
124 class DeferredBuffers : AtomicRefCounted
125 {
126 MeshBuffer hiResSphere;
127 // normals pointing inwards to render "bulbs" with point light inside
128 MeshBuffer invertedSphere;
129 MeshBuffer loResSphere;
130 MeshBuffer square;
131
132 DynBuffer!GeomFrameUbo geomFrameUbo;
133 DynBuffer!GeomModelUbo geomModelUbo;
134 DynBuffer!LightFrameUbo lightFrameUbo;
135 DynBuffer!LightModelUbo lightModelUbo;
136
137 this(DeferredExample ex, size_t saucerCount)
138 {
139 prepareMeshBuffers(ex);
140 prepareUboBuffers(ex, saucerCount);
141 }
142
143 override void dispose()
144 {
145 import std.algorithm : move;
146
147 hiResSphere.release();
148 invertedSphere.release();
149 loResSphere.release();
150 square.release();
151 move(geomFrameUbo);
152 move(geomModelUbo);
153 move(lightFrameUbo);
154 move(lightModelUbo);
155 }
156
157 final void prepareMeshBuffers(DeferredExample ex)
158 {
159 import std.algorithm : map;
160 import std.array : array;
161
162 const hiRes = buildUvSpheroid(fvec(0, 0, 0), 1f, 1f, 15);
163 const loRes = buildUvSpheroid(fvec(0, 0, 0), 1f, 1f, 6);
164
165 const invertedVtx = hiRes.vertices.map!(v => P3N3Vertex(v.pos, -v.normal)).array;
166
167 const ushort[] squareIndices = [ 0, 1, 2, 0, 2, 3 ];
168 const squareVertices = [
169 P2T2Vertex(fvec(-1, -1), fvec(0, 0)),
170 P2T2Vertex(fvec(-1, 1), fvec(0, 1)),
171 P2T2Vertex(fvec(1, 1), fvec(1, 1)),
172 P2T2Vertex(fvec(1, -1), fvec(1, 0)),
173 ];
174
175 Rc!Buffer indexBuf = ex.createStaticBuffer(
176 hiRes.indices ~ loRes.indices ~ squareIndices,
177 BufferUsage.index
178 );
179
180 const hiResVData = cast(const(ubyte)[])hiRes.vertices;
181 const invertedVData = cast(const(ubyte)[])invertedVtx;
182 const loResVData = cast(const(ubyte)[])loRes.vertices.map!(v => v.pos).array;
183 const squareVData = cast(const(ubyte)[])squareVertices;
184 Rc!Buffer vertexBuf = ex.createStaticBuffer(
185 hiResVData ~ invertedVData ~ loResVData ~ squareVData,
186 BufferUsage.vertex);
187
188 const hiResI = hiRes.indices.length * ushort.sizeof;
189 const invertedI = hiResI;
190 const loResI = invertedI + loRes.indices.length * ushort.sizeof;
191 const squareI = loResI + squareIndices.length * ushort.sizeof;
192
193 const hiResV = hiRes.vertices.length * P3N3Vertex.sizeof;
194 const invertedV = 2 * hiResV;
195 const loResV = invertedV + loRes.vertices.length * FVec3.sizeof;
196 const squareV = loResV + squareVertices.length * P2T2Vertex.sizeof;
197
198 hiResSphere.indexBuf = indexBuf;
199 hiResSphere.vertexBuf = vertexBuf;
200 hiResSphere.indices = interval(0, hiResI);
201 hiResSphere.vertices = interval(0, hiResV);
202
203 invertedSphere.indexBuf = indexBuf;
204 invertedSphere.vertexBuf = vertexBuf;
205 invertedSphere.indices = interval(0, hiResI);
206 invertedSphere.vertices = interval(hiResV, invertedV);
207
208 loResSphere.indexBuf = indexBuf;
209 loResSphere.vertexBuf = vertexBuf;
210 loResSphere.indices = interval(hiResI, loResI);
211 loResSphere.vertices = interval(invertedV, loResV);
212
213 square.indexBuf = indexBuf;
214 square.vertexBuf = vertexBuf;
215 square.indices = interval(loResI, squareI);
216 square.vertices = interval(loResV, squareV);
217 }
218
219 final void prepareUboBuffers(DeferredExample ex, size_t saucerCount)
220 {
221 geomFrameUbo = DynBuffer!GeomFrameUbo(ex, 1, BufferUsage.uniform);
222 geomModelUbo = DynBuffer!GeomModelUbo(ex, saucerCount, BufferUsage.uniform);
223 lightFrameUbo = DynBuffer!LightFrameUbo(ex, 1, BufferUsage.uniform);
224 lightModelUbo = DynBuffer!LightModelUbo(ex, saucerCount, BufferUsage.uniform);
225 }
226
227 final void flush(Device device)
228 {
229 MappedMemorySet mms;
230 geomModelUbo.addToMemorySet(mms);
231 geomFrameUbo.addToMemorySet(mms);
232 lightModelUbo.addToMemorySet(mms);
233 lightFrameUbo.addToMemorySet(mms);
234 device.flushMappedMemory(mms);
235 }
236 }
237
238 struct Mesh
239 {
240 ushort[] indices;
241 P3N3Vertex[] vertices;
242 }
243
244 Mesh buildUvSpheroid(in FVec3 center, in float radius, in float height,
245 in uint latDivs = 8)
246 {
247 import std.array : uninitializedArray;
248 import std.math : PI, cos, sin;
249
250 const longDivs = latDivs * 2;
251 const totalVertices = 2 + (latDivs - 1) * longDivs;
252 const totalIndices = 3 * longDivs * (2 + 2 * (latDivs - 2));
253
254 auto vertices = uninitializedArray!(P3N3Vertex[])(totalVertices);
255
256 size_t ind = 0;
257 void unitVertex(in FVec3 pos)
258 {
259 const v = fvec(radius * pos.xy, height * pos.z);
260 vertices[ind++] = P3N3Vertex(center + v, normalize(v));
261 }
262
263 const latAngle = PI / latDivs;
264 const longAngle = 2 * PI / longDivs;
265
266 // north pole
267 unitVertex(fvec(0, 0, 1));
268 // latitudes
269 foreach (lat; 1 .. latDivs)
270 {
271 const alpha = latAngle * lat;
272 const z = cos(alpha);
273 const sa = sin(alpha);
274 foreach (lng; 0 .. longDivs)
275 {
276 const beta = longAngle * lng;
277 const x = cos(beta) * sa;
278 const y = sin(beta) * sa;
279
280 unitVertex(fvec(x, y, z));
281 }
282 }
283 // south pole
284 unitVertex(fvec(0, 0, -1));
285 assert(ind == totalVertices);
286
287 // build ccw triangle faces
288 auto indices = uninitializedArray!(ushort[])(totalIndices);
289 ind = 0;
290 void face(in size_t v0, in size_t v1, in size_t v2)
291 {
292 indices[ind++] = cast(ushort) v0;
293 indices[ind++] = cast(ushort) v1;
294 indices[ind++] = cast(ushort) v2;
295 }
296
297 size_t left(size_t lng)
298 {
299 return lng;
300 }
301
302 size_t right(size_t lng)
303 {
304 return lng == longDivs - 1 ? 0 : lng + 1;
305 }
306
307 // northern div triangles
308 foreach (lng; 0 .. longDivs)
309 {
310 const pole = 0;
311 const bot = 1;
312 face(pole, bot + left(lng), bot + right(lng));
313 }
314 // middle divs rectangles
315 foreach (lat; 0 .. latDivs - 2)
316 {
317 const top = 1 + lat * longDivs;
318 const bot = top + longDivs;
319 foreach (lng; 0 .. longDivs)
320 {
321 const l = left(lng);
322 const r = right(lng);
323
324 face(top + l, bot + l, bot + r);
325 face(top + l, bot + r, top + r);
326 }
327 }
328 // southern div triangles
329 foreach (lng; 0 .. longDivs)
330 {
331 const pole = totalVertices - 1;
332 const top = 1 + (latDivs - 2) * longDivs;
333 face(pole, top + right(lng), top + left(lng));
334 }
335 assert(ind == totalIndices);
336
337 return Mesh(indices, vertices);
338 }