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 }