save location & map, lighting qol
[carveJwlIkooP6JGAAIwe30JlM.git] / world_gate.c
1 /*
2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #ifndef WORLD_GATE_C
6 #define WORLD_GATE_C
7
8 #include "world.h"
9 #include "world_gate.h"
10
11 #include "skaterift.h"
12 #include "common.h"
13 #include "model.h"
14 #include "entity.h"
15 #include "render.h"
16 #include "camera.h"
17
18 #include "world_water.h"
19
20 /*
21 * Update the transform matrices for gate
22 */
23 VG_STATIC void gate_transform_update( ent_gate *gate )
24 {
25 m4x3f to_local, recv_to_world;
26
27 q_m3x3( gate->q[0], gate->to_world );
28 v3_copy( gate->co[0], gate->to_world[3] );
29
30 m4x3_invert_affine( gate->to_world, to_local );
31
32 q_m3x3( gate->q[1], recv_to_world );
33 v3_copy( gate->co[1], recv_to_world[3] );
34 m4x3_mul( recv_to_world, to_local, gate->transport );
35
36 m3x3_scale( gate->to_world, (v3f){ gate->dimensions[0],
37 gate->dimensions[1], 1.0f } );
38 }
39
40 VG_STATIC void world_gates_init(void)
41 {
42 vg_info( "world_gates_init\n" );
43
44 shader_model_gate_register();
45
46 vg_linear_clear( vg_mem.scratch );
47
48 mdl_context mgate;
49 mdl_open( &mgate, "models/rs_gate.mdl", vg_mem.scratch );
50 mdl_load_metadata_block( &mgate, vg_mem.scratch );
51
52 mdl_mesh *surface = mdl_find_mesh( &mgate, "rs_gate" );
53 mdl_submesh *sm = mdl_arritm(&mgate.submeshs,surface->submesh_start);
54 world_gates.sm_surface = *sm;
55
56 const char *names[] = { "rs_gate_marker", "rs_gate_marker.001",
57 "rs_gate_marker.002", "rs_gate_marker.003" };
58
59 for( int i=0; i<4; i++ ){
60 mdl_mesh *marker = mdl_find_mesh( &mgate, names[i] );
61 sm = mdl_arritm( &mgate.submeshs, marker->submesh_start );
62 world_gates.sm_marker[i] = *sm;
63 }
64
65 mdl_async_load_glmesh( &mgate, &world_gates.mesh );
66 mdl_close( &mgate );
67 }
68
69 /*
70 * Render the view through a gate
71 */
72 VG_STATIC int render_gate( world_instance *world_inside,
73 ent_gate *gate, camera *cam, int layer_depth )
74 {
75 v3f viewdir, gatedir;
76 m3x3_mulv( cam->transform, (v3f){0.0f,0.0f,-1.0f}, viewdir );
77 q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, gatedir );
78
79 v3f v0;
80 v3_sub( cam->pos, gate->co[0], v0 );
81
82 float dist = v3_dot(v0, gatedir);
83
84 /* Hard cutoff */
85 if( dist > 3.0f )
86 return 0;
87
88 if( v3_dist( cam->pos, gate->co[0] ) > 100.0f )
89 return 0;
90
91 {
92 v3f a,b,c,d;
93
94 m4x3_mulv( gate->to_world, (v3f){-1.0f,-1.0f,0.0f}, a );
95 m4x3_mulv( gate->to_world, (v3f){ 1.0f,-1.0f,0.0f}, b );
96 m4x3_mulv( gate->to_world, (v3f){ 1.0f, 1.0f,0.0f}, c );
97 m4x3_mulv( gate->to_world, (v3f){-1.0f, 1.0f,0.0f}, d );
98
99 vg_line( a,b, 0xffffa000 );
100 vg_line( b,c, 0xffffa000 );
101 vg_line( c,d, 0xffffa000 );
102 vg_line( d,a, 0xffffa000 );
103
104 vg_line2( gate->co[0], gate->co[1], 0xff0000ff, 0x00000000 );
105 }
106
107 /* update gate camera */
108 gate_camera.fov = cam->fov;
109 gate_camera.nearz = 0.1f;
110 gate_camera.farz = 2000.0f;
111
112 m4x3_mul( gate->transport, cam->transform, gate_camera.transform );
113 camera_update_view( &gate_camera );
114 camera_update_projection( &gate_camera );
115
116 /* Add special clipping plane to projection */
117 v4f surface;
118 q_mulv( gate->q[1], (v3f){0.0f,0.0f,-1.0f}, surface );
119 surface[3] = v3_dot( surface, gate->co[1] );
120
121 m4x3_mulp( gate_camera.transform_inverse, surface, surface );
122 surface[3] = -fabsf(surface[3]);
123
124 if( dist < -0.5f )
125 m4x4_clip_projection( gate_camera.mtx.p, surface );
126
127 /* Ready to draw with new camrea */
128 camera_finalize( &gate_camera );
129
130 vg_line_pt3( gate_camera.transform[3], 0.3f, 0xff00ff00 );
131 {
132 shader_model_gate_use();
133 shader_model_gate_uPv( cam->mtx.pv );
134 shader_model_gate_uMdl( gate->to_world );
135 shader_model_gate_uCam( cam->pos );
136 shader_model_gate_uColour( (v4f){0.0f,1.0f,0.0f,0.0f} );
137 shader_model_gate_uTime( vg.time*0.25f );
138 shader_model_gate_uInvRes( (v2f){
139 1.0f / (float)vg.window_x,
140 1.0f / (float)vg.window_y });
141
142 glEnable( GL_STENCIL_TEST );
143 glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
144 glStencilFunc( GL_ALWAYS, 1, 0xFF );
145 glStencilMask( 0xFF );
146
147 mesh_bind( &world_gates.mesh );
148 mdl_draw_submesh( &world_gates.sm_surface );
149
150 glClear( GL_DEPTH_BUFFER_BIT );
151 glStencilFunc( GL_EQUAL, 1, 0xFF );
152 glStencilMask( 0x00 );
153 }
154
155 render_world( world_inside, &gate_camera, layer_depth );
156
157 {
158 glDisable( GL_STENCIL_TEST );
159
160 render_water_texture( world_inside, &gate_camera, layer_depth );
161 render_fb_bind( gpipeline.fb_main, 1 );
162
163 glEnable( GL_STENCIL_TEST );
164
165 render_water_surface( world_inside, &gate_camera );
166
167 glStencilMask( 0xFF );
168 glStencilFunc( GL_ALWAYS, 1, 0xFF );
169 glDisable( GL_STENCIL_TEST );
170 }
171
172 return 1;
173 }
174
175 /*
176 * Intersect the plane of a gate with a line segment, plane coordinate result
177 * stored in 'where'
178 */
179 VG_STATIC int gate_intersect_plane( ent_gate *gate,
180 v3f pos, v3f last, v2f where )
181 {
182 v4f surface;
183 q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, surface );
184 surface[3] = v3_dot( surface, gate->co[0] );
185
186 v3f v0, c, delta, p0;
187 v3_sub( pos, last, v0 );
188 float l = v3_length( v0 );
189
190 if( l == 0.0f )
191 return 0;
192
193 v3_divs( v0, l, v0 );
194
195 v3_muls( surface, surface[3], c );
196 v3_sub( c, last, delta );
197
198 float d = v3_dot( surface, v0 );
199
200 if( d > 0.00001f ){
201 float t = v3_dot(delta, surface) / d;
202 if( t >= 0.0f && t <= l ){
203 v3f local, rel;
204 v3_muladds( last, v0, t, local );
205 v3_sub( gate->co[0], local, rel );
206
207 where[0] = v3_dot( rel, gate->to_world[0] );
208 where[1] = v3_dot( rel, gate->to_world[1] );
209
210 where[0] /= v3_dot( gate->to_world[0], gate->to_world[0] );
211 where[1] /= v3_dot( gate->to_world[1], gate->to_world[1] );
212
213 return 1;
214 }
215 }
216
217 return 0;
218 }
219
220 /*
221 * Intersect specific gate
222 */
223 VG_STATIC int gate_intersect( ent_gate *gate, v3f pos, v3f last )
224 {
225 v2f xy;
226
227 if( gate_intersect_plane( gate, pos, last, xy ) ){
228 if( fabsf(xy[0]) <= 1.0f && fabsf(xy[1]) <= 1.0f ){
229 return 1;
230 }
231 }
232
233 return 0;
234 }
235
236 /*
237 * Intersect all gates in the world
238 */
239 VG_STATIC ent_gate *world_intersect_gates( world_instance *world,
240 v3f pos, v3f last )
241 {
242 for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
243 ent_gate *gate = mdl_arritm( &world->ent_gate, i );
244 if( gate->type == k_gate_type_unlinked ||
245 gate->type == k_gate_type_nonlocal_unlinked )
246 continue;
247
248 if( gate->type == k_gate_type_nonlocel ){
249 if( world_loader.state != k_world_loader_none ){
250 continue;
251 }
252 }
253
254 if( gate_intersect( gate, pos, last ) ){
255 return gate;
256 }
257 }
258
259 return NULL;
260 }
261
262 /*
263 * detatches any nonlocal gates
264 */
265 VG_STATIC void world_unlink_nonlocal( world_instance *world )
266 {
267 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
268 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
269
270 if( gate->type == k_gate_type_nonlocel ){
271 gate->type = k_gate_type_nonlocal_unlinked;
272 }
273 }
274 }
275
276 /*
277 * attatches nonlocal gates, to be called from main thread ONLY!
278 */
279 VG_STATIC void world_link_nonlocal_async( void *payload, u32 size )
280 {
281 world_instance *world = payload;
282 u32 world_id = world - world_static.worlds;
283
284 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
285 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
286
287 if( gate->type == k_gate_type_nonlocal_unlinked ){
288 const char *key = mdl_pstr( &world->meta, gate->key );
289 vg_info( "key: %s\n", key );
290
291 for( u32 i=0; i<vg_list_size(world_static.worlds); i++ ){
292 world_instance *other = &world_static.worlds[i];
293 if( other == world ) continue;
294 if( other->status != k_world_status_loaded ) continue;
295 vg_info( "Checking world %u for key matches\n", i );
296
297 for( u32 j=0; j<mdl_arrcount( &other->ent_gate ); j++ ){
298 ent_gate *gate2 = mdl_arritm( &other->ent_gate, j );
299 if( gate2->type != k_gate_type_nonlocal_unlinked ) continue;
300
301 const char *key2 = mdl_pstr( &other->meta, gate2->key );
302 vg_info( " key2: %s\n", key2 );
303
304 if( strcmp( key, key2 ) ) continue;
305
306 vg_success( "Non-local matching pair '%s' found. (%u:%u)\n",
307 key, world_id, i );
308
309 gate->type = k_gate_type_nonlocel;
310 gate2->type = k_gate_type_nonlocel;
311 gate->target = i;
312 gate2->target = world_id;
313
314 v3_copy( gate->co[0], gate2->co[1] );
315 v3_copy( gate2->co[0], gate->co[1] );
316 v4_copy( gate->q[0], gate2->q[1] );
317 v4_copy( gate2->q[0], gate->q[1] );
318
319 v4f qflip;
320 q_axis_angle( qflip, (v3f){0.0f,1.0f,0.0f}, VG_PIf );
321 q_mul( gate->q[0], qflip, gate->q[0] );
322 q_mul( gate->q[1], qflip, gate->q[1] );
323
324 gate_transform_update( gate );
325 gate_transform_update( gate2 );
326
327 goto matched;
328 }
329 }
330 matched:;
331 }
332 }
333 }
334
335 #endif /* WORLD_GATE_C */