_fields_ = [("identifier",c_uint32),
("vertex_count",c_uint32),
("indice_count",c_uint32),
- ("layer_count",c_uint32)]
+ ("layer_count",c_uint32),
+ ("marker_count",c_uint32)]
class sdf_primative(Structure):
_pack_ = 1
("sdf_type",c_int32),
("name",c_char*32)]
+class marker(Structure):
+ _pack_ = 1
+ _fields_ = [("co",c_float*3),
+ ( "q",c_float*4),
+ ( "s",c_float*3),
+ ("name",c_char*32)]
+
class model_vert(Structure):
_pack_ = 1
_fields_ = [("co",c_float*3),
("colour",c_float*4),
("uv",c_float*2)]
-def fixed_string(dest,string):
- return
- for i in range(len(string)):
- dest[i] = string[i]
+def v4_dot( a, b ):
+ return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*a[3]
+
+def v4_length( a ):
+ return math.sqrt( v4_dot(a,a) )
+
+def m3x3_mul( a, b, d ):
+ a00 = a[0][0]
+ a01 = a[0][1]
+ a02 = a[0][2]
+ a10 = a[1][0]
+ a11 = a[1][1]
+ a12 = a[1][2]
+ a20 = a[2][0]
+ a21 = a[2][1]
+ a22 = a[2][2]
+ b00 = b[0][0]
+ b01 = b[0][1]
+ b02 = b[0][2]
+ b10 = b[1][0]
+ b11 = b[1][1]
+ b12 = b[1][2]
+ b20 = b[2][0]
+ b21 = b[2][1]
+ b22 = b[2][2]
+ d[0][0] = a00*b00 + a10*b01 + a20*b02
+ d[0][1] = a01*b00 + a11*b01 + a21*b02
+ d[0][2] = a02*b00 + a12*b01 + a22*b02
+ d[1][0] = a00*b10 + a10*b11 + a20*b12
+ d[1][1] = a01*b10 + a11*b11 + a21*b12
+ d[1][2] = a02*b10 + a12*b11 + a22*b12
+ d[2][0] = a00*b20 + a10*b21 + a20*b22
+ d[2][1] = a01*b20 + a11*b21 + a21*b22
+ d[2][2] = a02*b20 + a12*b21 + a22*b22
+
+def q_m3x3( q, d ):
+ l = v4_length(q)
+ s = 2.0 if l > 0.0 else 0.0
+ xx = s*q[0]*q[0]
+ xy = s*q[0]*q[1]
+ wx = s*q[3]*q[0]
+ yy = s*q[1]*q[1]
+ yz = s*q[1]*q[2]
+ wy = s*q[3]*q[1]
+ zz = s*q[2]*q[2]
+ xz = s*q[0]*q[2]
+ wz = s*q[3]*q[2]
+ d[0][0] = 1.0 - yy - zz
+ d[1][1] = 1.0 - xx - zz
+ d[2][2] = 1.0 - xx - yy
+ d[0][1] = xy + wz
+ d[1][2] = yz + wx
+ d[2][0] = xz + wy
+ d[1][0] = xy - wz
+ d[2][1] = yz - wx
+ d[0][2] = xz - wy
+
+def m3x3_q( m, q ):
+ diag = m[0][0] + m[1][1] + m[2][2]
+ if diag >= 0.0:
+ r = math.sqrt( 1.0 + diag )
+ rinv = 0.5 / r
+ q[0] = rinv * (m[1][2] - m[2][1])
+ q[1] = rinv * (m[2][0] - m[0][2])
+ q[2] = rinv * (m[0][1] - m[1][0])
+ q[3] = r * 0.5
+ elif m[0][0] >= m[1][1] and m[0][0] >= m[2][2]:
+ r = math.sqrt( 1.0 - m[1][1] - m[2][2] + m[0][0] )
+ rinv = 0.5 / r
+ q[0] = r * 0.5
+ q[1] = rinv * (m[0][1] + m[1][0])
+ q[2] = rinv * (m[0][2] + m[2][0])
+ q[3] = rinv * (m[1][2] - m[2][1])
+ elif m[1][1] >= m[2][2]:
+ r = math.sqrt( 1.0 - m[0][0] - m[2][2] + m[1][1] )
+ rinv = 0.5 / r
+ q[0] = rinv * (m[0][1] + m[1][0])
+ q[1] = r * 0.5
+ q[2] = rinv * (m[1][2] + m[2][1])
+ q[3] = rinv * (m[2][0] - m[0][2])
+ else:
+ r = math.sqrt( 1.0 - m[0][0] - m[1][1] + m[2][2] )
+ rinv = 0.5 / r
+ q[0] = rinv * (m[0][2] + m[2][0])
+ q[1] = rinv * (m[1][2] + m[2][1])
+ q[2] = r * 0.5
+ q[3] = rinv * (m[0][1] - m[1][0])
def write_model(name):
fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb")
header.layer_count = 0
layers = []
+ markers = []
vertex_buffer = []
indice_buffer = []
for obj in collection.objects:
- if obj.type == 'MESH':
+ if obj.type == 'EMPTY':
+ mk = marker()
+ mk.co[0] = obj.location[0]
+ mk.co[1] = obj.location[2]
+ mk.co[2] = -obj.location[1]
+
+ # Convert rotation quat to our space type
+ quat = obj.matrix_world.to_quaternion()
+ mk.q[0] = quat[1]
+ mk.q[1] = quat[3]
+ mk.q[2] = -quat[2]
+ mk.q[3] = quat[0]
+
+ mk.s[0] = obj.scale[0]
+ mk.s[1] = obj.scale[2]
+ mk.s[2] = obj.scale[1]
+ mk.name = obj.name.encode('utf-8')
+
+ markers += [mk]
+ header.marker_count += 1
+
+ elif obj.type == 'MESH':
dgraph = bpy.context.evaluated_depsgraph_get()
data = obj.evaluated_get(dgraph).data
data.calc_loop_triangles()
fp.write( bytearray( header ) )
for l in layers:
fp.write( bytearray(l) )
+ for m in markers:
+ fp.write( bytearray(m) )
for v in vertex_buffer:
fp.write( bytearray(v) )
for i in indice_buffer:
#ifndef GATE_H
#define GATE_H
+#include "vg/vg.h"
+
+static const float k_gatesize = 4.0f;
+
+SHADER_DEFINE( shader_gate,
+ "layout (location=0) in vec3 a_co;"
+ "uniform mat4 uPv;"
+ "uniform mat4x3 uMdl;"
+ ""
+ "void main()"
+ "{"
+ "gl_Position = uPv * vec4(uMdl * vec4( a_co, 1.0 ),1.0);"
+ "}",
+
+ /* Fragment */
+ "out vec4 FragColor;"
+ ""
+ "uniform sampler2D uTexMain;"
+ "uniform vec2 uInvRes;"
+ ""
+ "void main()"
+ "{"
+ "vec2 uv = gl_FragCoord.xy*uInvRes;"
+ "FragColor = texture( uTexMain, uv );"
+ "}"
+ ,
+ UNIFORMS({ "uPv", "uMdl", "uTexMain", "uInvRes" })
+)
+
+typedef struct teleport_gate teleport_gate;
+
+static struct
+{
+ GLuint fb, rgb, rb, vao, vbo;
+}
+grender;
+
+struct teleport_gate
+{
+ v3f co;
+ v4f q;
+
+ m4x3f to_world, to_local;
+ teleport_gate *other;
+};
+
+static void gate_transform_update( teleport_gate *gate )
+{
+ q_m3x3( gate->q, gate->to_world );
+ v3_copy( gate->co, gate->to_world[3] );
+
+ m4x3_invert_affine( gate->to_world, gate->to_local );
+}
+
+static void gate_register(void)
+{
+ SHADER_INIT( shader_gate );
+}
+
+static void gate_init(void)
+{
+ glGenFramebuffers( 1, &grender.fb );
+ glBindFramebuffer( GL_FRAMEBUFFER, grender.fb );
+
+ glGenTextures( 1, &grender.rgb );
+ glBindTexture( GL_TEXTURE_2D, grender.rgb );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y,
+ 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, grender.rgb, 0);
+
+ /* TODO: Check for DEPTH32f availiblity and use if possible */
+
+ glGenRenderbuffers( 1, &grender.rb );
+ glBindRenderbuffer( GL_RENDERBUFFER, grender.rb );
+ glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
+ vg_window_x, vg_window_y );
+
+ glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, grender.rb );
+
+ {
+ float ksz = k_gatesize;
+ float quad[] = { -ksz,-ksz,0.0f, ksz, ksz,0.0f, -ksz, ksz,0.0f,
+ -ksz,-ksz,0.0f, ksz,-ksz,0.0f, ksz, ksz,0.0f,
+ -ksz,-ksz,0.0f, -ksz, ksz,0.0f, ksz, ksz,0.0f,
+ -ksz,-ksz,0.0f, ksz, ksz,0.0f, ksz,-ksz,0.0f };
+
+ glGenVertexArrays( 1, &grender.vao );
+ glGenBuffers( 1, &grender.vbo );
+ glBindVertexArray( grender.vao );
+ glBindBuffer( GL_ARRAY_BUFFER, grender.vbo );
+ glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
+ glBindVertexArray( grender.vao );
+ glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE,
+ sizeof(float)*3, (void*)0 );
+ glEnableVertexAttribArray( 0 );
+ VG_CHECK_GL();
+ }
+}
+
+static void render_world(m4x4f pv);
+
+/*
+ * http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
+ */
+static void plane_clip_projection( m4x4f mat, v4f plane )
+{
+ v4f c =
+ {
+ (vg_signf(plane[0]) + mat[2][0]) / mat[0][0],
+ (vg_signf(plane[1]) + mat[2][1]) / mat[1][1],
+ -1.0f,
+ (1.0f + mat[2][2]) / mat[3][2]
+ };
+
+ v4_muls( plane, 2.0f / v4_dot(plane,c), c );
+
+ mat[0][2] = c[0];
+ mat[1][2] = c[1];
+ mat[2][2] = c[2] + 1.0f;
+ mat[3][2] = c[3];
+}
+
+static void render_gate( teleport_gate *gate, m4x3f camera, float fov )
+{
+ m4x3f transport;
+
+ m4x3_mul( gate->other->to_world, gate->to_local, transport );
+
+ v3f a,b,c,d;
+
+ float ksz = k_gatesize;
+ m4x3_mulv( gate->to_world, (v3f){-ksz,-ksz,0.0f}, a );
+ m4x3_mulv( gate->to_world, (v3f){ ksz,-ksz,0.0f}, b );
+ m4x3_mulv( gate->to_world, (v3f){ ksz, ksz,0.0f}, c );
+ m4x3_mulv( gate->to_world, (v3f){-ksz, ksz,0.0f}, d );
+
+ vg_line( a,b, 0xffffa000 );
+ vg_line( b,c, 0xffffa000 );
+ vg_line( c,d, 0xffffa000 );
+ vg_line( d,a, 0xffffa000 );
+
+ vg_line( gate->co, gate->other->co, 0xffffffff );
+
+ m4x3f cam_new;
+ m4x3_mul( transport, camera, cam_new );
+
+ vg_line_pt3( cam_new[3], 0.3f, 0xff00ff00 );
+
+ glBindFramebuffer( GL_FRAMEBUFFER, grender.fb );
+ glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
+ glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+
+ m4x3f inverse;
+ m4x3_invert_affine( cam_new, inverse );
+
+ m4x4f view;
+ m4x3_expand( inverse, view );
+
+ v4f surface;
+ m3x3_mulv( gate->other->to_world, (v3f){0.0f,0.0f,-1.0f}, surface );
+ surface[3] = v3_dot( surface, gate->other->co );
+
+ m4x4f projection;
+ m4x4_projection( projection,
+ fov,
+ (float)vg_window_x / (float)vg_window_y,
+ 0.01f, 900.0f );
+
+#if 0 /* For debugging frustum */
+ {
+ m4x4f devm;
+ m4x4_mul( projection, view, devm );
+ m4x4_inv( devm, devm );
+
+ v4f corners[] =
+ {
+ {-1,-1,-1, 1}, { 1,-1,-1, 1}, { 1, 1,-1, 1}, {-1, 1,-1, 1},
+ {-1,-1, 1, 1}, { 1,-1, 1, 1}, { 1, 1, 1, 1}, {-1, 1, 1, 1}
+ };
+
+ for( int i=0; i<vg_list_size(corners); i++ )
+ {
+ m4x4_mulv( devm, corners[i], corners[i] );
+ v3_muls( corners[i], 1.0f/corners[i][3], corners[i] );
+ }
+
+ vg_line( corners[0], corners[1], 0xffffffff );
+ vg_line( corners[1], corners[2], 0xffffffff );
+ vg_line( corners[2], corners[3], 0xffffffff );
+ vg_line( corners[3], corners[0], 0xffffffff );
+ vg_line( corners[4], corners[5], 0xffffffff );
+ vg_line( corners[5], corners[6], 0xffffffff );
+ vg_line( corners[6], corners[7], 0xffffffff );
+ vg_line( corners[7], corners[4], 0xffffffff );
+ vg_line( corners[0], corners[4], 0xffffffff );
+ vg_line( corners[1], corners[5], 0xffffffff );
+ vg_line( corners[2], corners[6], 0xffffffff );
+ vg_line( corners[3], corners[7], 0xffffffff );
+
+ v3f clipped[4];
+ for( int i=0; i<4; i++ )
+ {
+ v3f v0, c, delta, p0;
+ v3_sub( corners[4+i],corners[0+i], v0 );
+ v3_normalize(v0);
+
+ v3_muls( surface, surface[3], c );
+ v3_sub( c, corners[0+i], delta );
+
+ float t = v3_dot(delta, surface) / v3_dot(surface, v0);
+ v3_muladds( corners[0+i], v0, t, clipped[i] );
+ }
+
+ vg_line( clipped[0], clipped[1], 0xff0000ff );
+ vg_line( clipped[1], clipped[2], 0xff0000ff );
+ vg_line( clipped[2], clipped[3], 0xff0000ff );
+ vg_line( clipped[3], clipped[0], 0xff0000ff );
+
+ m4x3_mulv( gate->other->to_world, (v3f){-2.0f,-2.0f,0.0f}, a );
+ m4x3_mulv( gate->other->to_world, (v3f){ 2.0f,-2.0f,0.0f}, b );
+ m4x3_mulv( gate->other->to_world, (v3f){ 2.0f, 2.0f,0.0f}, c );
+ m4x3_mulv( gate->other->to_world, (v3f){-2.0f, 2.0f,0.0f}, d );
+
+ vg_line( clipped[0], a, 0xff0000ff );
+ vg_line( clipped[1], b, 0xff0000ff );
+ vg_line( clipped[2], c, 0xff0000ff );
+ vg_line( clipped[3], d, 0xff0000ff );
+ }
+#endif
+
+ m4x3_mulp( inverse, surface, surface );
+ surface[3] = -fabsf(surface[3]);
+ plane_clip_projection( projection, surface );
+
+ m4x4_mul( projection, view, projection );
+
+ render_world( projection );
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+
+ SHADER_USE( shader_gate );
+
+ glUniformMatrix4fv( SHADER_UNIFORM( shader_gate, "uPv" ),
+ 1, GL_FALSE, (float *)vg_pv );
+ glUniformMatrix4x3fv( SHADER_UNIFORM( shader_gate, "uMdl" ),
+ 1, GL_FALSE, (float *)gate->to_world );
+
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_2D, grender.rgb );
+ glUniform1i( SHADER_UNIFORM( shader_gate, "uTexMain"), 0 );
+ glUniform2f( SHADER_UNIFORM( shader_gate, "uInvRes"),
+ 1.0f / (float)vg_window_x,
+ 1.0f / (float)vg_window_y );
+
+ glBindVertexArray( grender.vao );
+ glDrawArrays( GL_TRIANGLES, 0, 12 );
+}
+
+static int gate_intersect( teleport_gate *gate, v3f pos, v3f last )
+{
+ v4f surface;
+ m3x3_mulv( gate->to_world, (v3f){0.0f,0.0f,-1.0f}, surface );
+ surface[3] = v3_dot( surface, gate->co );
+
+ v3f v0, c, delta, p0;
+ v3_sub( pos, last, v0 );
+ float l = v3_length( v0 );
+ v3_divs( v0, l, v0 );
+
+ v3_muls( surface, surface[3], c );
+ v3_sub( c, last, delta );
+
+ float d = v3_dot(surface, v0);
+
+ if( fabsf(d) > 0.00001f )
+ {
+ float t = v3_dot(delta, surface) / d;
+ if( t >= 0.0f && t <= l )
+ {
+ v3f local, rel;
+ v3_muladds( last, v0, t, local );
+ v3_sub( gate->co, local, rel );
+
+ v3f vup, vside;
+ m3x3_mulv( gate->to_world, (v3f){0.0f,1.0f,0.0f}, vup );
+ m3x3_mulv( gate->to_world, (v3f){1.0f,0.0f,0.0f}, vside );
+
+ v2f xy = { v3_dot( rel, vside ), v3_dot( rel, vup ) };
+
+ if( fabsf(xy[0]) <= k_gatesize && fabsf(xy[1]) <= k_gatesize )
+ {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
#endif
#include "terrain.h"
#include "ragdoll.h"
#include "rigidbody.h"
+#include "gate.h"
int main( int argc, char *argv[] )
{
static struct gplayer
{
/* Physics */
- v3f co, v, a;
+ v3f co, v, a, v_last;
v4f rot;
float vswitch, slip, slip_last,
reverse;
.bbx = {{ -0.5f, -0.25f, -0.25f }, { 0.5f, 0.25f, 0.25f }}
};
+teleport_gate gate_a = {
+ .co = { 0.0f, -3.0f, -15.0f },
+ .q = { 0.0f, 0.0f, 0.0f, 1.0f }
+},
+gate_b = {
+ .co = { -8.0f, -3.0f, -17.0f },
+ .q = { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
static void player_transform_update(void)
{
q_normalize( player.rot );
void vg_register(void)
{
scene_register();
+ gate_register();
character_shader_register();
SHADER_INIT( shader_blit );
}
(v3f){0.0f,0.0f,0.0f}, 0.0f, 1.0f );
scene_copy_slice( &world.geo, &world.sm_terrain );
- v3_copy( submodel_get( mworld, "tutorial" )->pivot, world.tutorial );
+ v3_copy( model_marker_get( mworld, "mp_dev_tutorial" )->co, world.tutorial );
+
+
+ /* GATE DEV */
+ {
+ model_marker *ga = model_marker_get(mworld,"gate_a"),
+ *gb = model_marker_get(mworld,"gate_a_recv");
+
+ v3_copy( ga->co, gate_a.co );
+ v3_copy( gb->co, gate_b.co );
+ v4_copy( ga->q, gate_a.q );
+ v4_copy( gb->q, gate_b.q );
+ gate_a.other = &gate_b;
+ gate_b.other = &gate_a;
+
+ gate_transform_update( &gate_a );
+ gate_transform_update( &gate_b );
+ }
free( mworld );
scene_upload( &world.geo );
GL_TEXTURE_2D,
render.rgb_background, 0);
+ gate_init();
+
{
float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
glGenVertexArrays( 1, &render.fsquad.vao );
glGenBuffers( 1, &render.fsquad.vbo );
- glGenBuffers( 1, &render.fsquad.ebo );
glBindVertexArray( render.fsquad.vao );
glBindBuffer( GL_ARRAY_BUFFER, render.fsquad.vbo );
glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
v3f pco, pco1, pv;
v3_copy( player.co, pco );
v3_copy( player.v, pv );
+ v3_muladds( pco, pv, ktimestep, pco );
/*
* Try different 'rotations' of the velocity to find the best possible
player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
+ if( player.in_air )
+ player_physics_air();
+
+ if( !player.in_air )
+ player_physics_ground();
+
/* Integrate velocity */
+ v3f prevco;
+ v3_copy( player.co, prevco );
if( sv_phys )
{
apply_gravity( player.v, ktimestep );
player.iY = 0.0f; /* temp */
- if( player.in_air )
- player_physics_air();
+ /* GATE COLLISION */
+ if( gate_intersect( &gate_a, player.co, prevco ) )
+ {
+ teleport_gate *gate = &gate_a;
- if( !player.in_air )
- player_physics_ground();
-
- /* Camera and character */
+ m4x3f transport;
+ m4x3_mul( gate->other->to_world, gate->to_local, transport );
+ m4x3_mulv( transport, player.co, player.co );
+ m3x3_mulv( transport, player.v, player.v );
+ m3x3_mulv( transport, player.v_last, player.v_last );
+ v4f transport_rotation;
+ m3x3_q( transport, transport_rotation );
+ q_mul( transport_rotation, player.rot, player.rot );
+ }
+
+ /* Camera and character */
player_transform_update();
q_normalize(player.rot);
player_animate();
static void player_animate(void)
{
/* Camera position */
- static v3f last_vel = { 0.0f, 0.0f, 0.0f };
static v3f momentum, bob;
- v3_sub( player.v, last_vel, player.a );
- v3_copy( player.v, last_vel );
+ v3_sub( player.v, player.v_last, player.a );
+ v3_copy( player.v, player.v_last );
v3_add( momentum, player.a, momentum );
v3_lerp( momentum, (v3f){0.0f,0.0f,0.0f}, 0.1f, momentum );
GL_RGB, GL_UNSIGNED_BYTE, NULL );
}
+static void render_world( m4x4f projection )
+{
+ SHADER_USE(shader_standard_lit);
+
+ m4x3f identity_matrix;
+ m4x3_identity( identity_matrix );
+
+ glUniformMatrix4fv( SHADER_UNIFORM( shader_standard_lit, "uPv" ),
+ 1, GL_FALSE, (float *)projection );
+ glUniformMatrix4x3fv( SHADER_UNIFORM( shader_standard_lit, "uMdl" ),
+ 1, GL_FALSE, (float *)identity_matrix );
+
+ vg_tex2d_bind( &tex_grid, 0 );
+ glUniform1i( SHADER_UNIFORM( shader_standard_lit, "uTexMain" ), 0 );
+ glUniform4f( SHADER_UNIFORM(shader_standard_lit,"uColour"),
+ 0.4f,0.4f,0.4f,1.0f );
+
+ scene_bind( &world.geo );
+ scene_draw( &world.geo );
+}
+
void vg_render(void)
{
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
m4x4f world_4x4;
m4x3_expand( world_matrix, world_4x4 );
- m4x4_projection( vg_pv,
- freecam? 60.0f: 120.0f,
- (float)vg_window_x / (float)vg_window_y,
+
+ float fov = freecam? 60.0f: 120.0f;
+ m4x4_projection( vg_pv, fov, (float)vg_window_x / (float)vg_window_y,
0.01f, 1000.0f );
+
m4x4_mul( vg_pv, world_4x4, vg_pv );
vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 1.0f, 0.0f, 0.0f }, 0xffff0000 );
}
#endif
+ m4x3f cam_transform;
+ m4x3_invert_affine( world_matrix, cam_transform );
+ render_gate( &gate_a, cam_transform, fov );
+
/* Copy the RGB of what we have into the background buffer */