fix star projection
This commit is contained in:
parent
c771fa665d
commit
df5d905050
22 changed files with 612 additions and 660 deletions
|
|
@ -496,7 +496,7 @@
|
||||||
|
|
||||||
(pxl8.end_frame_3d))
|
(pxl8.end_frame_3d))
|
||||||
|
|
||||||
(sky.render-stars cam-yaw cam-pitch 1.0 last-dt)
|
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
|
||||||
|
|
||||||
(let [cx (/ (pxl8.get_width) 2)
|
(let [cx (/ (pxl8.get_width) 2)
|
||||||
cy (/ (pxl8.get_height) 2)
|
cy (/ (pxl8.get_height) 2)
|
||||||
|
|
|
||||||
|
|
@ -40,20 +40,20 @@
|
||||||
|
|
||||||
(pxl8.gui_window 200 100 240 200 "pxl8 demo")
|
(pxl8.gui_window 200 100 240 200 "pxl8 demo")
|
||||||
|
|
||||||
(when (gui:button 1 215 140 210 30 "Resume")
|
(when (gui:button 1 215 147 210 30 "Resume")
|
||||||
(hide))
|
(hide))
|
||||||
|
|
||||||
(let [music-label (if (music.is-playing) "Music: On" "Music: Off")]
|
(let [music-label (if (music.is-playing) "Music: On" "Music: Off")]
|
||||||
(when (gui:button 3 215 175 210 30 music-label)
|
(when (gui:button 3 215 182 210 30 music-label)
|
||||||
(if (music.is-playing)
|
(if (music.is-playing)
|
||||||
(music.stop)
|
(music.stop)
|
||||||
(music.start))))
|
(music.start))))
|
||||||
|
|
||||||
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
||||||
(when (gui:button 4 215 210 210 30 wire-label)
|
(when (gui:button 4 215 217 210 30 wire-label)
|
||||||
(set wireframe (not wireframe))))
|
(set wireframe (not wireframe))))
|
||||||
|
|
||||||
(when (gui:button 2 215 245 210 30 "Quit")
|
(when (gui:button 2 215 252 210 30 "Quit")
|
||||||
(pxl8.quit))
|
(pxl8.quit))
|
||||||
|
|
||||||
(if (gui:is_hovering)
|
(if (gui:is_hovering)
|
||||||
|
|
|
||||||
131
demo/mod/sky.fnl
131
demo/mod/sky.fnl
|
|
@ -1,4 +1,3 @@
|
||||||
(local ffi (require :ffi))
|
|
||||||
(local pxl8 (require :pxl8))
|
(local pxl8 (require :pxl8))
|
||||||
(local effects (require :pxl8.effects))
|
(local effects (require :pxl8.effects))
|
||||||
|
|
||||||
|
|
@ -21,10 +20,13 @@
|
||||||
;; Blue/magic: indices 176-178 (purples - brightest of the range)
|
;; Blue/magic: indices 176-178 (purples - brightest of the range)
|
||||||
(local IDX_MAGIC 176)
|
(local IDX_MAGIC 176)
|
||||||
|
|
||||||
(var sky-mesh nil)
|
|
||||||
(var star-time 0)
|
|
||||||
(var last-gradient-key nil)
|
(var last-gradient-key nil)
|
||||||
(var random-stars [])
|
(var random-stars [])
|
||||||
|
(var sky-mesh nil)
|
||||||
|
(var star-count 0)
|
||||||
|
(var star-directions nil)
|
||||||
|
(var star-projected nil)
|
||||||
|
(var star-time 0)
|
||||||
(var tiny-stars [])
|
(var tiny-stars [])
|
||||||
|
|
||||||
(fn generate-sky-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
|
(fn generate-sky-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
|
||||||
|
|
@ -161,99 +163,88 @@
|
||||||
(when (> dy -0.1)
|
(when (> dy -0.1)
|
||||||
(table.insert tiny-stars {:dx dx :dy dy :dz dz
|
(table.insert tiny-stars {:dx dx :dy dy :dz dz
|
||||||
:brightness brightness
|
:brightness brightness
|
||||||
:color color}))))))
|
:color color})))))
|
||||||
|
|
||||||
(fn project-direction [dir-x dir-y dir-z yaw pitch cos-rot sin-rot width height]
|
(set star-count (+ (length tiny-stars) (length random-stars)))
|
||||||
(let [rot-x (- (* dir-x cos-rot) (* dir-z sin-rot))
|
(set star-directions (pxl8.create_vec3_array star-count))
|
||||||
rot-z (+ (* dir-x sin-rot) (* dir-z cos-rot))
|
(set star-projected (pxl8.create_vec3_array star-count))
|
||||||
cos-yaw (math.cos yaw)
|
|
||||||
sin-yaw (math.sin yaw)
|
|
||||||
cos-pitch (math.cos pitch)
|
|
||||||
sin-pitch (math.sin pitch)
|
|
||||||
rotated-x (+ (* rot-x cos-yaw) (* rot-z sin-yaw))
|
|
||||||
rotated-z (+ (* (- rot-x) sin-yaw) (* rot-z cos-yaw))
|
|
||||||
rotated-y (- (* dir-y cos-pitch) (* rotated-z sin-pitch))
|
|
||||||
final-z (+ (* dir-y sin-pitch) (* rotated-z cos-pitch))]
|
|
||||||
(when (> final-z 0.01)
|
|
||||||
(let [fov 1.047
|
|
||||||
aspect (/ width height)
|
|
||||||
half-fov-tan (math.tan (* fov 0.5))
|
|
||||||
ndc-x (/ rotated-x (* final-z half-fov-tan aspect))
|
|
||||||
ndc-y (/ rotated-y (* final-z half-fov-tan))]
|
|
||||||
(when (and (>= ndc-x -1) (<= ndc-x 1) (>= ndc-y -1) (<= ndc-y 1))
|
|
||||||
{:x (math.floor (* (+ 1 ndc-x) 0.5 width))
|
|
||||||
:y (math.floor (* (- 1 ndc-y) 0.5 height))})))))
|
|
||||||
|
|
||||||
(fn render-stars [yaw pitch intensity dt]
|
(var idx 0)
|
||||||
|
(each [_ star (ipairs tiny-stars)]
|
||||||
|
(let [dir (. star-directions idx)]
|
||||||
|
(set dir.x star.dx)
|
||||||
|
(set dir.y star.dy)
|
||||||
|
(set dir.z star.dz))
|
||||||
|
(set idx (+ idx 1)))
|
||||||
|
(each [_ star (ipairs random-stars)]
|
||||||
|
(let [dir (. star-directions idx)]
|
||||||
|
(set dir.x star.dx)
|
||||||
|
(set dir.y star.dy)
|
||||||
|
(set dir.z star.dz))
|
||||||
|
(set idx (+ idx 1))))
|
||||||
|
|
||||||
|
(fn render-stars [cam-x cam-y cam-z intensity dt]
|
||||||
(set star-time (+ star-time (or dt 0)))
|
(set star-time (+ star-time (or dt 0)))
|
||||||
(when (> intensity 0)
|
(when (and (> intensity 0) (> star-count 0))
|
||||||
(let [width (pxl8.get_width)
|
(let [glows []
|
||||||
height (pxl8.get_height)
|
|
||||||
glows []
|
|
||||||
fade-in (* intensity intensity)
|
fade-in (* intensity intensity)
|
||||||
time-factor (/ star-time 60)
|
time-factor (/ star-time 60)
|
||||||
star-rotation (/ (* star-time math.pi 2) STAR_CYCLE_PERIOD)
|
star-rotation (/ (* star-time math.pi 2) STAR_CYCLE_PERIOD)
|
||||||
cos-rot (math.cos star-rotation)
|
t (pxl8.mat4_translate cam-x cam-y cam-z)
|
||||||
sin-rot (math.sin star-rotation)]
|
r (pxl8.mat4_rotate_y star-rotation)
|
||||||
|
s (pxl8.mat4_scale sky-radius sky-radius sky-radius)
|
||||||
|
transform (pxl8.mat4_multiply t (pxl8.mat4_multiply r s))
|
||||||
|
tiny-count (length tiny-stars)]
|
||||||
|
|
||||||
(each [i star (ipairs tiny-stars)]
|
(pxl8.project_points star-directions star-projected star-count transform)
|
||||||
(let [screen (project-direction star.dx star.dy star.dz yaw pitch cos-rot sin-rot width height)]
|
|
||||||
(when screen
|
(for [i 0 (- tiny-count 1)]
|
||||||
(let [int (math.floor (* star.brightness fade-in))]
|
(let [screen (. star-projected i)]
|
||||||
|
(when (> screen.z 0)
|
||||||
|
(let [star (. tiny-stars (+ i 1))
|
||||||
|
int (math.floor (* star.brightness fade-in))]
|
||||||
(when (> int 8)
|
(when (> int 8)
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
(table.insert glows {:x (math.floor screen.x) :y (math.floor screen.y)
|
||||||
:radius 1
|
:radius 1
|
||||||
:intensity int
|
:intensity int
|
||||||
:color star.color
|
:color star.color
|
||||||
:shape effects.GLOW_CIRCLE}))))))
|
:shape effects.GLOW_CIRCLE}))))))
|
||||||
|
|
||||||
(each [i star (ipairs random-stars)]
|
(for [i 0 (- (length random-stars) 1)]
|
||||||
(let [screen (project-direction star.dx star.dy star.dz yaw pitch cos-rot sin-rot width height)]
|
(let [screen (. star-projected (+ tiny-count i))]
|
||||||
(when screen
|
(when (> screen.z 0)
|
||||||
(let [phase (+ (* i 2.137) (* time-factor 3.0))
|
(let [star (. random-stars (+ i 1))
|
||||||
|
phase (+ (* (+ i 1) 2.137) (* time-factor 3.0))
|
||||||
twinkle (+ 0.75 (* 0.25 (math.sin (* phase 6.28))))
|
twinkle (+ 0.75 (* 0.25 (math.sin (* phase 6.28))))
|
||||||
int (math.floor (* star.brightness fade-in twinkle))]
|
int (math.floor (* star.brightness fade-in twinkle))
|
||||||
|
sx (math.floor screen.x)
|
||||||
|
sy (math.floor screen.y)]
|
||||||
(if (> star.brightness 220)
|
(if (> star.brightness 220)
|
||||||
(do
|
(do
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
(table.insert glows {:x sx :y sy :radius 3
|
||||||
:radius 3
|
|
||||||
:intensity (math.floor (* int 1.5))
|
:intensity (math.floor (* int 1.5))
|
||||||
:color star.color
|
:color star.color :shape effects.GLOW_DIAMOND})
|
||||||
:shape effects.GLOW_DIAMOND})
|
(table.insert glows {:x sx :y sy :radius 5
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
|
||||||
:radius 5
|
|
||||||
:intensity (math.floor (/ int 2))
|
:intensity (math.floor (/ int 2))
|
||||||
:color star.color
|
:color star.color :shape effects.GLOW_CIRCLE}))
|
||||||
:shape effects.GLOW_CIRCLE}))
|
|
||||||
(> star.brightness 180)
|
(> star.brightness 180)
|
||||||
(do
|
(do
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
(table.insert glows {:x sx :y sy :radius 2 :intensity int
|
||||||
:radius 2
|
:color star.color :shape effects.GLOW_DIAMOND})
|
||||||
:intensity int
|
(table.insert glows {:x sx :y sy :radius 4
|
||||||
:color star.color
|
|
||||||
:shape effects.GLOW_DIAMOND})
|
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
|
||||||
:radius 4
|
|
||||||
:intensity (math.floor (/ int 3))
|
:intensity (math.floor (/ int 3))
|
||||||
:color star.color
|
:color star.color :shape effects.GLOW_CIRCLE}))
|
||||||
:shape effects.GLOW_CIRCLE}))
|
|
||||||
(> star.brightness 120)
|
(> star.brightness 120)
|
||||||
(do
|
(do
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
(table.insert glows {:x sx :y sy :radius 2
|
||||||
:radius 2
|
|
||||||
:intensity (math.floor (* int 0.67))
|
:intensity (math.floor (* int 0.67))
|
||||||
:color star.color
|
:color star.color :shape effects.GLOW_DIAMOND})
|
||||||
:shape effects.GLOW_DIAMOND})
|
(table.insert glows {:x sx :y sy :radius 3
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
|
||||||
:radius 3
|
|
||||||
:intensity (math.floor (/ int 4))
|
:intensity (math.floor (/ int 4))
|
||||||
:color star.color
|
:color star.color :shape effects.GLOW_CIRCLE}))
|
||||||
:shape effects.GLOW_CIRCLE}))
|
(table.insert glows {:x sx :y sy :radius 2
|
||||||
(table.insert glows {:x screen.x :y screen.y
|
|
||||||
:radius 2
|
|
||||||
:intensity (math.floor (* int 0.5))
|
:intensity (math.floor (* int 0.5))
|
||||||
:color star.color
|
:color star.color :shape effects.GLOW_CIRCLE}))))))
|
||||||
:shape effects.GLOW_CIRCLE}))))))
|
|
||||||
|
|
||||||
(when (> (length glows) 0)
|
(when (> (length glows) 0)
|
||||||
(effects.glows glows)))))
|
(effects.glows glows)))))
|
||||||
|
|
|
||||||
5
demo/profile_3d.fnl
Normal file
5
demo/profile_3d.fnl
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
(local first_person3d (require :mod.first_person3d))
|
||||||
|
|
||||||
|
(global init first_person3d.init)
|
||||||
|
(global update first_person3d.update)
|
||||||
|
(global frame first_person3d.frame)
|
||||||
|
|
@ -121,7 +121,7 @@ pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam) {
|
||||||
if (cam->mode == PXL8_3D_CAMERA_PERSPECTIVE) {
|
if (cam->mode == PXL8_3D_CAMERA_PERSPECTIVE) {
|
||||||
return pxl8_mat4_perspective(cam->fov, cam->aspect, cam->near, cam->far);
|
return pxl8_mat4_perspective(cam->fov, cam->aspect, cam->near, cam->far);
|
||||||
} else {
|
} else {
|
||||||
return pxl8_mat4_ortho(
|
return pxl8_mat4_orthographic(
|
||||||
cam->ortho_left, cam->ortho_right,
|
cam->ortho_left, cam->ortho_right,
|
||||||
cam->ortho_bottom, cam->ortho_top,
|
cam->ortho_bottom, cam->ortho_top,
|
||||||
cam->near, cam->far
|
cam->near, cam->far
|
||||||
|
|
@ -219,9 +219,9 @@ pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, p
|
||||||
|
|
||||||
pxl8_mat4 view = pxl8_3d_camera_get_view(cam);
|
pxl8_mat4 view = pxl8_3d_camera_get_view(cam);
|
||||||
pxl8_mat4 proj = pxl8_3d_camera_get_projection(cam);
|
pxl8_mat4 proj = pxl8_3d_camera_get_projection(cam);
|
||||||
pxl8_mat4 vp = pxl8_mat4_mul(proj, view);
|
pxl8_mat4 vp = pxl8_mat4_multiply(proj, view);
|
||||||
|
|
||||||
pxl8_vec4 clip = pxl8_mat4_mul_vec4(vp, (pxl8_vec4){world_pos.x, world_pos.y, world_pos.z, 1.0f});
|
pxl8_vec4 clip = pxl8_mat4_multiply_vec4(vp, (pxl8_vec4){world_pos.x, world_pos.y, world_pos.z, 1.0f});
|
||||||
|
|
||||||
if (clip.w <= 0.0f) return result;
|
if (clip.w <= 0.0f) return result;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ void pxl8_cpu_destroy(pxl8_cpu_backend* cpu) {
|
||||||
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame) {
|
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame) {
|
||||||
if (!cpu || !frame) return;
|
if (!cpu || !frame) return;
|
||||||
cpu->frame = *frame;
|
cpu->frame = *frame;
|
||||||
cpu->mvp = pxl8_mat4_mul(frame->projection, frame->view);
|
cpu->mvp = pxl8_mat4_multiply(frame->projection, frame->view);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu) {
|
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu) {
|
||||||
|
|
@ -368,8 +368,8 @@ void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8
|
||||||
if (!cpu || !cpu->current_target) return;
|
if (!cpu || !cpu->current_target) return;
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
|
|
||||||
pxl8_vec4 c0 = pxl8_mat4_mul_vec4(cpu->mvp, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
|
pxl8_vec4 c0 = pxl8_mat4_multiply_vec4(cpu->mvp, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
|
||||||
pxl8_vec4 c1 = pxl8_mat4_mul_vec4(cpu->mvp, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
|
pxl8_vec4 c1 = pxl8_mat4_multiply_vec4(cpu->mvp, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
|
||||||
|
|
||||||
if (c0.w <= 0.0f || c1.w <= 0.0f) return;
|
if (c0.w <= 0.0f || c1.w <= 0.0f) return;
|
||||||
|
|
||||||
|
|
@ -1068,8 +1068,7 @@ static void dispatch_triangle(
|
||||||
const pxl8_atlas* textures, const pxl8_gfx_material* material
|
const pxl8_atlas* textures, const pxl8_gfx_material* material
|
||||||
) {
|
) {
|
||||||
if (material->wireframe) {
|
if (material->wireframe) {
|
||||||
u8 color = (material->texture_id > 0) ? (u8)material->texture_id : 15;
|
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, 15, material->double_sided);
|
||||||
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, color, material->double_sided);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1100,8 +1099,8 @@ void pxl8_cpu_draw_mesh(
|
||||||
) {
|
) {
|
||||||
if (!cpu || !mesh || !model || !material || mesh->index_count < 3 || !cpu->current_target) return;
|
if (!cpu || !mesh || !model || !material || mesh->index_count < 3 || !cpu->current_target) return;
|
||||||
|
|
||||||
pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, *model);
|
pxl8_mat4 mv = pxl8_mat4_multiply(cpu->frame.view, *model);
|
||||||
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv);
|
pxl8_mat4 mvp = pxl8_mat4_multiply(cpu->frame.projection, mv);
|
||||||
|
|
||||||
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
|
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
|
||||||
|
|
||||||
|
|
@ -1120,13 +1119,13 @@ void pxl8_cpu_draw_mesh(
|
||||||
pxl8_vec4 p1 = {v1->position.x, v1->position.y, v1->position.z, 1.0f};
|
pxl8_vec4 p1 = {v1->position.x, v1->position.y, v1->position.z, 1.0f};
|
||||||
pxl8_vec4 p2 = {v2->position.x, v2->position.y, v2->position.z, 1.0f};
|
pxl8_vec4 p2 = {v2->position.x, v2->position.y, v2->position.z, 1.0f};
|
||||||
|
|
||||||
vo0.clip_pos = pxl8_mat4_mul_vec4(mvp, p0);
|
vo0.clip_pos = pxl8_mat4_multiply_vec4(mvp, p0);
|
||||||
vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1);
|
vo1.clip_pos = pxl8_mat4_multiply_vec4(mvp, p1);
|
||||||
vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2);
|
vo2.clip_pos = pxl8_mat4_multiply_vec4(mvp, p2);
|
||||||
|
|
||||||
pxl8_vec4 w0 = pxl8_mat4_mul_vec4(*model, p0);
|
pxl8_vec4 w0 = pxl8_mat4_multiply_vec4(*model, p0);
|
||||||
pxl8_vec4 w1 = pxl8_mat4_mul_vec4(*model, p1);
|
pxl8_vec4 w1 = pxl8_mat4_multiply_vec4(*model, p1);
|
||||||
pxl8_vec4 w2 = pxl8_mat4_mul_vec4(*model, p2);
|
pxl8_vec4 w2 = pxl8_mat4_multiply_vec4(*model, p2);
|
||||||
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
||||||
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
||||||
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
||||||
|
|
@ -1144,9 +1143,9 @@ void pxl8_cpu_draw_mesh(
|
||||||
vo2.color = v2->color;
|
vo2.color = v2->color;
|
||||||
|
|
||||||
if (material->dynamic_lighting) {
|
if (material->dynamic_lighting) {
|
||||||
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v0->normal));
|
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v0->normal));
|
||||||
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v1->normal));
|
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v1->normal));
|
||||||
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v2->normal));
|
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v2->normal));
|
||||||
|
|
||||||
pxl8_light_result lr0 = calc_vertex_light(vo0.world_pos, n0, &cpu->frame);
|
pxl8_light_result lr0 = calc_vertex_light(vo0.world_pos, n0, &cpu->frame);
|
||||||
pxl8_light_result lr1 = calc_vertex_light(vo1.world_pos, n1, &cpu->frame);
|
pxl8_light_result lr1 = calc_vertex_light(vo1.world_pos, n1, &cpu->frame);
|
||||||
|
|
@ -1177,44 +1176,6 @@ void pxl8_cpu_draw_mesh(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_cpu_draw_mesh_wireframe(
|
|
||||||
pxl8_cpu_backend* cpu,
|
|
||||||
const pxl8_mesh* mesh,
|
|
||||||
pxl8_mat4 model,
|
|
||||||
u8 color
|
|
||||||
) {
|
|
||||||
if (!cpu || !cpu->current_target || !mesh || mesh->index_count < 3) return;
|
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
|
||||||
|
|
||||||
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, pxl8_mat4_mul(cpu->frame.view, model));
|
|
||||||
|
|
||||||
for (u32 i = 0; i < mesh->index_count; i += 3) {
|
|
||||||
const pxl8_vertex* v0 = &mesh->vertices[mesh->indices[i]];
|
|
||||||
const pxl8_vertex* v1 = &mesh->vertices[mesh->indices[i + 1]];
|
|
||||||
const pxl8_vertex* v2 = &mesh->vertices[mesh->indices[i + 2]];
|
|
||||||
|
|
||||||
pxl8_vec4 c0 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v0->position.x, v0->position.y, v0->position.z, 1.0f});
|
|
||||||
pxl8_vec4 c1 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v1->position.x, v1->position.y, v1->position.z, 1.0f});
|
|
||||||
pxl8_vec4 c2 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v2->position.x, v2->position.y, v2->position.z, 1.0f});
|
|
||||||
|
|
||||||
if (c0.w <= 0.0f || c1.w <= 0.0f || c2.w <= 0.0f) continue;
|
|
||||||
|
|
||||||
f32 hw = (f32)render_target->width * 0.5f;
|
|
||||||
f32 hh = (f32)render_target->height * 0.5f;
|
|
||||||
|
|
||||||
i32 x0 = (i32)(hw + c0.x / c0.w * hw);
|
|
||||||
i32 y0 = (i32)(hh - c0.y / c0.w * hh);
|
|
||||||
i32 x1 = (i32)(hw + c1.x / c1.w * hw);
|
|
||||||
i32 y1 = (i32)(hh - c1.y / c1.w * hh);
|
|
||||||
i32 x2 = (i32)(hw + c2.x / c2.w * hw);
|
|
||||||
i32 y2 = (i32)(hh - c2.y / c2.w * hh);
|
|
||||||
|
|
||||||
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
|
|
||||||
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
|
|
||||||
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu) {
|
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu) {
|
||||||
if (!cpu || cpu->target_stack_depth == 0) return NULL;
|
if (!cpu || cpu->target_stack_depth == 0) return NULL;
|
||||||
return cpu->target_stack[0]->framebuffer;
|
return cpu->target_stack[0]->framebuffer;
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,6 @@ void pxl8_cpu_draw_mesh(
|
||||||
const pxl8_atlas* textures
|
const pxl8_atlas* textures
|
||||||
);
|
);
|
||||||
|
|
||||||
void pxl8_cpu_draw_mesh_wireframe(
|
|
||||||
pxl8_cpu_backend* cpu,
|
|
||||||
const pxl8_mesh* mesh,
|
|
||||||
pxl8_mat4 model,
|
|
||||||
u8 color
|
|
||||||
);
|
|
||||||
|
|
||||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
||||||
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
|
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
|
||||||
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ struct pxl8_gfx {
|
||||||
u32 sprite_cache_capacity;
|
u32 sprite_cache_capacity;
|
||||||
u32 sprite_cache_count;
|
u32 sprite_cache_count;
|
||||||
pxl8_viewport viewport;
|
pxl8_viewport viewport;
|
||||||
|
pxl8_mat4 view_proj;
|
||||||
};
|
};
|
||||||
|
|
||||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||||
|
|
@ -611,8 +612,10 @@ void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8
|
||||||
|
|
||||||
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
||||||
|
|
||||||
pxl8_mat4 vp = pxl8_mat4_mul(frame.projection, frame.view);
|
pxl8_mat4 vp = pxl8_mat4_multiply(frame.projection, frame.view);
|
||||||
|
|
||||||
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||||
|
gfx->view_proj = vp;
|
||||||
|
|
||||||
switch (gfx->backend.type) {
|
switch (gfx->backend.type) {
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
case PXL8_GFX_BACKEND_CPU:
|
||||||
|
|
@ -628,6 +631,38 @@ const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
||||||
return &gfx->frustum;
|
return &gfx->frustum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pxl8_mat4* pxl8_3d_get_view_proj(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx) return NULL;
|
||||||
|
return &gfx->view_proj;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform) {
|
||||||
|
if (!gfx || !in || !out) return 0;
|
||||||
|
|
||||||
|
pxl8_mat4 mvp = transform ? pxl8_mat4_multiply(gfx->view_proj, *transform) : gfx->view_proj;
|
||||||
|
|
||||||
|
f32 hw = (f32)gfx->framebuffer_width * 0.5f;
|
||||||
|
f32 hh = (f32)gfx->framebuffer_height * 0.5f;
|
||||||
|
u32 visible = 0;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < count; i++) {
|
||||||
|
pxl8_vec4 clip = pxl8_mat4_multiply_vec4(mvp, (pxl8_vec4){in[i].x, in[i].y, in[i].z, 1.0f});
|
||||||
|
|
||||||
|
if (clip.w <= 0.0f) {
|
||||||
|
out[i].z = -1.0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 inv_w = 1.0f / clip.w;
|
||||||
|
out[i].x = hw + clip.x * inv_w * hw;
|
||||||
|
out[i].y = hh - clip.y * inv_w * hh;
|
||||||
|
out[i].z = clip.w;
|
||||||
|
visible++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
switch (gfx->backend.type) {
|
||||||
|
|
@ -672,17 +707,6 @@ void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* mo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color) {
|
|
||||||
if (!gfx || !mesh) return;
|
|
||||||
switch (gfx->backend.type) {
|
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_mesh_wireframe(gfx->backend.cpu, mesh, model, color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
switch (gfx->backend.type) {
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,11 @@ void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);
|
||||||
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
|
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
|
||||||
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
||||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);
|
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);
|
||||||
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);
|
|
||||||
void pxl8_3d_end_frame(pxl8_gfx* gfx);
|
void pxl8_3d_end_frame(pxl8_gfx* gfx);
|
||||||
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
|
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
|
||||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
|
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
|
||||||
|
const pxl8_mat4* pxl8_3d_get_view_proj(pxl8_gfx* gfx);
|
||||||
|
u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform);
|
||||||
|
|
||||||
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
|
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,12 @@ typedef enum pxl8_blend_mode {
|
||||||
} pxl8_blend_mode;
|
} pxl8_blend_mode;
|
||||||
|
|
||||||
typedef struct pxl8_gfx_material {
|
typedef struct pxl8_gfx_material {
|
||||||
|
char name[16];
|
||||||
|
pxl8_vec3 u_axis;
|
||||||
|
pxl8_vec3 v_axis;
|
||||||
|
f32 u_offset;
|
||||||
|
f32 v_offset;
|
||||||
|
|
||||||
u32 texture_id;
|
u32 texture_id;
|
||||||
u32 lightmap_id;
|
u32 lightmap_id;
|
||||||
u8 alpha;
|
u8 alpha;
|
||||||
|
|
|
||||||
|
|
@ -86,15 +86,17 @@ pxl8.create_anim_from_ase = anim.Anim.from_ase
|
||||||
pxl8.bounds = math.bounds
|
pxl8.bounds = math.bounds
|
||||||
|
|
||||||
pxl8.Camera3D = gfx3d.Camera3D
|
pxl8.Camera3D = gfx3d.Camera3D
|
||||||
pxl8.create_camera_3d = gfx3d.Camera3D.new
|
pxl8.Mesh = gfx3d.Mesh
|
||||||
pxl8.begin_frame_3d = gfx3d.begin_frame
|
pxl8.begin_frame_3d = gfx3d.begin_frame
|
||||||
pxl8.clear_3d = gfx3d.clear
|
pxl8.clear_3d = gfx3d.clear
|
||||||
pxl8.clear_depth = gfx3d.clear_depth
|
pxl8.clear_depth = gfx3d.clear_depth
|
||||||
|
pxl8.create_camera_3d = gfx3d.Camera3D.new
|
||||||
|
pxl8.create_mesh = gfx3d.Mesh.new
|
||||||
|
pxl8.create_vec3_array = gfx3d.create_vec3_array
|
||||||
pxl8.draw_line_3d = gfx3d.draw_line
|
pxl8.draw_line_3d = gfx3d.draw_line
|
||||||
pxl8.draw_mesh = gfx3d.draw_mesh
|
pxl8.draw_mesh = gfx3d.draw_mesh
|
||||||
pxl8.end_frame_3d = gfx3d.end_frame
|
pxl8.end_frame_3d = gfx3d.end_frame
|
||||||
pxl8.Mesh = gfx3d.Mesh
|
pxl8.project_points = gfx3d.project_points
|
||||||
pxl8.create_mesh = gfx3d.Mesh.new
|
|
||||||
|
|
||||||
pxl8.Compressor = sfx.Compressor
|
pxl8.Compressor = sfx.Compressor
|
||||||
pxl8.create_compressor = sfx.Compressor.new
|
pxl8.create_compressor = sfx.Compressor.new
|
||||||
|
|
@ -111,7 +113,9 @@ pxl8.gui_window = gui.window
|
||||||
pxl8.mat4_identity = math.mat4_identity
|
pxl8.mat4_identity = math.mat4_identity
|
||||||
pxl8.mat4_lookat = math.mat4_lookat
|
pxl8.mat4_lookat = math.mat4_lookat
|
||||||
pxl8.mat4_multiply = math.mat4_multiply
|
pxl8.mat4_multiply = math.mat4_multiply
|
||||||
pxl8.mat4_ortho = math.mat4_ortho
|
pxl8.mat4_multiply_vec3 = math.mat4_multiply_vec3
|
||||||
|
pxl8.mat4_multiply_vec4 = math.mat4_multiply_vec4
|
||||||
|
pxl8.mat4_orthographic = math.mat4_orthographic
|
||||||
pxl8.mat4_perspective = math.mat4_perspective
|
pxl8.mat4_perspective = math.mat4_perspective
|
||||||
pxl8.mat4_rotate_x = math.mat4_rotate_x
|
pxl8.mat4_rotate_x = math.mat4_rotate_x
|
||||||
pxl8.mat4_rotate_y = math.mat4_rotate_y
|
pxl8.mat4_rotate_y = math.mat4_rotate_y
|
||||||
|
|
|
||||||
|
|
@ -216,4 +216,12 @@ function gfx3d.end_frame()
|
||||||
C.pxl8_3d_end_frame(core.gfx)
|
C.pxl8_3d_end_frame(core.gfx)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function gfx3d.project_points(input, output, count, transform)
|
||||||
|
C.pxl8_3d_project_points(core.gfx, input, output, count, transform)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx3d.create_vec3_array(count)
|
||||||
|
return ffi.new("pxl8_vec3[?]", count)
|
||||||
|
end
|
||||||
|
|
||||||
return gfx3d
|
return gfx3d
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,16 @@ function math.mat4_identity()
|
||||||
return C.pxl8_mat4_identity()
|
return C.pxl8_mat4_identity()
|
||||||
end
|
end
|
||||||
|
|
||||||
function math.mat4_mul(a, b)
|
function math.mat4_multiply(a, b)
|
||||||
return C.pxl8_mat4_mul(a, b)
|
return C.pxl8_mat4_multiply(a, b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.mat4_multiply_vec3(m, v)
|
||||||
|
return C.pxl8_mat4_multiply_vec3(m, v)
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.mat4_multiply_vec4(m, v)
|
||||||
|
return C.pxl8_mat4_multiply_vec4(m, v)
|
||||||
end
|
end
|
||||||
|
|
||||||
function math.mat4_translate(x, y, z)
|
function math.mat4_translate(x, y, z)
|
||||||
|
|
@ -35,8 +43,8 @@ function math.mat4_scale(x, y, z)
|
||||||
return C.pxl8_mat4_scale(x, y, z)
|
return C.pxl8_mat4_scale(x, y, z)
|
||||||
end
|
end
|
||||||
|
|
||||||
function math.mat4_ortho(left, right, bottom, top, near, far)
|
function math.mat4_orthographic(left, right, bottom, top, near, far)
|
||||||
return C.pxl8_mat4_ortho(left, right, bottom, top, near, far)
|
return C.pxl8_mat4_orthographic(left, right, bottom, top, near, far)
|
||||||
end
|
end
|
||||||
|
|
||||||
function math.mat4_perspective(fov, aspect, near, far)
|
function math.mat4_perspective(fov, aspect, near, far)
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ function World:resolve_collision(from_x, from_y, from_z, to_x, to_y, to_z, radiu
|
||||||
return result.x, result.y, result.z
|
return result.x, result.y, result.z
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:set_wireframe(enabled, color)
|
function World:set_wireframe(enabled)
|
||||||
C.pxl8_world_set_wireframe(self._ptr, enabled, color or 15)
|
C.pxl8_world_set_wireframe(self._ptr, enabled)
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:unload()
|
function World:unload()
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ pxl8_mat4 pxl8_mat4_identity(void) {
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b) {
|
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
||||||
pxl8_mat4 mat = {0};
|
pxl8_mat4 mat = {0};
|
||||||
|
|
||||||
for (i32 col = 0; col < 4; col++) {
|
for (i32 col = 0; col < 4; col++) {
|
||||||
|
|
@ -126,7 +126,7 @@ pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b) {
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||||
return (pxl8_vec4){
|
return (pxl8_vec4){
|
||||||
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w,
|
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w,
|
||||||
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w,
|
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w,
|
||||||
|
|
@ -135,7 +135,7 @@ pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec3 pxl8_mat4_mul_vec3(pxl8_mat4 m, pxl8_vec3 v) {
|
pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v) {
|
||||||
return (pxl8_vec3){
|
return (pxl8_vec3){
|
||||||
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z,
|
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z,
|
||||||
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z,
|
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z,
|
||||||
|
|
@ -202,7 +202,7 @@ pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
|
pxl8_mat4 pxl8_mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
|
||||||
pxl8_mat4 mat = {0};
|
pxl8_mat4 mat = {0};
|
||||||
|
|
||||||
mat.m[0] = 2.0f / (right - left);
|
mat.m[0] = 2.0f / (right - left);
|
||||||
|
|
@ -255,15 +255,15 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
||||||
pxl8_frustum frustum;
|
pxl8_frustum frustum;
|
||||||
const f32* m = vp.m;
|
const f32* m = vp.m;
|
||||||
|
|
||||||
frustum.planes[0].normal.x = m[3] - m[0];
|
frustum.planes[0].normal.x = m[3] + m[0];
|
||||||
frustum.planes[0].normal.y = m[7] - m[4];
|
frustum.planes[0].normal.y = m[7] + m[4];
|
||||||
frustum.planes[0].normal.z = m[11] - m[8];
|
frustum.planes[0].normal.z = m[11] + m[8];
|
||||||
frustum.planes[0].distance = m[15] - m[12];
|
frustum.planes[0].distance = m[15] + m[12];
|
||||||
|
|
||||||
frustum.planes[1].normal.x = m[3] + m[0];
|
frustum.planes[1].normal.x = m[3] - m[0];
|
||||||
frustum.planes[1].normal.y = m[7] + m[4];
|
frustum.planes[1].normal.y = m[7] - m[4];
|
||||||
frustum.planes[1].normal.z = m[11] + m[8];
|
frustum.planes[1].normal.z = m[11] - m[8];
|
||||||
frustum.planes[1].distance = m[15] + m[12];
|
frustum.planes[1].distance = m[15] - m[12];
|
||||||
|
|
||||||
frustum.planes[2].normal.x = m[3] + m[1];
|
frustum.planes[2].normal.x = m[3] + m[1];
|
||||||
frustum.planes[2].normal.y = m[7] + m[5];
|
frustum.planes[2].normal.y = m[7] + m[5];
|
||||||
|
|
@ -275,15 +275,15 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
||||||
frustum.planes[3].normal.z = m[11] - m[9];
|
frustum.planes[3].normal.z = m[11] - m[9];
|
||||||
frustum.planes[3].distance = m[15] - m[13];
|
frustum.planes[3].distance = m[15] - m[13];
|
||||||
|
|
||||||
frustum.planes[4].normal.x = m[3] - m[2];
|
frustum.planes[4].normal.x = m[3] + m[2];
|
||||||
frustum.planes[4].normal.y = m[7] - m[6];
|
frustum.planes[4].normal.y = m[7] + m[6];
|
||||||
frustum.planes[4].normal.z = m[11] - m[10];
|
frustum.planes[4].normal.z = m[11] + m[10];
|
||||||
frustum.planes[4].distance = m[15] - m[14];
|
frustum.planes[4].distance = m[15] + m[14];
|
||||||
|
|
||||||
frustum.planes[5].normal.x = m[3] + m[2];
|
frustum.planes[5].normal.x = m[3] - m[2];
|
||||||
frustum.planes[5].normal.y = m[7] + m[6];
|
frustum.planes[5].normal.y = m[7] - m[6];
|
||||||
frustum.planes[5].normal.z = m[11] + m[10];
|
frustum.planes[5].normal.z = m[11] - m[10];
|
||||||
frustum.planes[5].distance = m[15] + m[14];
|
frustum.planes[5].distance = m[15] - m[14];
|
||||||
|
|
||||||
for (i32 i = 0; i < 6; i++) {
|
for (i32 i = 0; i < 6; i++) {
|
||||||
f32 len = pxl8_vec3_length(frustum.planes[i].normal);
|
f32 len = pxl8_vec3_length(frustum.planes[i].normal);
|
||||||
|
|
@ -298,19 +298,21 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec3 max) {
|
bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec3 max) {
|
||||||
|
const f32 FRUSTUM_EPSILON = -75.0f;
|
||||||
|
|
||||||
for (i32 i = 0; i < 6; i++) {
|
for (i32 i = 0; i < 6; i++) {
|
||||||
pxl8_vec3 normal = frustum->planes[i].normal;
|
pxl8_vec3 normal = frustum->planes[i].normal;
|
||||||
f32 d = frustum->planes[i].distance;
|
f32 d = frustum->planes[i].distance;
|
||||||
|
|
||||||
pxl8_vec3 p_vertex = {
|
pxl8_vec3 p_vertex = {
|
||||||
(normal.x >= 0.0f) ? max.x : min.x,
|
(normal.x > 0.0f) ? max.x : min.x,
|
||||||
(normal.y >= 0.0f) ? max.y : min.y,
|
(normal.y > 0.0f) ? max.y : min.y,
|
||||||
(normal.z >= 0.0f) ? max.z : min.z
|
(normal.z > 0.0f) ? max.z : min.z
|
||||||
};
|
};
|
||||||
|
|
||||||
f32 p_dist = pxl8_vec3_dot(normal, p_vertex) + d;
|
f32 p_dist = pxl8_vec3_dot(normal, p_vertex) + d;
|
||||||
|
|
||||||
if (p_dist < 0.0f) {
|
if (p_dist < FRUSTUM_EPSILON) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,10 +105,10 @@ pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b);
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_identity(void);
|
pxl8_mat4 pxl8_mat4_identity(void);
|
||||||
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);
|
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);
|
||||||
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b);
|
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);
|
||||||
pxl8_vec3 pxl8_mat4_mul_vec3(pxl8_mat4 m, pxl8_vec3 v);
|
pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v);
|
||||||
pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v);
|
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v);
|
||||||
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
|
pxl8_mat4 pxl8_mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
|
||||||
pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far);
|
pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far);
|
||||||
pxl8_mat4 pxl8_mat4_rotate_x(f32 angle);
|
pxl8_mat4 pxl8_mat4_rotate_x(f32 angle);
|
||||||
pxl8_mat4 pxl8_mat4_rotate_y(f32 angle);
|
pxl8_mat4 pxl8_mat4_rotate_y(f32 angle);
|
||||||
|
|
|
||||||
|
|
@ -256,10 +256,16 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"void pxl8_3d_clear_depth(pxl8_gfx* gfx);\n"
|
"void pxl8_3d_clear_depth(pxl8_gfx* gfx);\n"
|
||||||
"void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u8 color);\n"
|
"void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u8 color);\n"
|
||||||
"void pxl8_3d_end_frame(pxl8_gfx* gfx);\n"
|
"void pxl8_3d_end_frame(pxl8_gfx* gfx);\n"
|
||||||
|
"u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform);\n"
|
||||||
"\n"
|
"\n"
|
||||||
"typedef enum pxl8_blend_mode { PXL8_BLEND_OPAQUE = 0, PXL8_BLEND_ALPHA_TEST, PXL8_BLEND_ALPHA, PXL8_BLEND_ADDITIVE } pxl8_blend_mode;\n"
|
"typedef enum pxl8_blend_mode { PXL8_BLEND_OPAQUE = 0, PXL8_BLEND_ALPHA_TEST, PXL8_BLEND_ALPHA, PXL8_BLEND_ADDITIVE } pxl8_blend_mode;\n"
|
||||||
"\n"
|
"\n"
|
||||||
"typedef struct pxl8_gfx_material {\n"
|
"typedef struct pxl8_gfx_material {\n"
|
||||||
|
" char name[16];\n"
|
||||||
|
" pxl8_vec3 u_axis;\n"
|
||||||
|
" pxl8_vec3 v_axis;\n"
|
||||||
|
" f32 u_offset;\n"
|
||||||
|
" f32 v_offset;\n"
|
||||||
" u32 texture_id;\n"
|
" u32 texture_id;\n"
|
||||||
" u32 lightmap_id;\n"
|
" u32 lightmap_id;\n"
|
||||||
" u8 alpha;\n"
|
" u8 alpha;\n"
|
||||||
|
|
@ -298,14 +304,15 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v);\n"
|
"u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v);\n"
|
||||||
"void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2);\n"
|
"void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2);\n"
|
||||||
"void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);\n"
|
"void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);\n"
|
||||||
"void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"u32 pxl8_hash32(u32 x);\n"
|
"u32 pxl8_hash32(u32 x);\n"
|
||||||
"\n"
|
"\n"
|
||||||
"pxl8_mat4 pxl8_mat4_identity(void);\n"
|
"pxl8_mat4 pxl8_mat4_identity(void);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);\n"
|
"pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b);\n"
|
"pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_ortho(float left, float right, float bottom, float top, float near, float far);\n"
|
"pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v);\n"
|
||||||
|
"pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v);\n"
|
||||||
|
"pxl8_mat4 pxl8_mat4_orthographic(float left, float right, float bottom, float top, float near, float far);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_perspective(float fov, float aspect, float near, float far);\n"
|
"pxl8_mat4 pxl8_mat4_perspective(float fov, float aspect, float near, float far);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_rotate_x(float angle);\n"
|
"pxl8_mat4 pxl8_mat4_rotate_x(float angle);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_rotate_y(float angle);\n"
|
"pxl8_mat4 pxl8_mat4_rotate_y(float angle);\n"
|
||||||
|
|
@ -421,7 +428,7 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"bool pxl8_world_is_loaded(const pxl8_world* world);\n"
|
"bool pxl8_world_is_loaded(const pxl8_world* world);\n"
|
||||||
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
||||||
"pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
|
"pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
|
||||||
"void pxl8_world_set_wireframe(pxl8_world* world, bool enabled, u8 color);\n"
|
"void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);\n"
|
||||||
"\n"
|
"\n"
|
||||||
"typedef struct { i32 cursor_x; i32 cursor_y; bool cursor_down; bool cursor_clicked; u32 hot_id; u32 active_id; } pxl8_gui_state;\n"
|
"typedef struct { i32 cursor_x; i32 cursor_y; bool cursor_down; bool cursor_clicked; u32 hot_id; u32 active_id; } pxl8_gui_state;\n"
|
||||||
"pxl8_gui_state* pxl8_gui_state_create(void);\n"
|
"pxl8_gui_state* pxl8_gui_state_create(void);\n"
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,20 @@ typedef struct {
|
||||||
pxl8_bsp_chunk chunks[CHUNK_COUNT];
|
pxl8_bsp_chunk chunks[CHUNK_COUNT];
|
||||||
} pxl8_bsp_header;
|
} pxl8_bsp_header;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
f32 x0, y0, x1, y1;
|
||||||
|
} screen_rect;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 leaf;
|
||||||
|
screen_rect window;
|
||||||
|
} portal_queue_entry;
|
||||||
|
|
||||||
static inline pxl8_vec3 read_vec3(pxl8_stream* stream) {
|
static inline pxl8_vec3 read_vec3(pxl8_stream* stream) {
|
||||||
pxl8_vec3 v;
|
f32 x = pxl8_read_f32(stream);
|
||||||
v.x = pxl8_read_f32(stream);
|
f32 y = pxl8_read_f32(stream);
|
||||||
v.y = pxl8_read_f32(stream);
|
f32 z = pxl8_read_f32(stream);
|
||||||
v.z = pxl8_read_f32(stream);
|
return (pxl8_vec3){x, z, y};
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool validate_chunk(const pxl8_bsp_chunk* chunk, u32 element_size, size_t file_size) {
|
static bool validate_chunk(const pxl8_bsp_chunk* chunk, u32 element_size, size_t file_size) {
|
||||||
|
|
@ -75,25 +83,6 @@ static inline bool pxl8_bsp_get_edge_vertex(const pxl8_bsp* bsp, i32 surfedge_id
|
||||||
return *out_vert_idx < bsp->num_vertices;
|
return *out_vert_idx < bsp->num_vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool pxl8_bsp_get_edge_vertices(const pxl8_bsp* bsp, i32 surfedge_idx, u32* out_v0_idx, u32* out_v1_idx) {
|
|
||||||
if (surfedge_idx >= (i32)bsp->num_surfedges) return false;
|
|
||||||
|
|
||||||
i32 edge_idx = bsp->surfedges[surfedge_idx];
|
|
||||||
|
|
||||||
if (edge_idx >= 0) {
|
|
||||||
if ((u32)edge_idx >= bsp->num_edges) return false;
|
|
||||||
*out_v0_idx = bsp->edges[edge_idx].vertex[0];
|
|
||||||
*out_v1_idx = bsp->edges[edge_idx].vertex[1];
|
|
||||||
} else {
|
|
||||||
edge_idx = -edge_idx;
|
|
||||||
if ((u32)edge_idx >= bsp->num_edges) return false;
|
|
||||||
*out_v0_idx = bsp->edges[edge_idx].vertex[1];
|
|
||||||
*out_v1_idx = bsp->edges[edge_idx].vertex[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return *out_v0_idx < bsp->num_vertices && *out_v1_idx < bsp->num_vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||||
if (!path || !bsp) return PXL8_ERROR_INVALID_ARGUMENT;
|
if (!path || !bsp) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
|
@ -179,16 +168,20 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||||
|
|
||||||
chunk = &header.chunks[CHUNK_TEXINFO];
|
chunk = &header.chunks[CHUNK_TEXINFO];
|
||||||
if (!validate_chunk(chunk, 40, file_size)) goto error_cleanup;
|
if (!validate_chunk(chunk, 40, file_size)) goto error_cleanup;
|
||||||
bsp->num_texinfo = chunk->size / 40;
|
bsp->num_materials = chunk->size / 40;
|
||||||
if (bsp->num_texinfo > 0) {
|
if (bsp->num_materials > 0) {
|
||||||
bsp->texinfo = calloc(bsp->num_texinfo, sizeof(pxl8_bsp_texinfo));
|
bsp->materials = calloc(bsp->num_materials, sizeof(pxl8_gfx_material));
|
||||||
pxl8_stream_seek(&stream, chunk->offset);
|
pxl8_stream_seek(&stream, chunk->offset);
|
||||||
for (u32 i = 0; i < bsp->num_texinfo; i++) {
|
for (u32 i = 0; i < bsp->num_materials; i++) {
|
||||||
bsp->texinfo[i].u_axis = read_vec3(&stream);
|
bsp->materials[i].u_axis = read_vec3(&stream);
|
||||||
bsp->texinfo[i].u_offset = pxl8_read_f32(&stream);
|
bsp->materials[i].u_offset = pxl8_read_f32(&stream);
|
||||||
bsp->texinfo[i].v_axis = read_vec3(&stream);
|
bsp->materials[i].v_axis = read_vec3(&stream);
|
||||||
bsp->texinfo[i].v_offset = pxl8_read_f32(&stream);
|
bsp->materials[i].v_offset = pxl8_read_f32(&stream);
|
||||||
bsp->texinfo[i].miptex = pxl8_read_u32(&stream);
|
bsp->materials[i].texture_id = pxl8_read_u32(&stream);
|
||||||
|
bsp->materials[i].alpha = 255;
|
||||||
|
bsp->materials[i].dither = true;
|
||||||
|
bsp->materials[i].dynamic_lighting = true;
|
||||||
|
bsp->materials[i].double_sided = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,7 +196,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||||
bsp->faces[i].side = pxl8_read_u16(&stream);
|
bsp->faces[i].side = pxl8_read_u16(&stream);
|
||||||
bsp->faces[i].first_edge = pxl8_read_u32(&stream);
|
bsp->faces[i].first_edge = pxl8_read_u32(&stream);
|
||||||
bsp->faces[i].num_edges = pxl8_read_u16(&stream);
|
bsp->faces[i].num_edges = pxl8_read_u16(&stream);
|
||||||
bsp->faces[i].texinfo_id = pxl8_read_u16(&stream);
|
bsp->faces[i].material_id = pxl8_read_u16(&stream);
|
||||||
bsp->faces[i].styles[0] = pxl8_read_u8(&stream);
|
bsp->faces[i].styles[0] = pxl8_read_u8(&stream);
|
||||||
bsp->faces[i].styles[1] = pxl8_read_u8(&stream);
|
bsp->faces[i].styles[1] = pxl8_read_u8(&stream);
|
||||||
bsp->faces[i].styles[2] = pxl8_read_u8(&stream);
|
bsp->faces[i].styles[2] = pxl8_read_u8(&stream);
|
||||||
|
|
@ -225,8 +218,18 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||||
bsp->nodes[i].plane_id = pxl8_read_u32(&stream);
|
bsp->nodes[i].plane_id = pxl8_read_u32(&stream);
|
||||||
bsp->nodes[i].children[0] = pxl8_read_i16(&stream);
|
bsp->nodes[i].children[0] = pxl8_read_i16(&stream);
|
||||||
bsp->nodes[i].children[1] = pxl8_read_i16(&stream);
|
bsp->nodes[i].children[1] = pxl8_read_i16(&stream);
|
||||||
for (u32 j = 0; j < 3; j++) bsp->nodes[i].mins[j] = pxl8_read_i16(&stream);
|
i16 nx = pxl8_read_i16(&stream);
|
||||||
for (u32 j = 0; j < 3; j++) bsp->nodes[i].maxs[j] = pxl8_read_i16(&stream);
|
i16 ny = pxl8_read_i16(&stream);
|
||||||
|
i16 nz = pxl8_read_i16(&stream);
|
||||||
|
bsp->nodes[i].mins[0] = nx;
|
||||||
|
bsp->nodes[i].mins[1] = nz;
|
||||||
|
bsp->nodes[i].mins[2] = ny;
|
||||||
|
i16 mx = pxl8_read_i16(&stream);
|
||||||
|
i16 my = pxl8_read_i16(&stream);
|
||||||
|
i16 mz = pxl8_read_i16(&stream);
|
||||||
|
bsp->nodes[i].maxs[0] = mx;
|
||||||
|
bsp->nodes[i].maxs[1] = mz;
|
||||||
|
bsp->nodes[i].maxs[2] = my;
|
||||||
bsp->nodes[i].first_face = pxl8_read_u16(&stream);
|
bsp->nodes[i].first_face = pxl8_read_u16(&stream);
|
||||||
bsp->nodes[i].num_faces = pxl8_read_u16(&stream);
|
bsp->nodes[i].num_faces = pxl8_read_u16(&stream);
|
||||||
}
|
}
|
||||||
|
|
@ -241,8 +244,18 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||||
for (u32 i = 0; i < bsp->num_leafs; i++) {
|
for (u32 i = 0; i < bsp->num_leafs; i++) {
|
||||||
bsp->leafs[i].contents = pxl8_read_i32(&stream);
|
bsp->leafs[i].contents = pxl8_read_i32(&stream);
|
||||||
bsp->leafs[i].visofs = pxl8_read_i32(&stream);
|
bsp->leafs[i].visofs = pxl8_read_i32(&stream);
|
||||||
for (u32 j = 0; j < 3; j++) bsp->leafs[i].mins[j] = pxl8_read_i16(&stream);
|
i16 nx = pxl8_read_i16(&stream);
|
||||||
for (u32 j = 0; j < 3; j++) bsp->leafs[i].maxs[j] = pxl8_read_i16(&stream);
|
i16 ny = pxl8_read_i16(&stream);
|
||||||
|
i16 nz = pxl8_read_i16(&stream);
|
||||||
|
bsp->leafs[i].mins[0] = nx;
|
||||||
|
bsp->leafs[i].mins[1] = nz;
|
||||||
|
bsp->leafs[i].mins[2] = ny;
|
||||||
|
i16 mx = pxl8_read_i16(&stream);
|
||||||
|
i16 my = pxl8_read_i16(&stream);
|
||||||
|
i16 mz = pxl8_read_i16(&stream);
|
||||||
|
bsp->leafs[i].maxs[0] = mx;
|
||||||
|
bsp->leafs[i].maxs[1] = mz;
|
||||||
|
bsp->leafs[i].maxs[2] = my;
|
||||||
bsp->leafs[i].first_marksurface = pxl8_read_u16(&stream);
|
bsp->leafs[i].first_marksurface = pxl8_read_u16(&stream);
|
||||||
bsp->leafs[i].num_marksurfaces = pxl8_read_u16(&stream);
|
bsp->leafs[i].num_marksurfaces = pxl8_read_u16(&stream);
|
||||||
for (u32 j = 0; j < 4; j++) bsp->leafs[i].ambient_level[j] = pxl8_read_u8(&stream);
|
for (u32 j = 0; j < 4; j++) bsp->leafs[i].ambient_level[j] = pxl8_read_u8(&stream);
|
||||||
|
|
@ -267,8 +280,18 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||||
bsp->models = calloc(bsp->num_models, sizeof(pxl8_bsp_model));
|
bsp->models = calloc(bsp->num_models, sizeof(pxl8_bsp_model));
|
||||||
pxl8_stream_seek(&stream, chunk->offset);
|
pxl8_stream_seek(&stream, chunk->offset);
|
||||||
for (u32 i = 0; i < bsp->num_models; i++) {
|
for (u32 i = 0; i < bsp->num_models; i++) {
|
||||||
for (u32 j = 0; j < 3; j++) bsp->models[i].mins[j] = pxl8_read_f32(&stream);
|
f32 minx = pxl8_read_f32(&stream);
|
||||||
for (u32 j = 0; j < 3; j++) bsp->models[i].maxs[j] = pxl8_read_f32(&stream);
|
f32 miny = pxl8_read_f32(&stream);
|
||||||
|
f32 minz = pxl8_read_f32(&stream);
|
||||||
|
bsp->models[i].mins[0] = minx;
|
||||||
|
bsp->models[i].mins[1] = minz;
|
||||||
|
bsp->models[i].mins[2] = miny;
|
||||||
|
f32 maxx = pxl8_read_f32(&stream);
|
||||||
|
f32 maxy = pxl8_read_f32(&stream);
|
||||||
|
f32 maxz = pxl8_read_f32(&stream);
|
||||||
|
bsp->models[i].maxs[0] = maxx;
|
||||||
|
bsp->models[i].maxs[1] = maxz;
|
||||||
|
bsp->models[i].maxs[2] = maxy;
|
||||||
bsp->models[i].origin = read_vec3(&stream);
|
bsp->models[i].origin = read_vec3(&stream);
|
||||||
for (u32 j = 0; j < 4; j++) bsp->models[i].headnode[j] = pxl8_read_i32(&stream);
|
for (u32 j = 0; j < 4; j++) bsp->models[i].headnode[j] = pxl8_read_i32(&stream);
|
||||||
bsp->models[i].visleafs = pxl8_read_i32(&stream);
|
bsp->models[i].visleafs = pxl8_read_i32(&stream);
|
||||||
|
|
@ -335,16 +358,18 @@ error_cleanup:
|
||||||
void pxl8_bsp_destroy(pxl8_bsp* bsp) {
|
void pxl8_bsp_destroy(pxl8_bsp* bsp) {
|
||||||
if (!bsp) return;
|
if (!bsp) return;
|
||||||
|
|
||||||
|
free(bsp->cell_portals);
|
||||||
free(bsp->edges);
|
free(bsp->edges);
|
||||||
free(bsp->faces);
|
free(bsp->faces);
|
||||||
free(bsp->leafs);
|
free(bsp->leafs);
|
||||||
free(bsp->lightdata);
|
free(bsp->lightdata);
|
||||||
free(bsp->marksurfaces);
|
free(bsp->marksurfaces);
|
||||||
|
free(bsp->materials);
|
||||||
free(bsp->models);
|
free(bsp->models);
|
||||||
free(bsp->nodes);
|
free(bsp->nodes);
|
||||||
free(bsp->planes);
|
free(bsp->planes);
|
||||||
|
free(bsp->render_face_flags);
|
||||||
free(bsp->surfedges);
|
free(bsp->surfedges);
|
||||||
free(bsp->texinfo);
|
|
||||||
free(bsp->vertex_lights);
|
free(bsp->vertex_lights);
|
||||||
free(bsp->vertices);
|
free(bsp->vertices);
|
||||||
free(bsp->visdata);
|
free(bsp->visdata);
|
||||||
|
|
@ -376,66 +401,71 @@ bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to) {
|
||||||
i32 visofs = bsp->leafs[leaf_from].visofs;
|
i32 visofs = bsp->leafs[leaf_from].visofs;
|
||||||
if (visofs < 0) return true;
|
if (visofs < 0) return true;
|
||||||
|
|
||||||
u32 target_byte = leaf_to >> 3;
|
u32 row_size = (bsp->num_leafs + 7) >> 3;
|
||||||
u32 target_bit = leaf_to & 7;
|
u32 byte_idx = (u32)leaf_to >> 3;
|
||||||
|
u32 bit_idx = (u32)leaf_to & 7;
|
||||||
|
|
||||||
u32 pos = (u32)visofs;
|
u8* vis = bsp->visdata + visofs;
|
||||||
u32 out_pos = 0;
|
u8* vis_end = bsp->visdata + bsp->visdata_size;
|
||||||
|
u32 out = 0;
|
||||||
|
|
||||||
while (out_pos <= target_byte && pos < bsp->visdata_size) {
|
while (out < row_size && vis < vis_end) {
|
||||||
u8 b = bsp->visdata[pos++];
|
if (*vis) {
|
||||||
if (b != 0) {
|
if (out == byte_idx) {
|
||||||
if (out_pos == target_byte) {
|
return (*vis & (1 << bit_idx)) != 0;
|
||||||
return (b & (1 << target_bit)) != 0;
|
|
||||||
}
|
}
|
||||||
out_pos++;
|
out++;
|
||||||
|
vis++;
|
||||||
} else {
|
} else {
|
||||||
if (pos >= bsp->visdata_size) break;
|
vis++;
|
||||||
u32 count = bsp->visdata[pos++];
|
if (vis >= vis_end) break;
|
||||||
if (out_pos + count > target_byte) {
|
u32 count = *vis++;
|
||||||
|
if (out + count > byte_idx && byte_idx >= out) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_pos += count;
|
out += count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return out > byte_idx ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_bsp_pvs pxl8_bsp_decompress_pvs(const pxl8_bsp* bsp, i32 leaf) {
|
pxl8_bsp_pvs pxl8_bsp_decompress_pvs(const pxl8_bsp* bsp, i32 leaf) {
|
||||||
pxl8_bsp_pvs pvs = {0};
|
pxl8_bsp_pvs pvs = {0};
|
||||||
|
|
||||||
u32 pvs_size = (bsp->num_leafs + 7) / 8;
|
u32 row = (bsp->num_leafs + 7) >> 3;
|
||||||
pvs.data = calloc(pvs_size, 1);
|
pvs.data = malloc(row);
|
||||||
pvs.size = pvs_size;
|
pvs.size = row;
|
||||||
|
|
||||||
if (!pvs.data) return pvs;
|
if (!pvs.data) return pvs;
|
||||||
|
|
||||||
if (!bsp || leaf < 0 || (u32)leaf >= bsp->num_leafs) {
|
if (!bsp || leaf < 0 || (u32)leaf >= bsp->num_leafs) {
|
||||||
memset(pvs.data, 0xFF, pvs_size);
|
memset(pvs.data, 0xFF, row);
|
||||||
return pvs;
|
return pvs;
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 visofs = bsp->leafs[leaf].visofs;
|
i32 visofs = bsp->leafs[leaf].visofs;
|
||||||
if (visofs < 0 || !bsp->visdata || bsp->visdata_size == 0) {
|
if (visofs < 0 || !bsp->visdata || bsp->visdata_size == 0) {
|
||||||
memset(pvs.data, 0xFF, pvs_size);
|
memset(pvs.data, 0xFF, row);
|
||||||
return pvs;
|
return pvs;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 pos = (u32)visofs;
|
u8* in = bsp->visdata + visofs;
|
||||||
u32 out = 0;
|
u8* out = pvs.data;
|
||||||
|
u8* out_end = pvs.data + row;
|
||||||
|
|
||||||
while (out < pvs_size && pos < bsp->visdata_size) {
|
do {
|
||||||
u8 b = bsp->visdata[pos++];
|
if (*in) {
|
||||||
|
*out++ = *in++;
|
||||||
if (b != 0) {
|
|
||||||
pvs.data[out++] = b;
|
|
||||||
} else {
|
} else {
|
||||||
if (pos >= bsp->visdata_size) break;
|
in++;
|
||||||
u32 count = bsp->visdata[pos++];
|
i32 c = *in++;
|
||||||
out += count;
|
while (c > 0 && out < out_end) {
|
||||||
|
*out++ = 0;
|
||||||
|
c--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} while (out < out_end);
|
||||||
|
|
||||||
return pvs;
|
return pvs;
|
||||||
}
|
}
|
||||||
|
|
@ -449,10 +479,10 @@ void pxl8_bsp_pvs_destroy(pxl8_bsp_pvs* pvs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pxl8_bsp_pvs_is_visible(const pxl8_bsp_pvs* pvs, i32 leaf) {
|
bool pxl8_bsp_pvs_is_visible(const pxl8_bsp_pvs* pvs, i32 leaf) {
|
||||||
if (!pvs || !pvs->data || leaf < 0) return false;
|
if (!pvs || !pvs->data || leaf < 0) return true;
|
||||||
u32 byte_idx = leaf >> 3;
|
u32 byte_idx = (u32)leaf >> 3;
|
||||||
u32 bit_idx = leaf & 7;
|
u32 bit_idx = (u32)leaf & 7;
|
||||||
if (byte_idx >= pvs->size) return false;
|
if (byte_idx >= pvs->size) return true;
|
||||||
return (pvs->data[byte_idx] & (1 << bit_idx)) != 0;
|
return (pvs->data[byte_idx] & (1 << bit_idx)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,6 +581,83 @@ static inline bool leaf_in_frustum(const pxl8_bsp_leaf* leaf, const pxl8_frustum
|
||||||
return pxl8_frustum_test_aabb(frustum, mins, maxs);
|
return pxl8_frustum_test_aabb(frustum, mins, maxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool screen_rect_valid(screen_rect r) {
|
||||||
|
return r.x0 < r.x1 && r.y0 < r.y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline screen_rect screen_rect_intersect(screen_rect a, screen_rect b) {
|
||||||
|
return (screen_rect){
|
||||||
|
.x0 = (a.x0 > b.x0) ? a.x0 : b.x0,
|
||||||
|
.y0 = (a.y0 > b.y0) ? a.y0 : b.y0,
|
||||||
|
.x1 = (a.x1 < b.x1) ? a.x1 : b.x1,
|
||||||
|
.y1 = (a.y1 < b.y1) ? a.y1 : b.y1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void expand_rect_with_ndc(screen_rect* r, f32 nx, f32 ny) {
|
||||||
|
if (nx < r->x0) r->x0 = nx;
|
||||||
|
if (nx > r->x1) r->x1 = nx;
|
||||||
|
if (ny < r->y0) r->y0 = ny;
|
||||||
|
if (ny > r->y1) r->y1 = ny;
|
||||||
|
}
|
||||||
|
|
||||||
|
static screen_rect project_portal_to_screen(const pxl8_bsp_portal* portal, const pxl8_mat4* vp, f32 wall_height) {
|
||||||
|
pxl8_vec3 world_corners[4] = {
|
||||||
|
{portal->x0, 0, portal->z0},
|
||||||
|
{portal->x1, 0, portal->z1},
|
||||||
|
{portal->x1, wall_height, portal->z1},
|
||||||
|
{portal->x0, wall_height, portal->z0},
|
||||||
|
};
|
||||||
|
|
||||||
|
pxl8_vec4 clip[4];
|
||||||
|
bool in_front[4];
|
||||||
|
i32 front_count = 0;
|
||||||
|
|
||||||
|
const f32 NEAR_W = 0.001f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
clip[i] = pxl8_mat4_multiply_vec4(*vp, (pxl8_vec4){world_corners[i].x, world_corners[i].y, world_corners[i].z, 1.0f});
|
||||||
|
in_front[i] = clip[i].w > NEAR_W;
|
||||||
|
if (in_front[i]) front_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (front_count == 0) return (screen_rect){0, 0, 0, 0};
|
||||||
|
|
||||||
|
screen_rect result = {1e30f, 1e30f, -1e30f, -1e30f};
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
i32 j = (i + 1) % 4;
|
||||||
|
pxl8_vec4 a = clip[i];
|
||||||
|
pxl8_vec4 b = clip[j];
|
||||||
|
bool a_in = in_front[i];
|
||||||
|
bool b_in = in_front[j];
|
||||||
|
|
||||||
|
if (a_in) {
|
||||||
|
f32 inv_w = 1.0f / a.w;
|
||||||
|
expand_rect_with_ndc(&result, a.x * inv_w, a.y * inv_w);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a_in != b_in) {
|
||||||
|
f32 t = (NEAR_W - a.w) / (b.w - a.w);
|
||||||
|
pxl8_vec4 intersection = {
|
||||||
|
a.x + t * (b.x - a.x),
|
||||||
|
a.y + t * (b.y - a.y),
|
||||||
|
a.z + t * (b.z - a.z),
|
||||||
|
NEAR_W
|
||||||
|
};
|
||||||
|
f32 inv_w = 1.0f / intersection.w;
|
||||||
|
expand_rect_with_ndc(&result, intersection.x * inv_w, intersection.y * inv_w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.x0 < -1.0f) result.x0 = -1.0f;
|
||||||
|
if (result.y0 < -1.0f) result.y0 = -1.0f;
|
||||||
|
if (result.x1 > 1.0f) result.x1 = 1.0f;
|
||||||
|
if (result.y1 > 1.0f) result.y1 = 1.0f;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void collect_face_to_mesh(
|
static void collect_face_to_mesh(
|
||||||
const pxl8_bsp* bsp,
|
const pxl8_bsp* bsp,
|
||||||
u32 face_id,
|
u32 face_id,
|
||||||
|
|
@ -569,17 +676,18 @@ static void collect_face_to_mesh(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pxl8_bsp_texinfo* texinfo = NULL;
|
const pxl8_gfx_material* material = NULL;
|
||||||
f32 tex_scale = 64.0f;
|
f32 tex_scale = 64.0f;
|
||||||
if (face->texinfo_id < bsp->num_texinfo) {
|
if (face->material_id < bsp->num_materials) {
|
||||||
texinfo = &bsp->texinfo[face->texinfo_id];
|
material = &bsp->materials[face->material_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 base_idx = (u16)mesh->vertex_count;
|
u16 base_idx = (u16)mesh->vertex_count;
|
||||||
u32 num_verts = 0;
|
u32 num_verts = 0;
|
||||||
|
|
||||||
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
|
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
|
||||||
i32 surfedge_idx = face->first_edge + i;
|
u32 edge_i = face->side ? (face->num_edges - 1 - i) : i;
|
||||||
|
i32 surfedge_idx = face->first_edge + edge_i;
|
||||||
u32 vert_idx;
|
u32 vert_idx;
|
||||||
|
|
||||||
if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) {
|
if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) {
|
||||||
|
|
@ -589,9 +697,9 @@ static void collect_face_to_mesh(
|
||||||
pxl8_vec3 pos = bsp->vertices[vert_idx].position;
|
pxl8_vec3 pos = bsp->vertices[vert_idx].position;
|
||||||
|
|
||||||
f32 u = 0.0f, v = 0.0f;
|
f32 u = 0.0f, v = 0.0f;
|
||||||
if (texinfo) {
|
if (material) {
|
||||||
u = (pxl8_vec3_dot(pos, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
u = (pxl8_vec3_dot(pos, material->u_axis) + material->u_offset) / tex_scale;
|
||||||
v = (pxl8_vec3_dot(pos, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
v = (pxl8_vec3_dot(pos, material->v_axis) + material->v_offset) / tex_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 light = 255;
|
u8 light = 255;
|
||||||
|
|
@ -618,8 +726,8 @@ static void collect_face_to_mesh(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) {
|
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const pxl8_gfx_material* material) {
|
||||||
if (!gfx || !bsp || face_id >= bsp->num_faces) return;
|
if (!gfx || !bsp || face_id >= bsp->num_faces || !material) return;
|
||||||
|
|
||||||
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
||||||
if (!mesh) return;
|
if (!mesh) return;
|
||||||
|
|
@ -628,172 +736,115 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 t
|
||||||
|
|
||||||
if (mesh->index_count > 0) {
|
if (mesh->index_count > 0) {
|
||||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
pxl8_gfx_material mat = {
|
pxl8_3d_draw_mesh(gfx, mesh, &identity, material);
|
||||||
.texture_id = texture_id,
|
|
||||||
.alpha = 255,
|
|
||||||
.dither = true,
|
|
||||||
.dynamic_lighting = true,
|
|
||||||
};
|
|
||||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mesh_destroy(mesh);
|
pxl8_mesh_destroy(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos) {
|
void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos) {
|
||||||
static int call_count = 0;
|
if (!gfx || !bsp || bsp->num_faces == 0) return;
|
||||||
if (!gfx || !bsp || bsp->num_faces == 0) {
|
if (!bsp->cell_portals || bsp->num_cell_portals == 0) return;
|
||||||
if (call_count++ < 5) {
|
if (!bsp->materials || bsp->num_materials == 0) return;
|
||||||
pxl8_debug("bsp_render_textured: early return - gfx=%p, bsp=%p, num_faces=%u",
|
|
||||||
(void*)gfx, (void*)bsp, bsp ? bsp->num_faces : 0);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||||
if (!frustum) {
|
const pxl8_mat4* vp = pxl8_3d_get_view_proj(gfx);
|
||||||
if (call_count++ < 5) {
|
if (!frustum || !vp) return;
|
||||||
pxl8_debug("bsp_render_textured: frustum is NULL!");
|
|
||||||
}
|
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||||
|
if (camera_leaf < 0 || (u32)camera_leaf >= bsp->num_cell_portals) return;
|
||||||
|
|
||||||
|
pxl8_bsp* bsp_mut = (pxl8_bsp*)bsp;
|
||||||
|
if (!bsp_mut->render_face_flags) {
|
||||||
|
bsp_mut->render_face_flags = calloc(bsp->num_faces, 1);
|
||||||
|
if (!bsp_mut->render_face_flags) return;
|
||||||
|
}
|
||||||
|
memset(bsp_mut->render_face_flags, 0, bsp->num_faces);
|
||||||
|
|
||||||
|
pxl8_bsp_pvs pvs = pxl8_bsp_decompress_pvs(bsp, camera_leaf);
|
||||||
|
|
||||||
|
u32 visited_bytes = (bsp->num_leafs + 7) / 8;
|
||||||
|
u8* visited = calloc(visited_bytes, 1);
|
||||||
|
screen_rect* cell_windows = calloc(bsp->num_leafs, sizeof(screen_rect));
|
||||||
|
portal_queue_entry* queue = malloc(bsp->num_leafs * 4 * sizeof(portal_queue_entry));
|
||||||
|
if (!visited || !cell_windows || !queue) {
|
||||||
|
free(visited);
|
||||||
|
free(cell_windows);
|
||||||
|
free(queue);
|
||||||
|
pxl8_bsp_pvs_destroy(&pvs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
u32 head = 0, tail = 0;
|
||||||
|
screen_rect full_screen = {-1.0f, -1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
static u32 debug_counter = 0;
|
visited[camera_leaf >> 3] |= (1 << (camera_leaf & 7));
|
||||||
bool do_debug = (debug_counter++ % 300 == 0);
|
cell_windows[camera_leaf] = full_screen;
|
||||||
u32 visible_leafs = 0;
|
queue[tail++] = (portal_queue_entry){camera_leaf, full_screen};
|
||||||
u32 visible_faces = 0;
|
|
||||||
u32 total_faces_in_visible_leafs = 0;
|
|
||||||
static bool dumped_camera_leaf = false;
|
|
||||||
if (do_debug) {
|
|
||||||
bool self_visible = (camera_leaf < 0) || pxl8_bsp_is_leaf_visible(bsp, camera_leaf, camera_leaf);
|
|
||||||
if (!self_visible) {
|
|
||||||
pxl8_debug("WARNING: Camera leaf %d is NOT visible from itself!", camera_leaf);
|
|
||||||
}
|
|
||||||
if (!dumped_camera_leaf && camera_leaf >= 0) {
|
|
||||||
const pxl8_bsp_leaf* cl = &bsp->leafs[camera_leaf];
|
|
||||||
pxl8_debug("Camera leaf %d: contents=%d, marksurfaces=%u-%u (%u faces)",
|
|
||||||
camera_leaf, cl->contents, cl->first_marksurface,
|
|
||||||
cl->first_marksurface + cl->num_marksurfaces, cl->num_marksurfaces);
|
|
||||||
dumped_camera_leaf = true;
|
|
||||||
}
|
|
||||||
for (u32 i = 0; i < bsp->num_leafs; i++) {
|
|
||||||
if (camera_leaf < 0 || pxl8_bsp_is_leaf_visible(bsp, camera_leaf, i)) {
|
|
||||||
visible_leafs++;
|
|
||||||
total_faces_in_visible_leafs += bsp->leafs[i].num_marksurfaces;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8* rendered_faces = NULL;
|
f32 wall_height = 128.0f;
|
||||||
static u32 rendered_faces_capacity = 0;
|
|
||||||
|
|
||||||
if (rendered_faces_capacity < bsp->num_faces) {
|
while (head < tail) {
|
||||||
u8* new_buffer = realloc(rendered_faces, bsp->num_faces);
|
portal_queue_entry entry = queue[head++];
|
||||||
if (!new_buffer) return;
|
u32 leaf_id = entry.leaf;
|
||||||
rendered_faces = new_buffer;
|
screen_rect window = entry.window;
|
||||||
rendered_faces_capacity = bsp->num_faces;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(rendered_faces, 0, bsp->num_faces);
|
if (leaf_id >= bsp->num_cell_portals) continue;
|
||||||
|
const pxl8_bsp_cell_portals* cp = &bsp->cell_portals[leaf_id];
|
||||||
|
|
||||||
pxl8_mesh* mesh = pxl8_mesh_create(8192, 16384);
|
for (u8 i = 0; i < cp->num_portals; i++) {
|
||||||
if (!mesh) return;
|
const pxl8_bsp_portal* portal = &cp->portals[i];
|
||||||
|
u32 target = portal->target_leaf;
|
||||||
|
|
||||||
u32 current_texture = 0xFFFFFFFF;
|
if (target >= bsp->num_leafs) continue;
|
||||||
|
if (bsp->leafs[target].contents == -1) continue;
|
||||||
|
if (!pxl8_bsp_pvs_is_visible(&pvs, target)) continue;
|
||||||
|
|
||||||
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) {
|
screen_rect portal_rect = project_portal_to_screen(portal, vp, wall_height);
|
||||||
if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
|
if (!screen_rect_valid(portal_rect)) continue;
|
||||||
|
|
||||||
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
|
screen_rect new_window = screen_rect_intersect(window, portal_rect);
|
||||||
|
if (!screen_rect_valid(new_window)) continue;
|
||||||
|
|
||||||
bool is_camera_leaf = ((i32)leaf_id == camera_leaf);
|
u32 byte = target >> 3;
|
||||||
|
u32 bit = target & 7;
|
||||||
f32 cx = ((f32)leaf->mins[0] + (f32)leaf->maxs[0]) * 0.5f;
|
if (visited[byte] & (1 << bit)) {
|
||||||
f32 cy = ((f32)leaf->mins[1] + (f32)leaf->maxs[1]) * 0.5f;
|
screen_rect existing = cell_windows[target];
|
||||||
f32 cz = ((f32)leaf->mins[2] + (f32)leaf->maxs[2]) * 0.5f;
|
bool expanded = false;
|
||||||
f32 dx = camera_pos.x - cx;
|
if (new_window.x0 < existing.x0) { cell_windows[target].x0 = new_window.x0; expanded = true; }
|
||||||
f32 dy = camera_pos.y - cy;
|
if (new_window.y0 < existing.y0) { cell_windows[target].y0 = new_window.y0; expanded = true; }
|
||||||
f32 dz = camera_pos.z - cz;
|
if (new_window.x1 > existing.x1) { cell_windows[target].x1 = new_window.x1; expanded = true; }
|
||||||
f32 dist_sq = dx*dx + dy*dy + dz*dz;
|
if (new_window.y1 > existing.y1) { cell_windows[target].y1 = new_window.y1; expanded = true; }
|
||||||
bool camera_near_leaf = dist_sq < (512.0f * 512.0f);
|
if (expanded && tail < bsp->num_leafs * 4) {
|
||||||
|
queue[tail++] = (portal_queue_entry){target, cell_windows[target]};
|
||||||
bool skip_frustum = is_camera_leaf || camera_near_leaf;
|
}
|
||||||
if (!skip_frustum && !leaf_in_frustum(leaf, frustum)) continue;
|
|
||||||
|
|
||||||
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
|
||||||
u32 surf_idx = leaf->first_marksurface + i;
|
|
||||||
if (surf_idx >= bsp->num_marksurfaces) continue;
|
|
||||||
|
|
||||||
u32 face_id = bsp->marksurfaces[surf_idx];
|
|
||||||
if (face_id >= bsp->num_faces) continue;
|
|
||||||
|
|
||||||
if (rendered_faces[face_id]) continue;
|
|
||||||
rendered_faces[face_id] = 1;
|
|
||||||
if (do_debug) visible_faces++;
|
|
||||||
|
|
||||||
if (!skip_frustum && !face_in_frustum(bsp, face_id, frustum)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
visited[byte] |= (1 << bit);
|
||||||
u32 texture_id = 0;
|
cell_windows[target] = new_window;
|
||||||
if (face->texinfo_id < bsp->num_texinfo) {
|
queue[tail++] = (portal_queue_entry){target, new_window};
|
||||||
texture_id = bsp->texinfo[face->texinfo_id].miptex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture_id != current_texture && mesh->index_count > 0) {
|
|
||||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
|
||||||
pxl8_gfx_material mat = {
|
|
||||||
.texture_id = current_texture,
|
|
||||||
.alpha = 255,
|
|
||||||
.dither = true,
|
|
||||||
.dynamic_lighting = true,
|
|
||||||
};
|
|
||||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
|
|
||||||
pxl8_mesh_clear(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
current_texture = texture_id;
|
|
||||||
collect_face_to_mesh(bsp, face_id, mesh);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh->index_count > 0) {
|
pxl8_mesh* mesh = pxl8_mesh_create(8192, 16384);
|
||||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
if (!mesh) {
|
||||||
pxl8_gfx_material mat = {
|
free(visited);
|
||||||
.texture_id = current_texture,
|
free(cell_windows);
|
||||||
.alpha = 255,
|
free(queue);
|
||||||
.dither = true,
|
return;
|
||||||
.dynamic_lighting = true,
|
|
||||||
};
|
|
||||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_debug) {
|
u32 current_material = 0xFFFFFFFF;
|
||||||
pxl8_debug("Camera at (%.1f, %.1f, %.1f) -> leaf %d, %u/%u leafs, %u/%u faces (expected %u)",
|
u32 total_faces = 0;
|
||||||
camera_pos.x, camera_pos.y, camera_pos.z,
|
|
||||||
camera_leaf, visible_leafs, bsp->num_leafs,
|
|
||||||
visible_faces, bsp->num_faces, total_faces_in_visible_leafs);
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_mesh_destroy(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos, u32 color) {
|
|
||||||
if (!gfx || !bsp) return;
|
|
||||||
|
|
||||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
|
||||||
if (!frustum) return;
|
|
||||||
|
|
||||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
|
||||||
u8 line_color = (u8)(color & 0xFF);
|
|
||||||
|
|
||||||
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) {
|
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) {
|
||||||
if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
|
u32 byte = leaf_id >> 3;
|
||||||
|
u32 bit = leaf_id & 7;
|
||||||
|
if (!(visited[byte] & (1 << bit))) continue;
|
||||||
|
|
||||||
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
|
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
|
||||||
|
if (!leaf_in_frustum(leaf, frustum)) continue;
|
||||||
|
|
||||||
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
||||||
u32 surf_idx = leaf->first_marksurface + i;
|
u32 surf_idx = leaf->first_marksurface + i;
|
||||||
|
|
@ -802,23 +853,45 @@ void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 cam
|
||||||
u32 face_id = bsp->marksurfaces[surf_idx];
|
u32 face_id = bsp->marksurfaces[surf_idx];
|
||||||
if (face_id >= bsp->num_faces) continue;
|
if (face_id >= bsp->num_faces) continue;
|
||||||
|
|
||||||
|
if (bsp_mut->render_face_flags[face_id]) continue;
|
||||||
|
bsp_mut->render_face_flags[face_id] = 1;
|
||||||
|
|
||||||
if (!face_in_frustum(bsp, face_id, frustum)) continue;
|
if (!face_in_frustum(bsp, face_id, frustum)) continue;
|
||||||
|
|
||||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||||
|
u32 material_id = face->material_id;
|
||||||
|
if (material_id >= bsp->num_materials) continue;
|
||||||
|
total_faces++;
|
||||||
|
|
||||||
for (u32 e = 0; e < face->num_edges; e++) {
|
if (material_id != current_material && mesh->index_count > 0 && current_material < bsp->num_materials) {
|
||||||
i32 surfedge_idx = face->first_edge + e;
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
|
pxl8_3d_draw_mesh(gfx, mesh, &identity, &bsp->materials[current_material]);
|
||||||
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
pxl8_mesh_clear(mesh);
|
||||||
|
|
||||||
u32 v0_idx, v1_idx;
|
|
||||||
if (!pxl8_bsp_get_edge_vertices(bsp, surfedge_idx, &v0_idx, &v1_idx)) continue;
|
|
||||||
|
|
||||||
pxl8_vec3 p0 = bsp->vertices[v0_idx].position;
|
|
||||||
pxl8_vec3 p1 = bsp->vertices[v1_idx].position;
|
|
||||||
|
|
||||||
pxl8_3d_draw_line(gfx, p0, p1, line_color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current_material = material_id;
|
||||||
|
collect_face_to_mesh(bsp, face_id, mesh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mesh->index_count > 0 && current_material < bsp->num_materials) {
|
||||||
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
|
pxl8_3d_draw_mesh(gfx, mesh, &identity, &bsp->materials[current_material]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 debug_count = 0;
|
||||||
|
if (debug_count++ < 5) {
|
||||||
|
pxl8_debug("BSP render: %u faces, mesh verts=%u indices=%u", total_faces, mesh->vertex_count, mesh->index_count);
|
||||||
|
if (mesh->vertex_count > 0) {
|
||||||
|
pxl8_vertex* v = &mesh->vertices[0];
|
||||||
|
pxl8_debug(" vert[0]: pos=(%.1f,%.1f,%.1f) uv=(%.2f,%.2f)",
|
||||||
|
v->position.x, v->position.y, v->position.z, v->u, v->v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_bsp_pvs_destroy(&pvs);
|
||||||
|
free(visited);
|
||||||
|
free(cell_windows);
|
||||||
|
free(queue);
|
||||||
|
pxl8_mesh_destroy(mesh);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ typedef struct pxl8_bsp_face {
|
||||||
u16 side;
|
u16 side;
|
||||||
|
|
||||||
u8 styles[4];
|
u8 styles[4];
|
||||||
u16 texinfo_id;
|
u16 material_id;
|
||||||
|
|
||||||
pxl8_vec3 aabb_min;
|
pxl8_vec3 aabb_min;
|
||||||
pxl8_vec3 aabb_max;
|
pxl8_vec3 aabb_max;
|
||||||
|
|
@ -63,16 +63,6 @@ typedef struct pxl8_bsp_plane {
|
||||||
i32 type;
|
i32 type;
|
||||||
} pxl8_bsp_plane;
|
} pxl8_bsp_plane;
|
||||||
|
|
||||||
typedef struct pxl8_bsp_texinfo {
|
|
||||||
u32 miptex;
|
|
||||||
char name[16];
|
|
||||||
|
|
||||||
f32 u_offset;
|
|
||||||
pxl8_vec3 u_axis;
|
|
||||||
|
|
||||||
f32 v_offset;
|
|
||||||
pxl8_vec3 v_axis;
|
|
||||||
} pxl8_bsp_texinfo;
|
|
||||||
|
|
||||||
typedef struct pxl8_bsp_vertex {
|
typedef struct pxl8_bsp_vertex {
|
||||||
pxl8_vec3 position;
|
pxl8_vec3 position;
|
||||||
|
|
@ -91,47 +81,52 @@ typedef struct pxl8_bsp_lightmap_sample {
|
||||||
u8 r;
|
u8 r;
|
||||||
} pxl8_bsp_lightmap_sample;
|
} pxl8_bsp_lightmap_sample;
|
||||||
|
|
||||||
typedef struct pxl8_bsp_material_batch {
|
|
||||||
u16* face_indices;
|
|
||||||
u32 face_count;
|
|
||||||
u8 material_id;
|
|
||||||
pxl8_mesh* mesh;
|
|
||||||
} pxl8_bsp_material_batch;
|
|
||||||
|
|
||||||
typedef struct pxl8_bsp_pvs {
|
typedef struct pxl8_bsp_pvs {
|
||||||
u8* data;
|
u8* data;
|
||||||
u32 size;
|
u32 size;
|
||||||
} pxl8_bsp_pvs;
|
} pxl8_bsp_pvs;
|
||||||
|
|
||||||
|
typedef struct pxl8_bsp_portal {
|
||||||
|
f32 x0, z0;
|
||||||
|
f32 x1, z1;
|
||||||
|
u32 target_leaf;
|
||||||
|
} pxl8_bsp_portal;
|
||||||
|
|
||||||
|
typedef struct pxl8_bsp_cell_portals {
|
||||||
|
pxl8_bsp_portal portals[4];
|
||||||
|
u8 num_portals;
|
||||||
|
} pxl8_bsp_cell_portals;
|
||||||
|
|
||||||
typedef struct pxl8_bsp {
|
typedef struct pxl8_bsp {
|
||||||
|
pxl8_bsp_cell_portals* cell_portals;
|
||||||
pxl8_bsp_edge* edges;
|
pxl8_bsp_edge* edges;
|
||||||
pxl8_bsp_face* faces;
|
pxl8_bsp_face* faces;
|
||||||
pxl8_bsp_leaf* leafs;
|
pxl8_bsp_leaf* leafs;
|
||||||
u8* lightdata;
|
u8* lightdata;
|
||||||
pxl8_bsp_lightmap* lightmaps;
|
pxl8_bsp_lightmap* lightmaps;
|
||||||
u16* marksurfaces;
|
u16* marksurfaces;
|
||||||
pxl8_bsp_material_batch* material_batches;
|
pxl8_gfx_material* materials;
|
||||||
pxl8_bsp_model* models;
|
pxl8_bsp_model* models;
|
||||||
pxl8_bsp_node* nodes;
|
pxl8_bsp_node* nodes;
|
||||||
pxl8_bsp_plane* planes;
|
pxl8_bsp_plane* planes;
|
||||||
|
u8* render_face_flags;
|
||||||
i32* surfedges;
|
i32* surfedges;
|
||||||
pxl8_bsp_texinfo* texinfo;
|
|
||||||
u32* vertex_lights;
|
u32* vertex_lights;
|
||||||
pxl8_bsp_vertex* vertices;
|
pxl8_bsp_vertex* vertices;
|
||||||
u8* visdata;
|
u8* visdata;
|
||||||
|
|
||||||
u32 lightdata_size;
|
u32 lightdata_size;
|
||||||
|
u32 num_cell_portals;
|
||||||
u32 num_edges;
|
u32 num_edges;
|
||||||
u32 num_faces;
|
u32 num_faces;
|
||||||
u32 num_leafs;
|
u32 num_leafs;
|
||||||
u32 num_lightmaps;
|
u32 num_lightmaps;
|
||||||
u32 num_marksurfaces;
|
u32 num_marksurfaces;
|
||||||
u32 num_material_batches;
|
u32 num_materials;
|
||||||
u32 num_models;
|
u32 num_models;
|
||||||
u32 num_nodes;
|
u32 num_nodes;
|
||||||
u32 num_planes;
|
u32 num_planes;
|
||||||
u32 num_surfedges;
|
u32 num_surfedges;
|
||||||
u32 num_texinfo;
|
|
||||||
u32 num_vertex_lights;
|
u32 num_vertex_lights;
|
||||||
u32 num_vertices;
|
u32 num_vertices;
|
||||||
u32 visdata_size;
|
u32 visdata_size;
|
||||||
|
|
@ -155,9 +150,8 @@ pxl8_bsp_lightmap pxl8_bsp_lightmap_uniform(u8 r, u8 g, u8 b);
|
||||||
pxl8_bsp_lightmap pxl8_bsp_lightmap_mapped(u8 width, u8 height, u32 offset);
|
pxl8_bsp_lightmap pxl8_bsp_lightmap_mapped(u8 width, u8 height, u32 offset);
|
||||||
pxl8_bsp_lightmap_sample pxl8_bsp_sample_lightmap(const pxl8_bsp* bsp, u32 face_idx, f32 u, f32 v);
|
pxl8_bsp_lightmap_sample pxl8_bsp_sample_lightmap(const pxl8_bsp* bsp, u32 face_idx, f32 u, f32 v);
|
||||||
|
|
||||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id);
|
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const pxl8_gfx_material* material);
|
||||||
void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos);
|
void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos);
|
||||||
void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos, u32 color);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include "pxl8_rng.h"
|
#include "pxl8_rng.h"
|
||||||
|
|
||||||
#define CELL_SIZE 64.0f
|
#define CELL_SIZE 64.0f
|
||||||
#define PVS_MAX_DEPTH 40
|
#define PVS_MAX_DEPTH 32
|
||||||
#define WALL_HEIGHT 128.0f
|
#define WALL_HEIGHT 128.0f
|
||||||
|
|
||||||
typedef struct room_grid {
|
typedef struct room_grid {
|
||||||
|
|
@ -24,22 +24,6 @@ typedef struct bsp_build_context {
|
||||||
u32 plane_offset;
|
u32 plane_offset;
|
||||||
} bsp_build_context;
|
} bsp_build_context;
|
||||||
|
|
||||||
typedef struct portal {
|
|
||||||
f32 x0, z0;
|
|
||||||
f32 x1, z1;
|
|
||||||
u32 target_leaf;
|
|
||||||
} portal;
|
|
||||||
|
|
||||||
typedef struct cell_portals {
|
|
||||||
portal portals[4];
|
|
||||||
u8 num_portals;
|
|
||||||
} cell_portals;
|
|
||||||
|
|
||||||
typedef struct vis_window {
|
|
||||||
f32 left_x, left_z;
|
|
||||||
f32 right_x, right_z;
|
|
||||||
} vis_window;
|
|
||||||
|
|
||||||
typedef struct light_source {
|
typedef struct light_source {
|
||||||
pxl8_vec3 position;
|
pxl8_vec3 position;
|
||||||
f32 intensity;
|
f32 intensity;
|
||||||
|
|
@ -183,9 +167,9 @@ static void compute_bsp_vertex_lighting(
|
||||||
pxl8_debug("Computed vertex lighting: %u vertices, %u lights", bsp->num_vertices, num_lights);
|
pxl8_debug("Computed vertex lighting: %u vertices, %u lights", bsp->num_vertices, num_lights);
|
||||||
}
|
}
|
||||||
|
|
||||||
static cell_portals* build_cell_portals(const room_grid* grid, f32 cell_size) {
|
static pxl8_bsp_cell_portals* build_pxl8_bsp_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||||
i32 total_cells = grid->width * grid->height;
|
i32 total_cells = grid->width * grid->height;
|
||||||
cell_portals* portals = calloc(total_cells, sizeof(cell_portals));
|
pxl8_bsp_cell_portals* portals = calloc(total_cells, sizeof(pxl8_bsp_cell_portals));
|
||||||
if (!portals) return NULL;
|
if (!portals) return NULL;
|
||||||
|
|
||||||
for (i32 y = 0; y < grid->height; y++) {
|
for (i32 y = 0; y < grid->height; y++) {
|
||||||
|
|
@ -197,7 +181,7 @@ static cell_portals* build_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||||
f32 cz = y * cell_size;
|
f32 cz = y * cell_size;
|
||||||
|
|
||||||
if (x > 0 && room_grid_get(grid, x - 1, y) == 0) {
|
if (x > 0 && room_grid_get(grid, x - 1, y) == 0) {
|
||||||
portal* p = &portals[c].portals[portals[c].num_portals++];
|
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||||
p->x0 = cx;
|
p->x0 = cx;
|
||||||
p->z0 = cz;
|
p->z0 = cz;
|
||||||
p->x1 = cx;
|
p->x1 = cx;
|
||||||
|
|
@ -205,7 +189,7 @@ static cell_portals* build_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||||
p->target_leaf = y * grid->width + (x - 1);
|
p->target_leaf = y * grid->width + (x - 1);
|
||||||
}
|
}
|
||||||
if (x < grid->width - 1 && room_grid_get(grid, x + 1, y) == 0) {
|
if (x < grid->width - 1 && room_grid_get(grid, x + 1, y) == 0) {
|
||||||
portal* p = &portals[c].portals[portals[c].num_portals++];
|
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||||
p->x0 = cx + cell_size;
|
p->x0 = cx + cell_size;
|
||||||
p->z0 = cz + cell_size;
|
p->z0 = cz + cell_size;
|
||||||
p->x1 = cx + cell_size;
|
p->x1 = cx + cell_size;
|
||||||
|
|
@ -213,7 +197,7 @@ static cell_portals* build_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||||
p->target_leaf = y * grid->width + (x + 1);
|
p->target_leaf = y * grid->width + (x + 1);
|
||||||
}
|
}
|
||||||
if (y > 0 && room_grid_get(grid, x, y - 1) == 0) {
|
if (y > 0 && room_grid_get(grid, x, y - 1) == 0) {
|
||||||
portal* p = &portals[c].portals[portals[c].num_portals++];
|
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||||
p->x0 = cx + cell_size;
|
p->x0 = cx + cell_size;
|
||||||
p->z0 = cz;
|
p->z0 = cz;
|
||||||
p->x1 = cx;
|
p->x1 = cx;
|
||||||
|
|
@ -221,7 +205,7 @@ static cell_portals* build_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||||
p->target_leaf = (y - 1) * grid->width + x;
|
p->target_leaf = (y - 1) * grid->width + x;
|
||||||
}
|
}
|
||||||
if (y < grid->height - 1 && room_grid_get(grid, x, y + 1) == 0) {
|
if (y < grid->height - 1 && room_grid_get(grid, x, y + 1) == 0) {
|
||||||
portal* p = &portals[c].portals[portals[c].num_portals++];
|
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||||
p->x0 = cx;
|
p->x0 = cx;
|
||||||
p->z0 = cz + cell_size;
|
p->z0 = cz + cell_size;
|
||||||
p->x1 = cx + cell_size;
|
p->x1 = cx + cell_size;
|
||||||
|
|
@ -233,168 +217,57 @@ static cell_portals* build_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||||
return portals;
|
return portals;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline f32 cross_2d(f32 ax, f32 az, f32 bx, f32 bz) {
|
typedef struct flood_entry {
|
||||||
return ax * bz - az * bx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool clip_window_through_portal(
|
|
||||||
f32 src_x, f32 src_z,
|
|
||||||
const vis_window* window,
|
|
||||||
const portal* p,
|
|
||||||
vis_window* clipped
|
|
||||||
) {
|
|
||||||
f32 wl_x = window->left_x - src_x;
|
|
||||||
f32 wl_z = window->left_z - src_z;
|
|
||||||
f32 wr_x = window->right_x - src_x;
|
|
||||||
f32 wr_z = window->right_z - src_z;
|
|
||||||
|
|
||||||
f32 p0_x = p->x0 - src_x;
|
|
||||||
f32 p0_z = p->z0 - src_z;
|
|
||||||
f32 p1_x = p->x1 - src_x;
|
|
||||||
f32 p1_z = p->z1 - src_z;
|
|
||||||
|
|
||||||
f32 p0_vs_wl = cross_2d(p0_x, p0_z, wl_x, wl_z);
|
|
||||||
f32 p1_vs_wl = cross_2d(p1_x, p1_z, wl_x, wl_z);
|
|
||||||
f32 p0_vs_wr = cross_2d(p0_x, p0_z, wr_x, wr_z);
|
|
||||||
f32 p1_vs_wr = cross_2d(p1_x, p1_z, wr_x, wr_z);
|
|
||||||
|
|
||||||
if (p1_vs_wl <= 0.0f || p0_vs_wr >= 0.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p0_vs_wl > 0.0f) {
|
|
||||||
clipped->left_x = p->x0;
|
|
||||||
clipped->left_z = p->z0;
|
|
||||||
} else {
|
|
||||||
clipped->left_x = window->left_x;
|
|
||||||
clipped->left_z = window->left_z;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p1_vs_wr < 0.0f) {
|
|
||||||
clipped->right_x = p->x1;
|
|
||||||
clipped->right_z = p->z1;
|
|
||||||
} else {
|
|
||||||
clipped->right_x = window->right_x;
|
|
||||||
clipped->right_z = window->right_z;
|
|
||||||
}
|
|
||||||
|
|
||||||
f32 cl_x = clipped->left_x - src_x;
|
|
||||||
f32 cl_z = clipped->left_z - src_z;
|
|
||||||
f32 cr_x = clipped->right_x - src_x;
|
|
||||||
f32 cr_z = clipped->right_z - src_z;
|
|
||||||
|
|
||||||
if (cross_2d(cl_x, cl_z, cr_x, cr_z) >= -0.0001f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct portal_vis_context {
|
|
||||||
u8* pvs;
|
|
||||||
u8* visited;
|
|
||||||
const cell_portals* portals;
|
|
||||||
const pxl8_bsp_leaf* leafs;
|
|
||||||
f32 src_x;
|
|
||||||
f32 src_z;
|
|
||||||
u32 num_leafs;
|
|
||||||
u32 max_depth;
|
|
||||||
} portal_vis_context;
|
|
||||||
|
|
||||||
static void portal_flood_recursive(
|
|
||||||
portal_vis_context* ctx,
|
|
||||||
u32 current_leaf,
|
|
||||||
const vis_window* window,
|
|
||||||
u32 depth
|
|
||||||
) {
|
|
||||||
if (depth > ctx->max_depth) return;
|
|
||||||
|
|
||||||
u32 byte = current_leaf >> 3;
|
|
||||||
u32 bit = current_leaf & 7;
|
|
||||||
|
|
||||||
if (ctx->visited[byte] & (1 << bit)) return;
|
|
||||||
ctx->visited[byte] |= (1 << bit);
|
|
||||||
|
|
||||||
if (ctx->leafs[current_leaf].contents == -1) return;
|
|
||||||
|
|
||||||
ctx->pvs[byte] |= (1 << bit);
|
|
||||||
|
|
||||||
const cell_portals* cp = &ctx->portals[current_leaf];
|
|
||||||
for (u8 i = 0; i < cp->num_portals; i++) {
|
|
||||||
const portal* p = &cp->portals[i];
|
|
||||||
|
|
||||||
u32 target_byte = p->target_leaf >> 3;
|
|
||||||
u32 target_bit = p->target_leaf & 7;
|
|
||||||
if (ctx->visited[target_byte] & (1 << target_bit)) continue;
|
|
||||||
|
|
||||||
vis_window clipped;
|
|
||||||
if (clip_window_through_portal(ctx->src_x, ctx->src_z, window, p, &clipped)) {
|
|
||||||
portal_flood_recursive(ctx, p->target_leaf, &clipped, depth + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct vis_entry {
|
|
||||||
u32 leaf;
|
u32 leaf;
|
||||||
vis_window window;
|
u32 depth;
|
||||||
} vis_entry;
|
} flood_entry;
|
||||||
|
|
||||||
static void portal_flood_bfs(
|
static void portal_flood_bfs(
|
||||||
u32 start_leaf,
|
u32 start_leaf,
|
||||||
const cell_portals* portals,
|
const pxl8_bsp_cell_portals* portals,
|
||||||
const pxl8_bsp_leaf* leafs,
|
const pxl8_bsp_leaf* leafs,
|
||||||
u8* pvs,
|
u8* pvs,
|
||||||
u32 num_leafs,
|
u32 num_leafs,
|
||||||
f32 cell_size,
|
f32 cell_size,
|
||||||
i32 grid_width
|
i32 grid_width
|
||||||
) {
|
) {
|
||||||
|
(void)cell_size;
|
||||||
|
(void)grid_width;
|
||||||
|
|
||||||
u32 pvs_bytes = (num_leafs + 7) / 8;
|
u32 pvs_bytes = (num_leafs + 7) / 8;
|
||||||
u8* visited = calloc(pvs_bytes, 1);
|
u8* visited = calloc(pvs_bytes, 1);
|
||||||
vis_entry* queue = malloc(num_leafs * 4 * sizeof(vis_entry));
|
flood_entry* queue = malloc(num_leafs * sizeof(flood_entry));
|
||||||
if (!visited || !queue) {
|
if (!visited || !queue) {
|
||||||
free(visited);
|
free(visited);
|
||||||
free(queue);
|
free(queue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 start_x = start_leaf % grid_width;
|
|
||||||
i32 start_y = start_leaf / grid_width;
|
|
||||||
f32 src_x = (start_x + 0.5f) * cell_size;
|
|
||||||
f32 src_z = (start_y + 0.5f) * cell_size;
|
|
||||||
|
|
||||||
u32 head = 0, tail = 0;
|
u32 head = 0, tail = 0;
|
||||||
|
|
||||||
pvs[start_leaf >> 3] |= (1 << (start_leaf & 7));
|
pvs[start_leaf >> 3] |= (1 << (start_leaf & 7));
|
||||||
visited[start_leaf >> 3] |= (1 << (start_leaf & 7));
|
visited[start_leaf >> 3] |= (1 << (start_leaf & 7));
|
||||||
|
queue[tail++] = (flood_entry){start_leaf, 0};
|
||||||
const cell_portals* start_cp = &portals[start_leaf];
|
|
||||||
for (u8 i = 0; i < start_cp->num_portals; i++) {
|
|
||||||
const portal* p = &start_cp->portals[i];
|
|
||||||
vis_window initial = {p->x0, p->z0, p->x1, p->z1};
|
|
||||||
queue[tail++] = (vis_entry){p->target_leaf, initial};
|
|
||||||
}
|
|
||||||
|
|
||||||
while (head < tail) {
|
while (head < tail) {
|
||||||
vis_entry e = queue[head++];
|
flood_entry e = queue[head++];
|
||||||
|
|
||||||
|
if (e.depth >= PVS_MAX_DEPTH) continue;
|
||||||
if (leafs[e.leaf].contents == -1) continue;
|
if (leafs[e.leaf].contents == -1) continue;
|
||||||
|
|
||||||
u32 byte = e.leaf >> 3;
|
const pxl8_bsp_cell_portals* cp = &portals[e.leaf];
|
||||||
u32 bit = e.leaf & 7;
|
|
||||||
|
|
||||||
pvs[byte] |= (1 << bit);
|
|
||||||
|
|
||||||
if (visited[byte] & (1 << bit)) continue;
|
|
||||||
visited[byte] |= (1 << bit);
|
|
||||||
|
|
||||||
const cell_portals* cp = &portals[e.leaf];
|
|
||||||
for (u8 i = 0; i < cp->num_portals; i++) {
|
for (u8 i = 0; i < cp->num_portals; i++) {
|
||||||
const portal* p = &cp->portals[i];
|
u32 target = cp->portals[i].target_leaf;
|
||||||
|
u32 byte = target >> 3;
|
||||||
|
u32 bit = target & 7;
|
||||||
|
|
||||||
vis_window clipped;
|
if (visited[byte] & (1 << bit)) continue;
|
||||||
if (clip_window_through_portal(src_x, src_z, &e.window, p, &clipped)) {
|
visited[byte] |= (1 << bit);
|
||||||
queue[tail++] = (vis_entry){p->target_leaf, clipped};
|
|
||||||
}
|
if (leafs[target].contents == -1) continue;
|
||||||
|
|
||||||
|
pvs[byte] |= (1 << bit);
|
||||||
|
queue[tail++] = (flood_entry){target, e.depth + 1};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -402,7 +275,7 @@ static void portal_flood_bfs(
|
||||||
free(queue);
|
free(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8* compute_leaf_pvs(u32 start_leaf, const cell_portals* portals,
|
static u8* compute_leaf_pvs(u32 start_leaf, const pxl8_bsp_cell_portals* portals,
|
||||||
u32 num_leafs, const pxl8_bsp_leaf* leafs,
|
u32 num_leafs, const pxl8_bsp_leaf* leafs,
|
||||||
const room_grid* grid, f32 cell_size) {
|
const room_grid* grid, f32 cell_size) {
|
||||||
u32 pvs_bytes = (num_leafs + 7) / 8;
|
u32 pvs_bytes = (num_leafs + 7) / 8;
|
||||||
|
|
@ -434,7 +307,7 @@ static u32 rle_compress_pvs(const u8* pvs, u32 pvs_bytes, u8* out) {
|
||||||
return out_pos;
|
return out_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pxl8_result build_pvs_data(pxl8_bsp* bsp, const cell_portals* portals,
|
static pxl8_result build_pvs_data(pxl8_bsp* bsp, const pxl8_bsp_cell_portals* portals,
|
||||||
const room_grid* grid, f32 cell_size) {
|
const room_grid* grid, f32 cell_size) {
|
||||||
u32 num_leafs = bsp->num_leafs;
|
u32 num_leafs = bsp->num_leafs;
|
||||||
u32 pvs_bytes = (num_leafs + 7) / 8;
|
u32 pvs_bytes = (num_leafs + 7) / 8;
|
||||||
|
|
@ -571,8 +444,8 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
bsp->texinfo = NULL;
|
bsp->materials = NULL;
|
||||||
bsp->num_texinfo = 0;
|
bsp->num_materials = 0;
|
||||||
|
|
||||||
i32 vert_idx = 0;
|
i32 vert_idx = 0;
|
||||||
i32 face_idx = 0;
|
i32 face_idx = 0;
|
||||||
|
|
@ -600,7 +473,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
bsp->faces[face_idx].material_id = 0;
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
for (i32 i = 0; i < 4; i++) {
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
|
@ -628,7 +501,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
bsp->faces[face_idx].material_id = 0;
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
for (i32 i = 0; i < 4; i++) {
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
|
@ -656,7 +529,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
bsp->faces[face_idx].material_id = 0;
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
for (i32 i = 0; i < 4; i++) {
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
|
@ -684,7 +557,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
bsp->faces[face_idx].material_id = 0;
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
for (i32 i = 0; i < 4; i++) {
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
|
@ -721,7 +594,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
bsp->faces[face_idx].material_id = 0;
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
for (i32 i = 0; i < 4; i++) {
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
|
@ -829,7 +702,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
pxl8_debug("Built BSP tree: %u nodes, %u leafs, %u planes",
|
pxl8_debug("Built BSP tree: %u nodes, %u leafs, %u planes",
|
||||||
bsp->num_nodes, bsp->num_leafs, bsp->num_planes);
|
bsp->num_nodes, bsp->num_leafs, bsp->num_planes);
|
||||||
|
|
||||||
cell_portals* portals = build_cell_portals(grid, cell_size);
|
pxl8_bsp_cell_portals* portals = build_pxl8_bsp_cell_portals(grid, cell_size);
|
||||||
if (!portals) {
|
if (!portals) {
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
@ -874,12 +747,14 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_result pvs_result = build_pvs_data(bsp, portals, grid, cell_size);
|
pxl8_result pvs_result = build_pvs_data(bsp, portals, grid, cell_size);
|
||||||
free(portals);
|
|
||||||
|
|
||||||
if (pvs_result != PXL8_OK) {
|
if (pvs_result != PXL8_OK) {
|
||||||
|
free(portals);
|
||||||
return pvs_result;
|
return pvs_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bsp->cell_portals = portals;
|
||||||
|
bsp->num_cell_portals = total_cells;
|
||||||
|
|
||||||
return PXL8_OK;
|
return PXL8_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
struct pxl8_world {
|
struct pxl8_world {
|
||||||
pxl8_bsp bsp;
|
pxl8_bsp bsp;
|
||||||
bool loaded;
|
bool loaded;
|
||||||
bool wireframe;
|
|
||||||
u32 wireframe_color;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pxl8_world* pxl8_world_create(void) {
|
pxl8_world* pxl8_world_create(void) {
|
||||||
|
|
@ -23,7 +21,6 @@ pxl8_world* pxl8_world_create(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
world->loaded = false;
|
world->loaded = false;
|
||||||
world->wireframe_color = 15;
|
|
||||||
|
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
@ -98,12 +95,12 @@ pxl8_result pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_textur
|
||||||
|
|
||||||
pxl8_bsp* bsp = &world->bsp;
|
pxl8_bsp* bsp = &world->bsp;
|
||||||
|
|
||||||
u32 max_texinfo = count * 6;
|
u32 max_materials = count * 6;
|
||||||
bsp->texinfo = calloc(max_texinfo, sizeof(pxl8_bsp_texinfo));
|
bsp->materials = calloc(max_materials, sizeof(pxl8_gfx_material));
|
||||||
if (!bsp->texinfo) {
|
if (!bsp->materials) {
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
bsp->num_texinfo = 0;
|
bsp->num_materials = 0;
|
||||||
|
|
||||||
for (u32 face_idx = 0; face_idx < bsp->num_faces; face_idx++) {
|
for (u32 face_idx = 0; face_idx < bsp->num_faces; face_idx++) {
|
||||||
pxl8_bsp_face* face = &bsp->faces[face_idx];
|
pxl8_bsp_face* face = &bsp->faces[face_idx];
|
||||||
|
|
@ -136,45 +133,50 @@ pxl8_result pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_textur
|
||||||
v_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f};
|
v_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f};
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 texinfo_idx = bsp->num_texinfo;
|
u32 material_idx = bsp->num_materials;
|
||||||
bool found_existing = false;
|
bool found_existing = false;
|
||||||
for (u32 i = 0; i < bsp->num_texinfo; i++) {
|
for (u32 i = 0; i < bsp->num_materials; i++) {
|
||||||
if (strcmp(bsp->texinfo[i].name, matched->name) == 0 &&
|
if (strcmp(bsp->materials[i].name, matched->name) == 0 &&
|
||||||
bsp->texinfo[i].miptex == matched->texture_id &&
|
bsp->materials[i].texture_id == matched->texture_id &&
|
||||||
bsp->texinfo[i].u_axis.x == u_axis.x &&
|
bsp->materials[i].u_axis.x == u_axis.x &&
|
||||||
bsp->texinfo[i].u_axis.y == u_axis.y &&
|
bsp->materials[i].u_axis.y == u_axis.y &&
|
||||||
bsp->texinfo[i].u_axis.z == u_axis.z &&
|
bsp->materials[i].u_axis.z == u_axis.z &&
|
||||||
bsp->texinfo[i].v_axis.x == v_axis.x &&
|
bsp->materials[i].v_axis.x == v_axis.x &&
|
||||||
bsp->texinfo[i].v_axis.y == v_axis.y &&
|
bsp->materials[i].v_axis.y == v_axis.y &&
|
||||||
bsp->texinfo[i].v_axis.z == v_axis.z) {
|
bsp->materials[i].v_axis.z == v_axis.z) {
|
||||||
texinfo_idx = i;
|
material_idx = i;
|
||||||
found_existing = true;
|
found_existing = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found_existing) {
|
if (!found_existing) {
|
||||||
if (bsp->num_texinfo >= max_texinfo) {
|
if (bsp->num_materials >= max_materials) {
|
||||||
pxl8_error("Too many unique texinfo entries");
|
pxl8_error("Too many unique material entries");
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(bsp->texinfo[texinfo_idx].name, matched->name, sizeof(bsp->texinfo[texinfo_idx].name));
|
pxl8_gfx_material* mat = &bsp->materials[material_idx];
|
||||||
bsp->texinfo[texinfo_idx].name[sizeof(bsp->texinfo[texinfo_idx].name) - 1] = '\0';
|
memcpy(mat->name, matched->name, sizeof(mat->name));
|
||||||
bsp->texinfo[texinfo_idx].miptex = matched->texture_id;
|
mat->name[sizeof(mat->name) - 1] = '\0';
|
||||||
bsp->texinfo[texinfo_idx].u_offset = 0.0f;
|
mat->texture_id = matched->texture_id;
|
||||||
bsp->texinfo[texinfo_idx].v_offset = 0.0f;
|
mat->u_offset = 0.0f;
|
||||||
bsp->texinfo[texinfo_idx].u_axis = u_axis;
|
mat->v_offset = 0.0f;
|
||||||
bsp->texinfo[texinfo_idx].v_axis = v_axis;
|
mat->u_axis = u_axis;
|
||||||
|
mat->v_axis = v_axis;
|
||||||
|
mat->alpha = 255;
|
||||||
|
mat->dither = true;
|
||||||
|
mat->double_sided = true;
|
||||||
|
mat->dynamic_lighting = true;
|
||||||
|
|
||||||
bsp->num_texinfo++;
|
bsp->num_materials++;
|
||||||
}
|
}
|
||||||
|
|
||||||
face->texinfo_id = texinfo_idx;
|
face->material_id = material_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_info("Applied %u textures to %u faces, created %u texinfo entries",
|
pxl8_info("Applied %u textures to %u faces, created %u materials",
|
||||||
count, bsp->num_faces, bsp->num_texinfo);
|
count, bsp->num_faces, bsp->num_materials);
|
||||||
|
|
||||||
return PXL8_OK;
|
return PXL8_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -398,15 +400,13 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (world->wireframe) {
|
pxl8_bsp_render(gfx, &world->bsp, camera_pos);
|
||||||
pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color);
|
|
||||||
} else {
|
|
||||||
pxl8_bsp_render_textured(gfx, &world->bsp, camera_pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled, u8 color) {
|
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled) {
|
||||||
if (!world) return;
|
if (!world || !world->loaded) return;
|
||||||
world->wireframe = enabled;
|
|
||||||
world->wireframe_color = color;
|
for (u32 i = 0; i < world->bsp.num_materials; i++) {
|
||||||
|
world->bsp.materials[i].wireframe = enabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ bool pxl8_world_check_collision(const pxl8_world* world, pxl8_vec3 pos, f32 radi
|
||||||
bool pxl8_world_is_loaded(const pxl8_world* world);
|
bool pxl8_world_is_loaded(const pxl8_world* world);
|
||||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||||
pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
|
pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
|
||||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled, u8 color);
|
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue