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