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 }