1 //------------------------------------------------------------------------------
2 //  noninterleaved.d
3 //
4 //  How to use non-interleaved vertex data (vertex components in
5 //  separate non-interleaved chunks in the same vertex buffers). Note
6 //  that only 4 separate chunks are currently possible because there
7 //  are 4 vertex buffer bind slots in sg_bindings, but you can keep
8 //  several related vertex components interleaved in the same chunk.
9 //------------------------------------------------------------------------------
10 module examples.noninterleaved;
11 
12 private:
13 
14 import sg = sokol.gfx;
15 import app = sokol.app;
16 import log = sokol.log;
17 import handmade.math : Mat4, Vec3;
18 import sglue = sokol.glue;
19 import shd = examples.shaders.noninterleaved;
20 
21 extern (C):
22 @safe:
23 
24 struct State
25 {
26     float rx = 0;
27     float ry = 0;
28 
29     sg.Pipeline pip;
30     sg.Bindings bind;
31     sg.PassAction passAction = {};
32 
33     Mat4 view()
34     {
35         return Mat4.lookAt(Vec3(0.0, 1.5, 6.0), Vec3.zero(), Vec3.up());
36     }
37 }
38 
39 static State state;
40 
41 void init()
42 {
43     sg.Desc gfxd = {environment: sglue.environment,
44     logger: {func: &log.func}};
45     sg.setup(gfxd);
46 
47     float[168] vertices = [
48         // positions
49         -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0,
50         -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
51         -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0,
52         1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
53         -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0,
54         -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
55         // colors
56         1.0, 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0,
57         1.0, 0.5, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0,
58         0.5, 1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 1.0,
59         0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0,
60         1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0,
61         1.0, 0.5, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0,
62         0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 1.0,
63         1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0,
64     ];
65 
66     // dfmt off
67     sg.BufferDesc vbufd = {
68         data:
69         {
70             ptr: vertices.ptr,
71             size: vertices.sizeof
72         },
73         label: "vertices",
74     };
75     // dfmt on
76 
77     ushort[36] indices = [
78         0, 1, 2, 0, 2, 3,
79         6, 5, 4, 7, 6, 4,
80         8, 9, 10, 8, 10, 11,
81         14, 13, 12, 15, 14, 12,
82         16, 17, 18, 16, 18, 19,
83         22, 21, 20, 23, 22, 20,
84     ];
85 
86     // dfmt off
87     sg.BufferDesc ibufd = {
88         type: sg.BufferType.Indexbuffer,
89         data: {ptr: indices.ptr, size: indices.sizeof},
90     };
91 
92     sg.PipelineDesc pld = {
93         layout: {
94             attrs: [
95                 shd.ATTR_NONINTERLEAVED_POSITION: {
96                     format: sg.VertexFormat.Float3,
97                     buffer_index: 0
98                 },
99                 shd.ATTR_NONINTERLEAVED_COLOR0: {
100                     format: sg.VertexFormat.Float4,
101                     buffer_index: 1
102                 },
103             ],
104         },
105         shader: sg.makeShader(shd.noninterleavedShaderDesc(sg.queryBackend())),
106         index_type: sg.IndexType.Uint16,
107         cull_mode: sg.CullMode.Back,
108         depth: {
109             write_enabled: true,
110             compare: sg.CompareFunc.Less_equal
111         },
112         label: "pipeline"
113     };
114     // dfmt on
115     state.pip = sg.makePipeline(pld);
116 
117     // fill the resource bindings, note how the same vertex
118     // buffer is bound to the first two slots, and the vertex-buffer-offsets
119     // are used to point to the position- and color-components.
120     state.bind.vertex_buffers[0] = sg.makeBuffer(vbufd);
121     state.bind.vertex_buffers[1] = sg.makeBuffer(vbufd);
122     // position vertex components are at the start of the buffer
123     state.bind.vertex_buffer_offsets[0] = 0;
124     // color vertex components follow after the positions
125     state.bind.vertex_buffer_offsets[1] = 24 * 3 * float.sizeof;
126     state.bind.index_buffer = sg.makeBuffer(ibufd);
127 }
128 
129 void frame()
130 {
131     immutable float t = cast(float)(app.frameDuration() * 60.0);
132 
133     state.rx += 1.0 * t;
134     state.ry += 2.0 * t;
135 
136     shd.VsParams vsParams = {mvp: computeMvp(state.rx, state.ry)};
137 
138     sg.Pass pass = {action: state.passAction, swapchain: sglue.swapchain()};
139     sg.beginPass(pass);
140     sg.applyPipeline(state.pip);
141     sg.applyBindings(state.bind);
142     sg.Range r = {ptr: &vsParams, size: vsParams.sizeof};
143     sg.applyUniforms(shd.UB_VS_PARAMS, r);
144     sg.draw(0, 36, 1);
145     sg.endPass();
146     sg.commit();
147 }
148 
149 void cleanup()
150 {
151     sg.shutdown();
152 }
153 
154 Mat4 computeMvp(float rx, float ry)
155 {
156     immutable proj = Mat4.perspective(60.0, app.widthf() / app.heightf(), 0.01, 10.0);
157     immutable rxm = Mat4.rotate(rx, Vec3(1.0, 0.0, 0.0));
158     immutable rym = Mat4.rotate(ry, Vec3(0.0, 1.0, 0.0));
159     immutable model = Mat4.mul(rxm, rym);
160     immutable view_proj = Mat4.mul(proj, state.view());
161     return Mat4.mul(view_proj, model);
162 }
163 
164 // dfmt off
165 void main()
166 {
167     app.Desc runner = {
168         window_title: "noninterleaved.d",
169         init_cb: &init,
170         frame_cb: &frame,
171         cleanup_cb: &cleanup,
172         width: 800,
173         height: 600,
174         sample_count: 4,
175         icon: {sokol_default: true},
176         logger: {func: &log.func}
177     };
178     app.run(runner);
179 }
180 // dfmt on