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 = examples.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.storage_buffer)
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 vbufd = {
90         type: sg.BufferType.Storagebuffer,
91         data:
92         {
93             ptr: vertices.ptr,
94             size: vertices.sizeof
95         },
96         label: "vertices",
97     };
98     // dfmt on
99 
100     state.bind.storage_buffers[shd.SBUF_SSBO] = sg.makeBuffer(vbufd);
101 
102     ushort[36] indices = [
103         0, 1, 2, 0, 2, 3,
104         6, 5, 4, 7, 6, 4,
105         8, 9, 10, 8, 10, 11,
106         14, 13, 12, 15, 14, 12,
107         16, 17, 18, 16, 18, 19,
108         22, 21, 20, 23, 22, 20,
109     ];
110 
111     // dfmt off
112     sg.BufferDesc ibufd = {
113         type: sg.BufferType.Indexbuffer,
114         data: {ptr: indices.ptr, size: indices.sizeof},
115     };
116     state.bind.index_buffer = sg.makeBuffer(ibufd);
117 
118     sg.PipelineDesc pld = {
119         shader: sg.makeShader(shd.vertexpullShaderDesc(sg.queryBackend())),
120         index_type: sg.IndexType.Uint16,
121         cull_mode: sg.CullMode.Back,
122         depth: {
123             write_enabled: true,
124             compare: sg.CompareFunc.Less_equal
125         },
126         label: "pipeline"
127     };
128     // dfmt on
129     state.pip = sg.makePipeline(pld);
130 }
131 
132 void frame()
133 {
134     immutable float t = cast(float)(app.frameDuration() * 60.0);
135 
136     state.rx += 1.0 * t;
137     state.ry += 2.0 * t;
138 
139     shd.VsParams vsParams = {mvp: computeMvp(state.rx, state.ry)};
140 
141     sg.Pass pass = {action: state.passAction, swapchain: sglue.swapchain()};
142     sg.beginPass(pass);
143     sg.applyPipeline(state.pip);
144     sg.applyBindings(state.bind);
145     sg.Range r = {ptr: &vsParams, size: vsParams.sizeof};
146     sg.applyUniforms(shd.UB_VS_PARAMS, r);
147     sg.draw(0, 36, 1);
148     sg.endPass();
149     sg.commit();
150 }
151 
152 void cleanup()
153 {
154     sg.shutdown();
155 }
156 
157 Mat4 computeMvp(float rx, float ry)
158 {
159     immutable proj = Mat4.perspective(60.0, app.widthf() / app.heightf(), 0.01, 10.0);
160     immutable rxm = Mat4.rotate(rx, Vec3(1.0, 0.0, 0.0));
161     immutable rym = Mat4.rotate(ry, Vec3(0.0, 1.0, 0.0));
162     immutable model = Mat4.mul(rxm, rym);
163     immutable view_proj = Mat4.mul(proj, state.view());
164     return Mat4.mul(view_proj, model);
165 }
166 
167 // dfmt off
168 void main()
169 {
170     app.Desc runner = {
171         window_title: "vertexpull.d",
172         init_cb: &init,
173         frame_cb: &frame,
174         cleanup_cb: &cleanup,
175         width: 800,
176         height: 600,
177         sample_count: 4,
178         icon: {sokol_default: true},
179         logger: {func: &log.func}
180     };
181     app.run(runner);
182 }
183 // dfmt on