1 //------------------------------------------------------------------------------
2 //  vertexpull.d
3 //
4 //  Pull vertices from a storage buffer instead of using fixed-function
5 //  vertex input.
6 //------------------------------------------------------------------------------
7 module examples.vertexpull;
8 
9 private:
10 
11 import sg = sokol.gfx;
12 import app = sokol.app;
13 import log = sokol.log;
14 import handmade.math : Mat4, Vec3;
15 import sglue = sokol.glue;
16 import shd = shaders.vertexpull;
17 
18 extern (C):
19 @safe:
20 
21 struct State
22 {
23     float rx = 0;
24     float ry = 0;
25 
26     sg.Pipeline pip;
27     sg.Bindings bind;
28     sg.PassAction passAction = {};
29 
30     Mat4 view()
31     {
32         return Mat4.lookAt(Vec3(0.0, 1.5, 6.0), Vec3.zero(), Vec3.up());
33     }
34 }
35 
36 static State state;
37 
38 void init()
39 {
40     sg.Desc gfxd = {environment: sglue.environment,
41     logger: {func: &log.func}};
42     sg.setup(gfxd);
43 
44     // if storage buffers are not supported on the current backend, just render a red screen
45     if (!sg.queryFeatures.compute)
46     {
47         state.passAction.colors[0].load_action = sg.LoadAction.Clear;
48         state.passAction.colors[0].clear_value.r = 1.0;
49         state.passAction.colors[0].clear_value.g = 0.0;
50         state.passAction.colors[0].clear_value.b = 0.0;
51         state.passAction.colors[0].clear_value.a = 1.0;
52         return;
53     }
54     // otherwise set regular clear color
55     state.passAction.colors[0].load_action = sg.LoadAction.Clear;
56     state.passAction.colors[0].clear_value.r = 0.75;
57     state.passAction.colors[0].clear_value.g = 0.5;
58     state.passAction.colors[0].clear_value.b = 0.25;
59     state.passAction.colors[0].clear_value.a = 1.0;
60 
61     shd.SbVertex[24] vertices = [
62         {pos: [-1.0, -1.0, -1.0], color: [1.0, 0.0, 0.0, 1.0]},
63         {pos: [1.0, -1.0, -1.0], color: [1.0, 0.0, 0.0, 1.0]},
64         {pos: [1.0, 1.0, -1.0], color: [1.0, 0.0, 0.0, 1.0]},
65         {pos: [-1.0, 1.0, -1.0], color: [1.0, 0.0, 0.0, 1.0]},
66         {pos: [-1.0, -1.0, 1.0], color: [0.0, 1.0, 0.0, 1.0]},
67         {pos: [1.0, -1.0, 1.0], color: [0.0, 1.0, 0.0, 1.0]},
68         {pos: [1.0, 1.0, 1.0], color: [0.0, 1.0, 0.0, 1.0]},
69         {pos: [-1.0, 1.0, 1.0], color: [0.0, 1.0, 0.0, 1.0]},
70         {pos: [-1.0, -1.0, -1.0], color: [0.0, 0.0, 1.0, 1.0]},
71         {pos: [-1.0, 1.0, -1.0], color: [0.0, 0.0, 1.0, 1.0]},
72         {pos: [-1.0, 1.0, 1.0], color: [0.0, 0.0, 1.0, 1.0]},
73         {pos: [-1.0, -1.0, 1.0], color: [0.0, 0.0, 1.0, 1.0]},
74         {pos: [1.0, -1.0, -1.0], color: [1.0, 0.5, 0.0, 1.0]},
75         {pos: [1.0, 1.0, -1.0], color: [1.0, 0.5, 0.0, 1.0]},
76         {pos: [1.0, 1.0, 1.0], color: [1.0, 0.5, 0.0, 1.0]},
77         {pos: [1.0, -1.0, 1.0], color: [1.0, 0.5, 0.0, 1.0]},
78         {pos: [-1.0, -1.0, -1.0], color: [0.0, 0.5, 1.0, 1.0]},
79         {pos: [-1.0, -1.0, 1.0], color: [0.0, 0.5, 1.0, 1.0]},
80         {pos: [1.0, -1.0, 1.0], color: [0.0, 0.5, 1.0, 1.0]},
81         {pos: [1.0, -1.0, -1.0], color: [0.0, 0.5, 1.0, 1.0]},
82         {pos: [-1.0, 1.0, -1.0], color: [1.0, 0.0, 0.5, 1.0]},
83         {pos: [-1.0, 1.0, 1.0], color: [1.0, 0.0, 0.5, 1.0]},
84         {pos: [1.0, 1.0, 1.0], color: [1.0, 0.0, 0.5, 1.0]},
85         {pos: [1.0, 1.0, -1.0], color: [1.0, 0.0, 0.5, 1.0]},
86     ];
87 
88     // dfmt off
89     sg.BufferDesc sbuf_desc = {
90         usage: { storage_buffer: true },
91         data:
92         {
93             ptr: vertices.ptr,
94             size: vertices.sizeof
95         },
96         label: "vertices",
97     };
98     // dfmt on
99     auto sbuf = sg.makeBuffer(sbuf_desc);
100     sg.ViewDesc sbuf_view_desc = {
101         storage_buffer: { buffer: sbuf },
102     };
103     state.bind.views[shd.VIEW_SSBO] = sg.makeView(sbuf_view_desc);
104 
105     ushort[36] indices = [
106         0, 1, 2, 0, 2, 3,
107         6, 5, 4, 7, 6, 4,
108         8, 9, 10, 8, 10, 11,
109         14, 13, 12, 15, 14, 12,
110         16, 17, 18, 16, 18, 19,
111         22, 21, 20, 23, 22, 20,
112     ];
113 
114     // dfmt off
115     sg.BufferDesc ibufd = {
116         usage: { index_buffer: true },
117         data: {ptr: indices.ptr, size: indices.sizeof},
118     };
119     state.bind.index_buffer = sg.makeBuffer(ibufd);
120 
121     sg.PipelineDesc pld = {
122         shader: sg.makeShader(shd.vertexpullShaderDesc(sg.queryBackend())),
123         index_type: sg.IndexType.Uint16,
124         cull_mode: sg.CullMode.Back,
125         depth: {
126             write_enabled: true,
127             compare: sg.CompareFunc.Less_equal
128         },
129         label: "pipeline"
130     };
131     // dfmt on
132     state.pip = sg.makePipeline(pld);
133 }
134 
135 void frame()
136 {
137     immutable float t = cast(float)(app.frameDuration() * 60.0);
138 
139     state.rx += 1.0 * t;
140     state.ry += 2.0 * t;
141 
142     shd.VsParams vsParams = {mvp: computeMvp(state.rx, state.ry)};
143 
144     sg.Pass pass = {action: state.passAction, swapchain: sglue.swapchain()};
145     sg.beginPass(pass);
146     sg.applyPipeline(state.pip);
147     sg.applyBindings(state.bind);
148     sg.Range r = {ptr: &vsParams, size: vsParams.sizeof};
149     sg.applyUniforms(shd.UB_VS_PARAMS, r);
150     sg.draw(0, 36, 1);
151     sg.endPass();
152     sg.commit();
153 }
154 
155 void cleanup()
156 {
157     sg.shutdown();
158 }
159 
160 Mat4 computeMvp(float rx, float ry)
161 {
162     immutable proj = Mat4.perspective(60.0, app.widthf() / app.heightf(), 0.01, 10.0);
163     immutable rxm = Mat4.rotate(rx, Vec3(1.0, 0.0, 0.0));
164     immutable rym = Mat4.rotate(ry, Vec3(0.0, 1.0, 0.0));
165     immutable model = Mat4.mul(rxm, rym);
166     immutable view_proj = Mat4.mul(proj, state.view());
167     return Mat4.mul(view_proj, model);
168 }
169 
170 // dfmt off
171 void main()
172 {
173     app.Desc runner = {
174         window_title: "vertexpull.d",
175         init_cb: &init,
176         frame_cb: &frame,
177         cleanup_cb: &cleanup,
178         width: 800,
179         height: 600,
180         sample_count: 4,
181         icon: {sokol_default: true},
182         logger: {func: &log.func}
183     };
184     app.run(runner);
185 }
186 // dfmt on
187 
188 version (WebAssembly)
189 {
190     debug
191     {
192         import emscripten.assertd;
193     }
194 }