1 //------------------------------------------------------------------------------
2 //  sgl-context.d
3 //
4 //  Demonstrates how to render into different render passes with sokol-gl.
5 //  using contexts.
6 //------------------------------------------------------------------------------
7 module examples.sgl_context;
8 
9 private:
10 
11 import sg = sokol.gfx;
12 import sglue = sokol.glue;
13 import sapp = sokol.app;
14 import slog = sokol.log;
15 import sgl = sokol.gl;
16 import handmade.math : sin, cos;
17 
18 extern (C):
19 @safe:
20 
21 struct Offscreen
22 {
23     sg.Attachments attachments;
24     sg.Image img;
25     sgl.Context sgl_ctx;
26     sg.PassAction pass_action = {
27         colors: [
28             {
29                 load_action: sg.LoadAction.Clear, clear_value: {
30                     r: 0, g: 0, b: 0, a: 1
31                 }
32             },
33         ]
34     };
35 }
36 
37 struct Display
38 {
39     sg.Sampler smp;
40     sgl.Pipeline sgl_pip;
41     sg.PassAction pass_action = {
42         colors: [
43             {
44                 load_action: sg.LoadAction.Clear, clear_value: {
45                     r: 0.5, g: 0.7, b: 1.0, a: 1.0
46                 }
47             },
48         ],
49     };
50 }
51 
52 struct State
53 {
54     Display display;
55     Offscreen offscreen;
56 }
57 
58 static State state;
59 
60 enum offscreen_pixel_format = sg.PixelFormat.Rgba8;
61 enum offscreen_sample_count = 1;
62 enum offscreen_width = 32;
63 enum offscreen_height = 32;
64 
65 void init()
66 {
67     sg.Desc gfxd = {
68         environment: sglue.environment(),
69         logger: {func: &slog.func}
70     };
71     sg.setup(gfxd);
72 
73     // setup sokol-gl with the default context compatible with the default
74     // render pass (which means just keep pixelformats and sample count at defaults)
75     //
76     // reduce the vertex- and command-count though, otherwise we just waste memory
77     sgl.Desc gld = {
78         max_vertices: 64,
79         max_commands: 16,
80         logger: {func: &slog.func},
81     };
82     sgl.setup(gld);
83 
84     // create a sokol-gl pipeline object for 3D rendering into the default pass
85     sg.PipelineDesc pld = {
86         cull_mode: sg.CullMode.Back,
87         depth: {write_enabled: true,
88         compare: sg.CompareFunc.Less_equal},
89     };
90     state.display.sgl_pip = sgl.contextMakePipeline(sgl.defaultContext, pld);
91 
92     // create a sokol-gl context compatible with the offscreen render pass
93     // (specific color pixel format, no depth-stencil-surface, no MSAA)
94     sgl.ContextDesc ctd = {
95         max_vertices: 8,
96         max_commands: 4,
97         color_format: offscreen_pixel_format,
98         depth_format: sg.PixelFormat.None,
99         sample_count: offscreen_sample_count
100     };
101     state.offscreen.sgl_ctx = sgl.makeContext(ctd);
102 
103     // create an offscreen render target texture, pass-attachments object and pass-action
104     sg.ImageDesc imgd = {
105         render_target: true,
106         width: offscreen_width,
107         height: offscreen_height,
108         pixel_format: offscreen_pixel_format,
109         sample_count: offscreen_sample_count
110     };
111     state.offscreen.img = sg.makeImage(imgd);
112 
113     sg.AttachmentsDesc attd = {colors: [
114         {image: state.offscreen.img}
115     ]};
116     state.offscreen.attachments = sg.makeAttachments(attd);
117 
118     // sampler for sampling the offscreen render target
119     sg.SamplerDesc smd = {
120         wrap_u: sg.Wrap.Clamp_to_edge,
121         wrap_v: sg.Wrap.Clamp_to_edge,
122         min_filter: sg.Filter.Nearest,
123         mag_filter: sg.Filter.Nearest
124     };
125     state.display.smp = sg.makeSampler(smd);
126 }
127 
128 void frame()
129 {
130     immutable float a = sgl.asRadians(sapp.frameCount());
131 
132     // draw a rotating quad into the offscreen render target texture
133     sgl.setContext(state.offscreen.sgl_ctx);
134     sgl.defaults();
135     sgl.matrixModeModelview();
136     sgl.rotate(a, 0.0, 0.0, 1.0);
137     draw_quad();
138 
139     // draw a rotating 3D cube, using the offscreen render target as texture
140     sgl.setContext(sgl.defaultContext());
141     sgl.defaults();
142     sgl.enableTexture();
143     sgl.texture(state.offscreen.img, state.display.smp);
144     sgl.loadPipeline(state.display.sgl_pip);
145     sgl.matrixModeProjection();
146     sgl.perspective(sgl.asRadians(45.0), sapp.widthf() / sapp.heightf(), 0.1, 100.0);
147     immutable(float)[3] eye = [sin(a) * 6.0, sin(a) * 3.0, cos(a) * 6.0];
148     sgl.matrixModeModelview();
149     sgl.lookat(eye[0], eye[1], eye[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
150     draw_cube();
151 
152     // do the actual offscreen and display rendering in sokol-gfx passes
153     sg.Pass offscreen_pass = {
154         action: state.offscreen.pass_action,
155         attachments: state.offscreen.attachments
156     };
157     sg.beginPass(offscreen_pass);
158     sgl.contextDraw(state.offscreen.sgl_ctx);
159     sg.endPass();
160     sg.Pass display_pass = {
161         action: state.display.pass_action,
162         swapchain: sglue.swapchain
163     };
164     sg.beginPass(display_pass);
165     sgl.contextDraw(sgl.defaultContext());
166     sg.endPass();
167     sg.commit();
168 }
169 
170 void cleanup()
171 {
172     sgl.shutdown();
173     sg.shutdown();
174 }
175 
176 void main()
177 {
178     sapp.Desc runner = {
179         window_title: "sgl-context.d",
180         init_cb: &init,
181         frame_cb: &frame,
182         cleanup_cb: &cleanup,
183         width: 800,
184         height: 600,
185         sample_count: 4,
186         logger: {func: &slog.func},
187         icon: {sokol_default: true}
188     };
189     sapp.run(runner);
190 }
191 
192 void draw_quad()
193 {
194     sgl.beginQuads();
195     sgl.v2fC3b(0.0, -1.0, 255, 0, 0);
196     sgl.v2fC3b(1.0, 0.0, 0, 0, 255);
197     sgl.v2fC3b(0.0, 1.0, 0, 255, 255);
198     sgl.v2fC3b(-1.0, 0.0, 0, 255, 0);
199     sgl.end();
200 }
201 
202 void draw_cube()
203 {
204     sgl.beginQuads();
205     sgl.v3fT2f(-1.0, 1.0, -1.0, 0.0, 1.0);
206     sgl.v3fT2f(1.0, 1.0, -1.0, 1.0, 1.0);
207     sgl.v3fT2f(1.0, -1.0, -1.0, 1.0, 0.0);
208     sgl.v3fT2f(-1.0, -1.0, -1.0, 0.0, 0.0);
209     sgl.v3fT2f(-1.0, -1.0, 1.0, 0.0, 1.0);
210     sgl.v3fT2f(1.0, -1.0, 1.0, 1.0, 1.0);
211     sgl.v3fT2f(1.0, 1.0, 1.0, 1.0, 0.0);
212     sgl.v3fT2f(-1.0, 1.0, 1.0, 0.0, 0.0);
213     sgl.v3fT2f(-1.0, -1.0, 1.0, 0.0, 1.0);
214     sgl.v3fT2f(-1.0, 1.0, 1.0, 1.0, 1.0);
215     sgl.v3fT2f(-1.0, 1.0, -1.0, 1.0, 0.0);
216     sgl.v3fT2f(-1.0, -1.0, -1.0, 0.0, 0.0);
217     sgl.v3fT2f(1.0, -1.0, 1.0, 0.0, 1.0);
218     sgl.v3fT2f(1.0, -1.0, -1.0, 1.0, 1.0);
219     sgl.v3fT2f(1.0, 1.0, -1.0, 1.0, 0.0);
220     sgl.v3fT2f(1.0, 1.0, 1.0, 0.0, 0.0);
221     sgl.v3fT2f(1.0, -1.0, -1.0, 0.0, 1.0);
222     sgl.v3fT2f(1.0, -1.0, 1.0, 1.0, 1.0);
223     sgl.v3fT2f(-1.0, -1.0, 1.0, 1.0, 0.0);
224     sgl.v3fT2f(-1.0, -1.0, -1.0, 0.0, 0.0);
225     sgl.v3fT2f(-1.0, 1.0, -1.0, 0.0, 1.0);
226     sgl.v3fT2f(-1.0, 1.0, 1.0, 1.0, 1.0);
227     sgl.v3fT2f(1.0, 1.0, 1.0, 1.0, 0.0);
228     sgl.v3fT2f(1.0, 1.0, -1.0, 0.0, 0.0);
229     sgl.end();
230 }