1 module texture;
2
3 import example;
4
5 import gfx.core;
6 import gfx.graal;
7 import gfx.window;
8
9 import gl3n.linalg : mat4, mat3, vec3, vec4;
10
11 import std.exception;
12 import std.stdio;
13 import std.typecons;
14 import std.math;
15
16 class TextureExample : Example
17 {
18 Rc!RenderPass renderPass;
19 Rc!Pipeline pipeline;
20 Rc!PipelineLayout layout;
21 ushort[] indices;
22 Rc!Buffer vertBuf;
23 Rc!Buffer indBuf;
24 Rc!Buffer matBuf;
25 Rc!Buffer ligBuf;
26 Rc!Image texImg;
27 Rc!ImageView texView;
28 Rc!Sampler texSampler;
29 Rc!DescriptorPool descPool;
30 Rc!DescriptorSetLayout setLayout;
31 DescriptorSet set;
32
33 struct Vertex {
34 float[3] position;
35 float[3] normal;
36 float[2] tex;
37 }
38
39 struct Matrices {
40 float[4][4] mvp;
41 float[4][4] normal;
42 }
43
44 enum maxLights = 5;
45
46 struct Light {
47 float[4] direction;
48 float[4] color;
49 }
50
51 struct Lights {
52 Light[maxLights] lights;
53 uint num;
54 }
55
56 this(string[] args) {
57 super("Texture", args);
58 }
59
60 override void dispose()
61 {
62 if (device) {
63 device.waitIdle();
64 }
65 vertBuf.unload();
66 indBuf.unload();
67 matBuf.unload();
68 ligBuf.unload();
69 texImg.unload();
70 texView.unload();
71 texSampler.unload();
72 setLayout.unload();
73 descPool.unload();
74 layout.unload();
75 pipeline.unload();
76 renderPass.unload();
77 super.dispose();
78 }
79
80 override void prepare()
81 {
82 super.prepare();
83 prepareBuffers();
84 prepareTexture();
85 preparePipeline();
86 prepareDescriptorSet();
87 }
88
89 void prepareBuffers()
90 {
91 import gfx.genmesh.cube : genCube;
92 import gfx.genmesh.algorithm : indexCollectMesh, triangulate, vertices;
93 import gfx.genmesh.poly : quad;
94 import std.algorithm : map;
95
96 auto crate = genCube()
97 .map!(f => quad(
98 Vertex( f[0].p, f[0].n, [ 0f, 0f ] ),
99 Vertex( f[1].p, f[1].n, [ 0f, 1f ] ),
100 Vertex( f[2].p, f[2].n, [ 1f, 1f ] ),
101 Vertex( f[3].p, f[3].n, [ 1f, 0f ] ),
102 ))
103 .triangulate()
104 .vertices()
105 .indexCollectMesh();
106
107 auto normalize(in float[4] vec) {
108 return vec4(vec).normalized().vector;
109 }
110 const lights = Lights( [
111 Light(normalize([1.0, 1.0, -1.0, 0.0]), [0.8, 0.5, 0.2, 1.0]),
112 Light(normalize([-1.0, 1.0, -1.0, 0.0]), [0.2, 0.5, 0.8, 1.0]),
113 Light.init, Light.init, Light.init
114 ], 2);
115
116 indices = crate.indices;
117 vertBuf = createStaticBuffer(crate.vertices, BufferUsage.vertex);
118 indBuf = createStaticBuffer(crate.indices, BufferUsage.index);
119
120 matBuf = createDynamicBuffer(Matrices.sizeof, BufferUsage.uniform);
121 ligBuf = createStaticBuffer(lights, BufferUsage.uniform);
122 }
123
124 void prepareTexture()
125 {
126 import img : ImageFormat, ImgImage = Image;
127 auto img = ImgImage.loadFromView!("crate.jpg")(ImageFormat.argb);
128 texImg = createTextureImage(
129 cast(const(void)[])img.data, ImageInfo.d2(img.width, img.height).withFormat(Format.rgba8_uNorm)
130 );
131 // argb swizzling
132 version(LittleEndian) {
133 const swizzle = Swizzle.bgra;
134 }
135 else {
136 const swizzle = Swizzle.argb;
137 }
138 texView = texImg.createView(ImageType.d2, ImageSubresourceRange(ImageAspect.color), swizzle);
139
140 import gfx.core.typecons : some;
141
142 texSampler = device.createSampler(SamplerInfo(
143 Filter.linear, Filter.linear, Filter.nearest,
144 [WrapMode.repeat, WrapMode.repeat, WrapMode.repeat],
145 some(16f), 0f, [0f, 0f]
146 ));
147 }
148
149 override void prepareRenderPass()
150 {
151 const attachments = [
152 AttachmentDescription(swapchain.format, 1,
153 AttachmentOps(LoadOp.clear, StoreOp.store),
154 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare),
155 trans(ImageLayout.undefined, ImageLayout.presentSrc),
156 No.mayAlias
157 )
158 ];
159 const subpasses = [
160 SubpassDescription(
161 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ],
162 none!AttachmentRef, []
163 )
164 ];
165
166 renderPass = device.createRenderPass(attachments, subpasses, []);
167 }
168
169 class TextureFrameData : FrameData
170 {
171 PrimaryCommandBuffer cmdBuf;
172 Rc!Framebuffer framebuffer;
173
174 this(ImageBase swcColor, CommandBuffer tempBuf)
175 {
176 super(swcColor);
177 cmdBuf = cmdPool.allocatePrimary(1)[0];
178
179 auto colorView = swcColor.createView(
180 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity
181 ).rc;
182
183 this.framebuffer = this.outer.device.createFramebuffer(this.outer.renderPass, [
184 colorView.obj
185 ], size[0], size[1], 1);
186 }
187
188 override void dispose()
189 {
190 framebuffer.unload();
191 cmdPool.free([ cast(CommandBuffer)cmdBuf ]);
192 super.dispose();
193 }
194 }
195
196 void preparePipeline()
197 {
198 const spv = [
199 import("shader.vert.spv"), import("shader.frag.spv")
200 ];
201 auto vtxShader = device.createShaderModule(
202 cast(immutable(uint)[])spv[0], "main"
203 ).rc;
204 auto fragShader = device.createShaderModule(
205 cast(immutable(uint)[])spv[1], "main"
206 ).rc;
207
208 const layoutBindings = [
209 PipelineLayoutBinding(0, DescriptorType.uniformBuffer, 1, ShaderStage.vertex),
210 PipelineLayoutBinding(1, DescriptorType.uniformBuffer, 1, ShaderStage.fragment),
211 PipelineLayoutBinding(2, DescriptorType.combinedImageSampler, 1, ShaderStage.fragment),
212 ];
213
214 setLayout = device.createDescriptorSetLayout(layoutBindings);
215 layout = device.createPipelineLayout([ setLayout.obj ], []);
216
217 PipelineInfo info;
218 info.shaders.vertex = vtxShader;
219 info.shaders.fragment = fragShader;
220 info.inputBindings = [
221 VertexInputBinding(0, Vertex.sizeof, No.instanced)
222 ];
223 info.inputAttribs = [
224 VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0),
225 VertexInputAttrib(1, 0, Format.rgb32_sFloat, Vertex.normal.offsetof),
226 VertexInputAttrib(2, 0, Format.rg32_sFloat, Vertex.tex.offsetof),
227 ];
228 info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
229 info.rasterizer = Rasterizer(
230 PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp,
231 none!DepthBias, 1f
232 );
233 info.viewports = [
234 ViewportConfig(
235 Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]),
236 Rect(0, 0, surfaceSize[0], surfaceSize[1])
237 )
238 ];
239 info.blendInfo = ColorBlendInfo(
240 none!LogicOp, [
241 ColorBlendAttachment(No.enabled,
242 BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add),
243 BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add),
244 ColorMask.all
245 )
246 ],
247 [ 0f, 0f, 0f, 0f ]
248 );
249 info.layout = layout;
250 info.renderPass = renderPass;
251 info.subpassIndex = 0;
252
253 auto pls = device.createPipelines( [info] );
254 pipeline = pls[0];
255 }
256
257 void prepareDescriptorSet()
258 {
259 const poolSizes = [
260 DescriptorPoolSize(DescriptorType.uniformBuffer, 2),
261 DescriptorPoolSize(DescriptorType.combinedImageSampler, 1)
262 ];
263 descPool = device.createDescriptorPool(1, poolSizes);
264 set = descPool.allocate([ setLayout.obj ])[0];
265
266 auto writes = [
267 WriteDescriptorSet(set, 0, 0, DescriptorWrite.make(
268 DescriptorType.uniformBuffer,
269 matBuf.descriptor(),
270 )),
271
272 WriteDescriptorSet(set, 1, 0, DescriptorWrite.make(
273 DescriptorType.uniformBuffer,
274 ligBuf.descriptor(),
275 )),
276
277 WriteDescriptorSet(set, 2, 0, DescriptorWrite.make(
278 DescriptorType.combinedImageSampler,
279 texView.descriptorWithSampler(ImageLayout.undefined, texSampler),
280 )),
281 ];
282 device.updateDescriptorSets(writes, []);
283 }
284
285 void updateMatrices(in Matrices mat)
286 {
287 auto mm = matBuf.boundMemory.map();
288 auto v = mm.view!(Matrices[])(0, 1);
289 v[0] = mat;
290 MappedMemorySet mms;
291 mm.addToSet(mms);
292 device.flushMappedMemory(mms);
293 }
294
295 override FrameData makeFrameData(ImageBase swcColor, CommandBuffer tempBuf)
296 {
297 return new TextureFrameData(swcColor, tempBuf);
298 }
299
300 override Submission[] recordCmds(FrameData frameData)
301 {
302 auto tfd = cast(TextureFrameData)frameData;
303
304 const cv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f);
305
306 auto buf = tfd.cmdBuf;
307
308 //buf.reset();
309 buf.begin(CommandBufferUsage.oneTimeSubmit);
310
311 buf.beginRenderPass(
312 renderPass, tfd.framebuffer,
313 Rect(0, 0, surfaceSize[0], surfaceSize[1]), [ ClearValues(cv) ]
314 );
315
316 buf.bindPipeline(pipeline);
317 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, 0) ]);
318 buf.bindIndexBuffer(indBuf, 0, IndexType.u16);
319 buf.bindDescriptorSets(PipelineBindPoint.graphics, layout, 0, [set], []);
320 buf.drawIndexed(cast(uint)indices.length, 1, 0, 0, 0);
321
322 buf.endRenderPass();
323
324 buf.end();
325
326 return simpleSubmission([ buf ]);
327 }
328
329 }
330
331 /// correction matrix for the vulkan coordinate system
332 // (gl3n is made with opengl in mind)
333 mat4 correctionMatrix() pure
334 {
335 return mat4(
336 1f, 0f, 0f, 0f,
337 0f, -1f, 0f, 0f,
338 0f, 0f, 0.5f, 0.5f,
339 0f, 0f, 0f, 1f,
340 );
341 }
342
343 int main(string[] args)
344 {
345 try {
346 auto example = new TextureExample(args);
347 example.prepare();
348 scope(exit) example.dispose();
349
350 example.window.onKeyOn = (KeyEvent ev)
351 {
352 if (ev.sym == KeySym.escape) {
353 example.window.closeFlag = true;
354 }
355 };
356
357 // 6 RPM at 60 FPS
358 const puls = 6 * 2*PI / 3600f;
359 auto angle = 0f;
360 const view = mat4.look_at(vec3(0, -5, 3), vec3(0, 0, 0), vec3(0, 0, 1));
361 const proj = mat4.perspective(640, 480, 45, 1, 10);
362 const viewProj = correctionMatrix() * proj*view;
363
364 while (!example.window.closeFlag) {
365 const model = mat4.rotation(angle, vec3(0, 0, 1));
366 const mvp = viewProj*model;
367 const normals = model.inverse().transposed();
368 angle += puls;
369
370 example.updateMatrices( TextureExample.Matrices(
371 mvp.transposed().matrix,
372 normals.transposed().matrix
373 ) );
374
375 example.render();
376 example.frameTick();
377 example.display.pollAndDispatch();
378 }
379
380 return 0;
381 }
382 catch(Exception ex) {
383 stderr.writeln("error occured: ", ex.msg);
384 return 1;
385 }
386 }