2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
9 #include "world_gate.h"
11 #include "skaterift.h"
17 #include "world_water.h"
18 #include "player_remote.h"
19 #include "shaders/model_gate_unlinked.h"
22 struct world_gates world_gates
;
25 * Update the transform matrices for gate
27 void gate_transform_update( ent_gate
*gate
)
29 if( gate
->flags
& k_ent_gate_flip
)
32 q_axis_angle( qflip
, (v3f
){0.0f
,1.0f
,0.0f
}, VG_PIf
);
33 q_mul( gate
->q
[1], qflip
, gate
->q
[1] );
34 q_normalize( gate
->q
[1] );
37 m4x3f to_local
, recv_to_world
;
39 q_m3x3( gate
->q
[0], gate
->to_world
);
40 v3_copy( gate
->co
[0], gate
->to_world
[3] );
41 m4x3_invert_affine( gate
->to_world
, to_local
);
43 q_m3x3( gate
->q
[1], recv_to_world
);
44 v3_copy( gate
->co
[1], recv_to_world
[3] );
46 m4x3_mul( recv_to_world
, to_local
, gate
->transport
);
49 void world_gates_init(void)
51 vg_info( "world_gates_init\n" );
52 vg_linear_clear( vg_mem
.scratch
);
55 mdl_open( &mgate
, "models/rs_gate.mdl", vg_mem
.scratch
);
56 mdl_load_metadata_block( &mgate
, vg_mem
.scratch
);
58 mdl_mesh
*surface
= mdl_find_mesh( &mgate
, "rs_gate" );
59 mdl_submesh
*sm
= mdl_arritm(&mgate
.submeshs
,surface
->submesh_start
);
60 world_gates
.sm_surface
= *sm
;
62 const char *names
[] = { "rs_gate_marker", "rs_gate_marker.001",
63 "rs_gate_marker.002", "rs_gate_marker.003" };
65 for( int i
=0; i
<4; i
++ ){
66 mdl_mesh
*marker
= mdl_find_mesh( &mgate
, names
[i
] );
67 sm
= mdl_arritm( &mgate
.submeshs
, marker
->submesh_start
);
68 world_gates
.sm_marker
[i
] = *sm
;
71 mdl_async_load_glmesh( &mgate
, &world_gates
.mesh
, NULL
);
75 void ent_gate_get_mdl_mtx( ent_gate
*gate
, m4x3f mmdl
)
77 m4x3_copy( gate
->to_world
, mmdl
);
79 if( !(gate
->flags
& k_ent_gate_custom_mesh
) ){
80 m3x3_scale( mmdl
, (v3f
){ gate
->dimensions
[0],
81 gate
->dimensions
[1], 1.0f
} );
85 static void render_gate_mesh( world_instance
*world
, ent_gate
*gate
)
87 if( gate
->flags
& k_ent_gate_custom_mesh
){
88 mesh_bind( &world
->mesh_no_collide
);
89 for( u32 i
=0; i
<gate
->submesh_count
; i
++ ){
90 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
91 gate
->submesh_start
+i
);
92 mdl_draw_submesh( sm
);
96 mesh_bind( &world_gates
.mesh
);
97 mdl_draw_submesh( &world_gates
.sm_surface
);
102 * Render the view through a gate
104 int render_gate( world_instance
*world
, world_instance
*world_inside
,
105 ent_gate
*gate
, vg_camera
*cam
)
107 v3f viewdir
, gatedir
;
108 m3x3_mulv( cam
->transform
, (v3f
){0.0f
,0.0f
,-1.0f
}, viewdir
);
109 q_mulv( gate
->q
[0], (v3f
){0.0f
,0.0f
,-1.0f
}, gatedir
);
112 v3_sub( cam
->pos
, gate
->co
[0], v0
);
114 float dist
= v3_dot(v0
, gatedir
);
120 if( v3_dist( cam
->pos
, gate
->co
[0] ) > 100.0f
)
124 f32 w
= gate
->dimensions
[0],
125 h
= gate
->dimensions
[1];
128 m4x3_mulv( gate
->to_world
, (v3f
){-w
,-h
,0.0f
}, a
);
129 m4x3_mulv( gate
->to_world
, (v3f
){ w
,-h
,0.0f
}, b
);
130 m4x3_mulv( gate
->to_world
, (v3f
){ w
, h
,0.0f
}, c
);
131 m4x3_mulv( gate
->to_world
, (v3f
){-w
, h
,0.0f
}, d
);
133 vg_line( a
,b
, 0xffffa000 );
134 vg_line( b
,c
, 0xffffa000 );
135 vg_line( c
,d
, 0xffffa000 );
136 vg_line( d
,a
, 0xffffa000 );
137 vg_line( gate
->co
[0], gate
->co
[1], 0xff0000ff );
140 /* update gate camera */
141 world_gates
.cam
.fov
= cam
->fov
;
142 world_gates
.cam
.nearz
= 0.1f
;
143 world_gates
.cam
.farz
= 2000.0f
;
145 m4x3_mul( gate
->transport
, cam
->transform
, world_gates
.cam
.transform
);
146 vg_camera_update_view( &world_gates
.cam
);
147 vg_camera_update_projection( &world_gates
.cam
);
149 /* Add special clipping plane to projection */
151 q_mulv( gate
->q
[1], (v3f
){0.0f
,0.0f
,-1.0f
}, surface
);
152 surface
[3] = v3_dot( surface
, gate
->co
[1] );
154 m4x3_mulp( world_gates
.cam
.transform_inverse
, surface
, surface
);
155 surface
[3] = -fabsf(surface
[3]);
158 m4x4_clip_projection( world_gates
.cam
.mtx
.p
, surface
);
160 /* Ready to draw with new camrea */
161 vg_camera_finalize( &world_gates
.cam
);
163 vg_line_point( world_gates
.cam
.transform
[3], 0.3f
, 0xff00ff00 );
165 shader_model_gate_use();
166 shader_model_gate_uPv( cam
->mtx
.pv
);
167 shader_model_gate_uCam( cam
->pos
);
168 shader_model_gate_uColour( (v4f
){0.0f
,1.0f
,0.0f
,0.0f
} );
169 shader_model_gate_uTime( vg
.time
*0.25f
);
170 shader_model_gate_uInvRes( (v2f
){
171 1.0f
/ (float)vg
.window_x
,
172 1.0f
/ (float)vg
.window_y
});
174 glEnable( GL_STENCIL_TEST
);
175 glStencilOp( GL_KEEP
, GL_KEEP
, GL_REPLACE
);
176 glStencilFunc( GL_ALWAYS
, 1, 0xFF );
177 glStencilMask( 0xFF );
178 glEnable( GL_CULL_FACE
);
181 ent_gate_get_mdl_mtx( gate
, mmdl
);
182 shader_model_gate_uMdl( mmdl
);
183 render_gate_mesh( world
, gate
);
185 render_world( world_inside
, &world_gates
.cam
,
186 1, !localplayer
.gate_waiting
, 1, 1 );
191 void render_gate_unlinked( world_instance
*world
,
192 ent_gate
*gate
, vg_camera
*cam
)
194 m4x3f mmdl
; m4x4f m4mdl
;
195 ent_gate_get_mdl_mtx( gate
, mmdl
);
196 m4x3_expand( mmdl
, m4mdl
);
197 m4x4_mul( cam
->mtx_prev
.pv
, m4mdl
, m4mdl
);
199 shader_model_gate_unlinked_use();
200 shader_model_gate_unlinked_uPv( cam
->mtx
.pv
);
201 shader_model_gate_unlinked_uPvmPrev( m4mdl
);
202 shader_model_gate_unlinked_uCam( cam
->pos
);
203 shader_model_gate_unlinked_uColour( (v4f
){0.0f
,1.0f
,0.0f
,0.0f
} );
204 shader_model_gate_unlinked_uTime( vg
.time
*0.25f
);
205 shader_model_gate_unlinked_uMdl( mmdl
);
207 vg_line_point( gate
->co
[0], 0.1f
, 0xffffff00 );
209 render_gate_mesh( world
, gate
);
213 * Intersect the plane of a gate with a line segment, plane coordinate result
216 static int gate_intersect_plane( ent_gate
*gate
,
217 v3f pos
, v3f last
, v2f where
)
220 q_mulv( gate
->q
[0], (v3f
){0.0f
,0.0f
,-1.0f
}, surface
);
221 surface
[3] = v3_dot( surface
, gate
->co
[0] );
223 v3f v0
, c
, delta
, p0
;
224 v3_sub( pos
, last
, v0
);
225 float l
= v3_length( v0
);
230 v3_divs( v0
, l
, v0
);
232 v3_muls( surface
, surface
[3], c
);
233 v3_sub( c
, last
, delta
);
235 float d
= v3_dot( surface
, v0
);
238 float t
= v3_dot(delta
, surface
) / d
;
239 if( t
>= 0.0f
&& t
<= l
){
241 v3_muladds( last
, v0
, t
, local
);
242 v3_sub( gate
->co
[0], local
, rel
);
244 where
[0] = v3_dot( rel
, gate
->to_world
[0] );
245 where
[1] = v3_dot( rel
, gate
->to_world
[1] );
247 where
[0] /= v3_dot( gate
->to_world
[0], gate
->to_world
[0] );
248 where
[1] /= v3_dot( gate
->to_world
[1], gate
->to_world
[1] );
258 * Intersect specific gate
260 int gate_intersect( ent_gate
*gate
, v3f pos
, v3f last
)
264 if( gate_intersect_plane( gate
, pos
, last
, xy
) ){
265 if( (fabsf(xy
[0]) <= gate
->dimensions
[0]) &&
266 (fabsf(xy
[1]) <= gate
->dimensions
[1]) ){
275 * Intersect all gates in the world
277 u32
world_intersect_gates( world_instance
*world
, v3f pos
, v3f last
)
279 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_gate
); i
++ ){
280 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, i
);
282 if( !(gate
->flags
& k_ent_gate_linked
) ) continue;
283 if( gate
->flags
& k_ent_gate_locked
) continue;
285 if( gate
->flags
& k_ent_gate_nonlocal
){
286 if( world_static
.instances
[gate
->target
].status
287 != k_world_status_loaded
)
291 if( gate_intersect( gate
, pos
, last
) )
292 return mdl_entity_id( k_ent_gate
, i
);
298 entity_call_result
ent_gate_call( world_instance
*world
, ent_call
*call
)
300 u32 index
= mdl_entity_id_id( call
->id
);
301 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
303 if( call
->function
== 0 ) /* unlock() */
305 gate
->flags
&= ~k_ent_gate_locked
;
306 return k_entity_call_result_OK
;
310 return k_entity_call_result_unhandled
;
316 * detatches any nonlocal gates
318 void world_unlink_nonlocal( world_instance
*world
)
320 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_gate
); j
++ )
322 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, j
);
324 if( gate
->flags
& k_ent_gate_nonlocal
)
326 gate
->flags
&= ~k_ent_gate_linked
;
332 * This has to be synchronous because main thread looks at gate data for
333 * rendering, and we modify gates that the main thread has ownership of.
335 void world_link_gates_async( void *payload
, u32 size
)
337 VG_ASSERT( vg_thread_purpose() == k_thread_purpose_main
);
339 world_instance
*world
= payload
;
340 u32 world_id
= world
- world_static
.instances
;
342 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_gate
); j
++ )
344 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, j
);
345 gate_transform_update( gate
);
347 if( skaterift
.demo_mode
)
348 if( world_static
.instance_addons
[world_id
]->flags
& ADDON_REG_PREMIUM
)
351 if( !(gate
->flags
& k_ent_gate_nonlocal
) ) continue;
352 if( gate
->flags
& k_ent_gate_linked
) continue;
354 const char *key
= mdl_pstr( &world
->meta
, gate
->key
);
355 vg_info( "key: %s\n", key
);
357 for( u32 i
=0; i
<vg_list_size(world_static
.instances
); i
++ ){
358 world_instance
*other
= &world_static
.instances
[i
];
359 if( other
== world
) continue;
360 if( other
->status
!= k_world_status_loaded
) continue;
361 vg_info( "Checking world %u for key matches\n", i
);
363 for( u32 k
=0; k
<mdl_arrcount( &other
->ent_gate
); k
++ ){
364 ent_gate
*gate2
= mdl_arritm( &other
->ent_gate
, k
);
366 if( !(gate2
->flags
& k_ent_gate_nonlocal
) ) continue;
367 if( gate2
->flags
& k_ent_gate_linked
) continue;
369 const char *key2
= mdl_pstr( &other
->meta
, gate2
->key
);
370 vg_info( " key2: %s\n", key2
);
372 if( strcmp( key
, key2
) ) continue;
374 vg_success( "Non-local matching pair '%s' found. (%u:%u)\n",
377 gate
->flags
|= k_ent_gate_linked
;
378 gate2
->flags
|= k_ent_gate_linked
;
380 gate2
->target
= world_id
;
382 v3_copy( gate
->co
[0], gate2
->co
[1] );
383 v3_copy( gate2
->co
[0], gate
->co
[1] );
384 v4_copy( gate
->q
[0], gate2
->q
[1] );
385 v4_copy( gate2
->q
[0], gate
->q
[1] );
387 if( world
->meta
.info
.version
< 102 ){
388 /* LEGACY BEHAVIOUR: v101
389 * this would flip both the client worlds portal's entrance and
390 * exit. effectively the clients portal would be the opposite
391 * to the hub worlds one. new behaviour is to just flip the
392 * destinations so the rules are consistent in each world.
395 q_axis_angle( qflip
, (v3f
){0.0f
,1.0f
,0.0f
}, VG_PIf
);
396 q_mul( gate
->q
[0], qflip
, gate
->q
[0] );
397 q_mul( gate
->q
[1], qflip
, gate
->q
[1] );
398 q_mul( gate2
->q
[1], qflip
, gate2
->q
[1] );
401 gate_transform_update( gate
);
402 gate_transform_update( gate2
);
410 #endif /* WORLD_GATE_C */