1 //------------------------------------------------------------------------------ 2 // shapes.d 3 // 4 // Simple sokol.shape demo. 5 //------------------------------------------------------------------------------ 6 module examples.shapes; 7 8 private: 9 10 import sg = sokol.gfx; 11 import app = sokol.app; 12 import log = sokol.log; 13 import handmade.math : Mat4, Vec3; 14 import sglue = sokol.glue; 15 import sdtx = sokol.debugtext; 16 import sshape = sokol.shape; 17 import shd = examples.shaders.shapes; 18 19 extern (C): 20 @safe: 21 22 struct State 23 { 24 float rx = 0; 25 float ry = 0; 26 27 sg.Pipeline pip; 28 sg.Bindings bind; 29 sg.PassAction passAction = {}; 30 shd.VsParams vsParams = {}; 31 Shape[5] shapes = [ 32 Shape(Vec3(-1, 1, 0)), 33 Shape(Vec3(1, 1, 0)), 34 Shape(Vec3(-2, -1, 0)), 35 Shape(Vec3(2, -1, 0)), 36 Shape(Vec3(0, -1, 0)), 37 ]; 38 39 Mat4 view() 40 { 41 return Mat4.lookAt(Vec3(0.0, 1.5, 6.0), Vec3.zero(), Vec3.up()); 42 } 43 } 44 45 struct Shape 46 { 47 Vec3 pos = Vec3.zero(); 48 sshape.ElementRange draw = {}; 49 } 50 51 static State state; 52 53 void init() 54 { 55 sg.Desc gfxd = {environment: sglue.environment, 56 logger: {func: &log.func}}; 57 sg.setup(gfxd); 58 59 sdtx.Desc desc = {fonts: [ 60 sdtx.fontOric() 61 ], 62 logger: {func: &log.func},}; 63 sdtx.setup(desc); 64 65 sshape.Vertex[6 * 1024] vertices; 66 ushort[16 * 1024] indices; 67 68 state.passAction.colors[0].load_action = sg.LoadAction.Clear; 69 state.passAction.colors[0].clear_value.r = 0.0; 70 state.passAction.colors[0].clear_value.g = 0.0; 71 state.passAction.colors[0].clear_value.b = 0.0; 72 state.passAction.colors[0].clear_value.a = 1.0; 73 74 // dfmt off 75 sg.PipelineDesc pld = { 76 layout: { 77 buffers: [sshape.vertexBufferLayoutState()], 78 attrs: [ 79 shd.ATTR_SHAPES_POSITION: sshape.positionVertexAttrState, 80 shd.ATTR_SHAPES_NORMAL: sshape.normalVertexAttrState, 81 shd.ATTR_SHAPES_COLOR0: sshape.colorVertexAttrState, 82 shd.ATTR_SHAPES_TEXCOORD: sshape.texcoordVertexAttrState, 83 ], 84 }, 85 shader: sg.makeShader(shd.shapesShaderDesc(sg.queryBackend())), 86 index_type: sg.IndexType.Uint16, 87 cull_mode: sg.CullMode.None, 88 depth: { 89 write_enabled: true, 90 compare: sg.CompareFunc.Less_equal 91 }, 92 }; 93 // dfmt on 94 state.pip = sg.makePipeline(pld); 95 96 // dfmt off 97 sshape.Buffer buf = { 98 vertices: {buffer: {ptr: vertices.ptr, size: vertices.sizeof}}, 99 indices: {buffer: {ptr: indices.ptr, size: indices.sizeof}}, 100 }; 101 buf = sshape.buildBox(buf, sshape.Box( 102 width: 1.0, height: 1.0, depth: 1.0, 103 tiles: 10, random_colors: true, 104 ) 105 ); 106 state.shapes[0].draw = sshape.elementRange(buf); 107 buf = sshape.buildPlane(buf, sshape.Plane( 108 width: 1.0, depth: 1.0, 109 tiles: 10, random_colors: true, 110 ) 111 ); 112 state.shapes[1].draw = sshape.elementRange(buf); 113 buf = sshape.buildSphere(buf, sshape.Sphere( 114 radius: 0.75, slices: 36, 115 stacks: 20, random_colors: true, 116 ) 117 ); 118 state.shapes[2].draw = sshape.elementRange(buf); 119 buf = sshape.buildCylinder(buf, sshape.Cylinder( 120 radius: 0.5, height: 1.5, slices: 36, 121 stacks: 10, random_colors: true, 122 ) 123 ); 124 state.shapes[3].draw = sshape.elementRange(buf); 125 buf = sshape.buildTorus(buf, sshape.Torus( 126 radius: 0.5, ring_radius: 0.3, rings: 36, 127 sides: 18, random_colors: true, 128 ) 129 ); 130 // dfmt on 131 132 state.shapes[4].draw = sshape.elementRange(buf); 133 assert(buf.valid); 134 // one vertex- and index-buffer for all shapes 135 state.bind.vertex_buffers[0] = sg.makeBuffer(sshape.vertexBufferDesc(buf)); 136 state.bind.index_buffer = sg.makeBuffer(sshape.indexBufferDesc(buf)); 137 } 138 139 void frame() 140 { 141 sdtx.canvas(app.widthf() * 0.5, app.heightf() * 0.5); 142 sdtx.pos(0.5, 0.5); 143 sdtx.puts("press key to switch draw mode:\n\n"); 144 sdtx.puts(" 1: vertex normals\n"); 145 sdtx.puts(" 2: texture coords\n"); 146 sdtx.puts(" 3: vertex colors\n"); 147 148 // view-project matrix 149 const proj = Mat4.perspective(60.0, app.widthf / app.heightf, 0.01, 10.0); 150 const view_proj = Mat4.mul(proj, state.view); 151 // model-rotation matrix 152 immutable float dt = cast(float)(app.frameDuration * 60); 153 state.rx += 1.0 * dt; 154 state.ry += 1.0 * dt; 155 const rxm = Mat4.rotate(state.rx, Vec3(1, 0, 0)); 156 const rym = Mat4.rotate(state.ry, Vec3(0, 1, 0)); 157 const rm = Mat4.mul(rxm, rym); 158 159 sg.Pass pass = {action: state.passAction, swapchain: sglue.swapchain()}; 160 sg.beginPass(pass); 161 sg.applyPipeline(state.pip); 162 sg.applyBindings(state.bind); 163 164 sg.Range rg = {ptr: &state.vsParams, size: state.vsParams.sizeof}; 165 // dfmt off 166 foreach (shape;state.shapes) { 167 // per-shape model-view-projection matrix 168 const model = Mat4.mul(Mat4.translate(shape.pos), rm); 169 state.vsParams.mvp = Mat4.mul(view_proj, model); 170 sg.applyUniforms(shd.UB_VS_PARAMS, rg); 171 sg.draw(shape.draw.base_element, shape.draw.num_elements, 1); 172 } 173 // dfmt on 174 sdtx.draw; 175 sg.endPass; 176 sg.commit; 177 } 178 179 void input(const app.Event* event) 180 { 181 const ev = *event; 182 if (ev.type == app.EventType.Key_down) 183 { 184 switch (ev.key_code) 185 { 186 case app.Keycode._1: 187 state.vsParams.draw_mode = 0.0f; 188 break; 189 case app.Keycode._2: 190 state.vsParams.draw_mode = 1.0f; 191 break; 192 case app.Keycode._3: 193 state.vsParams.draw_mode = 2.0f; 194 break; 195 default: 196 break; 197 } 198 } 199 } 200 201 void cleanup() 202 { 203 sdtx.shutdown(); 204 sg.shutdown(); 205 } 206 207 // dfmt off 208 void main() 209 { 210 app.Desc runner = { 211 window_title: "shapes.d", 212 init_cb: &init, 213 frame_cb: &frame, 214 event_cb: &input, 215 cleanup_cb: &cleanup, 216 width: 800, 217 height: 600, 218 sample_count: 4, 219 icon: {sokol_default: true}, 220 logger: {func: &log.func} 221 }; 222 app.run(runner); 223 } 224 // dfmt on