fixed mouse and keyboard stuff under DOS
[retroray] / src / scr_mod.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <assert.h>
19 #include "gaw/gaw.h"
20 #include "app.h"
21 #include "rtk.h"
22 #include "scene.h"
23 #include "rt.h"
24 #include "cmesh.h"
25 #include "meshgen.h"
26
27 enum {
28         TBN_NEW, TBN_OPEN, TBN_SAVE, TBN_SEP1,
29         TBN_SEL, TBN_MOVE, TBN_ROT, TBN_SCALE, TBN_SEP2,
30         TBN_ADD, TBN_RM, TBN_SEP3,
31         TBN_UNION, TBN_ISECT, TBN_DIFF, TBN_SEP4,
32         TBN_MTL, TBN_REND, TBN_VIEWREND, TBN_SEP5, TBN_CFG,
33
34         NUM_TOOL_BUTTONS
35 };
36 static const char *tbn_icon_name[] = {
37         "new", "open", "save", 0,
38         "sel", "move", "rot", "scale", 0,
39         "add", "remove", 0,
40         "union", "isect", "diff", 0,
41         "mtl", "rend", "viewrend", 0, "cfg"
42 };
43 static int tbn_icon_pos[][2] = {
44         {0,0}, {16,0}, {32,0}, {-1,-1},
45         {48,0}, {64,0}, {80,0}, {96,0}, {-1,-1},
46         {112,0}, {112,16}, {-1,-1},
47         {0,16}, {16,16}, {32,16}, {-1,-1},
48         {48,16}, {64,16}, {80,16}, {-1,-1}, {96,16}
49 };
50 static int tbn_istool[] = {
51         0, 0, 0, 0,
52         1, 1, 1, 1, 0,
53         0, 0, 0,
54         1, 1, 1, 0,
55         0, 0, 0, 0, 0
56 };
57 static rtk_icon *tbn_icons[NUM_TOOL_BUTTONS];
58 static rtk_widget *tbn_buttons[NUM_TOOL_BUTTONS];
59
60 #define TOOLBAR_HEIGHT  26
61
62 enum {
63         TOOL_SEL, TOOL_MOVE, TOOL_ROT, TOOL_SCALE,
64         TOOL_UNION, TOOL_ISECT, TOOL_DIFF,
65         NUM_TOOLS
66 };
67 static rtk_widget *tools[NUM_TOOLS];
68
69
70 static int mdl_init(void);
71 static void mdl_destroy(void);
72 static int mdl_start(void);
73 static void mdl_stop(void);
74 static void mdl_display(void);
75 static void mdl_reshape(int x, int y);
76 static void mdl_keyb(int key, int press);
77 static void mdl_mouse(int bn, int press, int x, int y);
78 static void mdl_motion(int x, int y);
79
80 static void draw_object(struct object *obj);
81 static void draw_grid(void);
82 static void tbn_callback(rtk_widget *w, void *cls);
83
84 static void act_settool(int tidx);
85 static void act_addobj(void);
86 static void act_rmobj(void);
87
88 static void draw_rband(void);
89 static void primray(cgm_ray *ray, int x, int y);
90 static void moveobj(struct object *obj, int px0, int py0, int px1, int py1);
91
92
93 struct app_screen scr_model = {
94         "modeller",
95         mdl_init, mdl_destroy,
96         mdl_start, mdl_stop,
97         mdl_display, mdl_reshape,
98         mdl_keyb, mdl_mouse, mdl_motion
99 };
100
101 static rtk_widget *toolbar;
102 static rtk_iconsheet *icons;
103
104 static struct cmesh *mesh_sph;
105
106 static float cam_theta, cam_phi = 20, cam_dist = 8;
107 static float view_matrix[16], proj_matrix[16];
108 static float view_matrix_inv[16], proj_matrix_inv[16];
109 static int viewport[4];
110 static cgm_ray pickray;
111
112 static int cur_tool;
113 static int selobj = -1;
114
115 static rtk_rect rband;
116 static int rband_valid;
117
118
119 static int mdl_init(void)
120 {
121         int i, toolidx;
122         rtk_widget *w;
123
124         if(!(icons = rtk_load_iconsheet("data/icons.png"))) {
125                 errormsg("failed to load iconsheet\n");
126                 return -1;
127         }
128         for(i=0; i<NUM_TOOL_BUTTONS; i++) {
129                 if(tbn_icon_name[i]) {
130                         tbn_icons[i] = rtk_define_icon(icons, tbn_icon_name[i],
131                                         tbn_icon_pos[i][0], tbn_icon_pos[i][1], 16, 16);
132                 } else {
133                         tbn_icons[i] = 0;
134                 }
135         }
136
137         if(!(toolbar = rtk_create_window(0, "toolbar", 0, 0, win_width, TOOLBAR_HEIGHT))) {
138                 return -1;
139         }
140         rtk_win_layout(toolbar, RTK_HBOX);
141
142         toolidx = 0;
143         for(i=0; i<NUM_TOOL_BUTTONS; i++) {
144                 if(!tbn_icons[i]) {
145                         rtk_create_separator(toolbar);
146                 } else {
147                         if(!(w = rtk_create_iconbutton(toolbar, tbn_icons[i], 0))) {
148                                 return -1;
149                         }
150                         tbn_buttons[i] = w;
151                         rtk_set_callback(w, tbn_callback, (void*)(intptr_t)i);
152                         if(tbn_istool[i]) {
153                                 rtk_bn_mode(w, RTK_TOGGLEBN);
154                                 tools[toolidx++] = w;
155                         }
156                         if(i == TBN_SEL) {
157                                 rtk_set_value(w, 1);
158                         }
159                 }
160         }
161         assert(toolidx == NUM_TOOLS);
162
163         if(!(mesh_sph = cmesh_alloc())) {
164                 errormsg("failed to allocate sphere vis mesh\n");
165                 return -1;
166         }
167         gen_sphere(mesh_sph, 1.0f, 16, 8, 1.0f, 1.0f);
168         return 0;
169 }
170
171 static void mdl_destroy(void)
172 {
173         cmesh_free(mesh_sph);
174         rtk_free_iconsheet(icons);
175 }
176
177 static int mdl_start(void)
178 {
179         gaw_clear_color(0.125, 0.125, 0.125, 1);
180
181         gaw_enable(GAW_DEPTH_TEST);
182         gaw_enable(GAW_CULL_FACE);
183         gaw_enable(GAW_LIGHTING);
184         gaw_enable(GAW_LIGHT0);
185         return 0;
186 }
187
188 static void mdl_stop(void)
189 {
190 }
191
192 static void mdl_display(void)
193 {
194         int i, num;
195
196         gaw_clear(GAW_COLORBUF | GAW_DEPTHBUF);
197
198         rtk_draw_widget(toolbar);
199
200         gaw_matrix_mode(GAW_MODELVIEW);
201         gaw_load_identity();
202         gaw_translate(0, 0, -cam_dist);
203         gaw_rotate(cam_phi, 1, 0, 0);
204         gaw_rotate(cam_theta, 0, 1, 0);
205         gaw_get_modelview(view_matrix);
206         cgm_mcopy(view_matrix_inv, view_matrix);
207         cgm_minverse(view_matrix_inv);
208
209         draw_grid();
210
211         gaw_mtl_diffuse(0.5, 0.5, 0.5, 1);
212
213         num = scn_num_objects(scn);
214         for(i=0; i<num; i++) {
215                 if(i == selobj) {
216                         gaw_zoffset(1);
217                         gaw_enable(GAW_POLYGON_OFFSET);
218                         draw_object(scn->objects[i]);
219                         gaw_disable(GAW_POLYGON_OFFSET);
220
221                         gaw_save();
222                         gaw_disable(GAW_LIGHTING);
223                         gaw_poly_wire();
224                         gaw_color3f(0, 1, 0);
225                         draw_object(scn->objects[i]);
226                         gaw_poly_gouraud();
227                         gaw_restore();
228                 } else {
229                         draw_object(scn->objects[i]);
230                 }
231         }
232
233         if(rband_valid) {
234                 draw_rband();
235         }
236 }
237
238 static void draw_object(struct object *obj)
239 {
240         struct sphere *sph;
241
242         if(!obj->xform_valid) {
243                 calc_object_matrix(obj);
244         }
245         gaw_push_matrix();
246         gaw_mult_matrix(obj->xform);
247
248         switch(obj->type) {
249         case OBJ_SPHERE:
250                 sph = (struct sphere*)obj;
251                 gaw_scale(sph->rad, sph->rad, sph->rad);
252                 cmesh_draw(mesh_sph);
253                 break;
254
255         default:
256                 break;
257         }
258
259         gaw_pop_matrix();
260 }
261
262 static void draw_grid(void)
263 {
264         gaw_save();
265         gaw_disable(GAW_LIGHTING);
266
267         gaw_begin(GAW_LINES);
268         gaw_color3f(0.5, 0, 0);
269         gaw_vertex3f(0, 0, 0);
270         gaw_vertex3f(-100, 0, 0);
271         gaw_vertex3f(0, 0, 0);
272         gaw_vertex3f(100, 0, 0);
273         gaw_color3f(0, 0.5, 0);
274         gaw_vertex3f(0, 0, 0);
275         gaw_vertex3f(0, 0, -100);
276         gaw_vertex3f(0, 0, 0);
277         gaw_vertex3f(0, 0, 100);
278         gaw_end();
279
280         gaw_restore();
281 }
282
283 static void mdl_reshape(int x, int y)
284 {
285         float aspect = (float)x / (float)(y - TOOLBAR_HEIGHT);
286
287         viewport[0] = 0;
288         viewport[1] = TOOLBAR_HEIGHT;
289         viewport[2] = x;
290         viewport[3] = y - TOOLBAR_HEIGHT;
291         gaw_viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
292
293         gaw_matrix_mode(GAW_PROJECTION);
294         gaw_load_identity();
295         gaw_perspective(50, aspect, 0.5, 100.0);
296         gaw_get_projection(proj_matrix);
297         cgm_mcopy(proj_matrix_inv, proj_matrix);
298         cgm_minverse(proj_matrix_inv);
299
300         rtk_resize(toolbar, win_width, TOOLBAR_HEIGHT);
301 }
302
303 static void mdl_keyb(int key, int press)
304 {
305         if(rtk_input_key(toolbar, key, press)) {
306                 app_redisplay();
307                 return;
308         }
309
310         if(press) {
311                 switch(key) {
312                 case 27:
313                         act_settool(TOOL_SEL);
314                         break;
315                 case 'g':
316                         act_settool(TOOL_MOVE);
317                         break;
318                 case 'r':
319                         act_settool(TOOL_ROT);
320                         break;
321                 case 's':
322                         act_settool(TOOL_SCALE);
323                         break;
324
325                 case KEY_DEL:
326                         act_rmobj();
327                         break;
328
329                 default:
330                         break;
331                 }
332         }
333 }
334
335 static int vpdrag;
336
337 static void mdl_mouse(int bn, int press, int x, int y)
338 {
339         struct rayhit hit;
340         if(!vpdrag && rtk_input_mbutton(toolbar, bn, press, x, y)) {
341                 app_redisplay();
342                 return;
343         }
344
345         if(press) {
346                 rband.x = x;
347                 rband.y = y;
348                 vpdrag |= (1 << bn);
349         } else {
350                 vpdrag &= ~(1 << bn);
351
352                 if(rband_valid) {
353                         printf("rubber band: %d,%d %dx%d\n", rband.x, rband.y, rband.width, rband.height);
354                         rband_valid = 0;
355
356                 } else if(bn == 0 && x == rband.x && y == rband.y) {
357                         primray(&pickray, x, y);
358                         if(scn_intersect(scn, &pickray, &hit)) {
359                                 selobj = scn_object_index(scn, hit.obj);
360                         } else {
361                                 selobj = -1;
362                         }
363                 }
364                 app_redisplay();
365         }
366 }
367
368 static void mdl_motion(int x, int y)
369 {
370         int dx, dy;
371
372         if(!vpdrag && rtk_input_mmotion(toolbar, x, y)) {
373                 app_redisplay();
374                 return;
375         }
376
377         dx = x - mouse_x;
378         dy = y - mouse_y;
379
380         if(modkeys) {
381                 /* navigation */
382                 if(mouse_state[0]) {
383                         cam_theta += dx * 0.5f;
384                         cam_phi += dy * 0.5f;
385                         if(cam_phi < -90) cam_phi = -90;
386                         if(cam_phi > 90) cam_phi = 90;
387                         app_redisplay();
388                 }
389
390                 if(mouse_state[2]) {
391                         cam_dist += dy * 0.1f;
392                         if(cam_dist < 0) cam_dist = 0;
393                         app_redisplay();
394                 }
395         } else {
396                 if(mouse_state[0]) {
397                         switch(cur_tool) {
398                         case TOOL_SEL:
399                                 if(rband.x != x || rband.y != y) {
400                                         rband.width = x - rband.x;
401                                         rband.height = y - rband.y;
402                                         rband_valid = 1;
403                                 }
404                                 break;
405
406                         case TOOL_MOVE:
407                                 if(selobj >= 0) {
408                                         struct object *obj = scn->objects[selobj];
409                                         moveobj(obj, mouse_x, mouse_y, x, y);
410                                 }
411                                 break;
412
413                         default:
414                                 break;
415                         }
416                 }
417                 app_redisplay();
418         }
419 }
420
421 static void add_sphere(void)
422 {
423         struct object *obj;
424
425         if(!(obj = create_object(OBJ_SPHERE))) {
426                 return;
427         }
428         scn_add_object(scn, obj);
429 }
430
431 static void tbn_callback(rtk_widget *w, void *cls)
432 {
433         int id = (intptr_t)cls;
434         int idx;
435
436         switch(id) {
437         case TBN_SEL:
438         case TBN_MOVE:
439         case TBN_ROT:
440         case TBN_SCALE:
441                 idx = id - TBN_SEL;
442                 if(0) {
443         case TBN_UNION:
444         case TBN_ISECT:
445         case TBN_DIFF:
446                         idx = id - TBN_UNION + TOOL_UNION;
447                 }
448                 act_settool(idx);
449                 break;
450
451         case TBN_ADD:
452                 act_addobj();
453                 break;
454
455         case TBN_RM:
456                 act_rmobj();
457                 break;
458
459         default:
460                 break;
461         }
462 }
463
464 static void act_settool(int tidx)
465 {
466         int i;
467         cur_tool = tidx;
468         for(i=0; i<NUM_TOOLS; i++) {
469                 if(i == cur_tool) {
470                         rtk_set_value(tools[i], 1);
471                 } else {
472                         rtk_set_value(tools[i], 0);
473                 }
474         }
475         app_redisplay();
476 }
477
478 static void act_addobj(void)
479 {
480         int idx = scn_num_objects(scn);
481         add_sphere();
482         selobj = idx;
483
484         app_redisplay();
485 }
486
487 static void act_rmobj(void)
488 {
489         if(selobj >= 0) {
490                 scn_rm_object(scn, selobj);
491                 selobj = -1;
492                 app_redisplay();
493         }
494 }
495
496 static void draw_rband(void)
497 {
498         int i, x, y, w, h;
499         uint32_t *fbptr, *bptr;
500
501         x = rband.x;
502         y = rband.y;
503
504         if(rband.width < 0) {
505                 w = -rband.width;
506                 x += rband.width;
507         } else {
508                 w = rband.width;
509         }
510         if(rband.height < 0) {
511                 h = -rband.height;
512                 y += rband.height;
513         } else {
514                 h = rband.height;
515         }
516
517         fbptr = framebuf + y * win_width + x;
518         bptr = fbptr + win_width * (h - 1);
519
520         for(i=0; i<w; i++) {
521                 fbptr[i] ^= 0xffffff;
522                 bptr[i] ^= 0xffffff;
523         }
524         fbptr += win_width;
525         for(i=0; i<h-2; i++) {
526                 fbptr[0] ^= 0xffffff;
527                 fbptr[w - 1] ^= 0xffffff;
528                 fbptr += win_width;
529         }
530 }
531
532 static void primray(cgm_ray *ray, int x, int y)
533 {
534         float nx, ny;
535         cgm_vec3 npos, farpt;
536         float inv_pv[16];
537
538         y = win_height - y;
539         nx = (float)(x - viewport[0]) / (float)viewport[2];
540         ny = (float)(y - viewport[1]) / (float)viewport[3];
541
542         cgm_mcopy(inv_pv, proj_matrix_inv);
543         cgm_mmul(inv_pv, view_matrix_inv);
544
545         cgm_vcons(&npos, nx, ny, 0.0f);
546         cgm_unproject(&ray->origin, &npos, inv_pv);
547         npos.z = 1.0f;
548         cgm_unproject(&farpt, &npos, inv_pv);
549
550         ray->dir.x = farpt.x - ray->origin.x;
551         ray->dir.y = farpt.y - ray->origin.y;
552         ray->dir.z = farpt.z - ray->origin.z;
553 }
554
555 static void moveobj(struct object *obj, int px0, int py0, int px1, int py1)
556 {
557         cgm_ray ray;
558         float dist;
559         cgm_vec3 p0, p1;
560
561         primray(&ray, px0, py0);
562         cgm_vnormalize(&ray.dir);
563         dist = ray_object_dist(&ray, obj);
564         cgm_raypos(&p0, &ray, dist);
565         primray(&ray, px1, py1);
566         cgm_vnormalize(&ray.dir);
567         cgm_raypos(&p1, &ray, dist);
568
569         cgm_vsub(&p1, &p0);
570         cgm_vadd(&obj->pos, &p1);
571         obj->xform_valid = 0;
572 }