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