show chip8 display on model
This commit is contained in:
parent
ce31614950
commit
441a3df8ea
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
zig-cache
|
zig-cache
|
||||||
zig-out
|
zig-out
|
||||||
|
*.blend1
|
||||||
|
4
build-models.sh
Executable file
4
build-models.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd src/assets/models
|
||||||
|
blender emulator.blend --background --python export_obj.py
|
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
# Blender 3.5.1 MTL File: 'emulator.blend'
|
# Blender MTL File: 'emulator.blend'
|
||||||
# www.blender.org
|
# Material Count: 4
|
||||||
|
|
||||||
newmtl Behind_buttons
|
newmtl Behind_buttons
|
||||||
Ns 0.000000
|
Ns 0.000000
|
||||||
@ -14,15 +14,16 @@ illum 2
|
|||||||
newmtl Button
|
newmtl Button
|
||||||
Ns 250.000000
|
Ns 250.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.003095 0.002616 0.002869
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
Ni 1.450000
|
Ni 1.450000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
illum 2
|
||||||
map_Kd Buttons texture.png
|
map_Kd /home/rokas/code/fun/chip8-zig/src/assets/models/Buttons texture.png
|
||||||
|
|
||||||
newmtl Case
|
newmtl Case
|
||||||
Ns 298.057037
|
Ns 298.057005
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 1.000000 1.000000 1.000000
|
||||||
Kd 0.057402 0.087001 0.228570
|
Kd 0.057402 0.087001 0.228570
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
@ -40,3 +41,4 @@ Ke 0.000000 0.000000 0.000000
|
|||||||
Ni 1.450000
|
Ni 1.450000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
illum 2
|
||||||
|
map_Kd /home/rokas/code/fun/chip8-zig/src/assets/models/screen-texture.png
|
||||||
|
File diff suppressed because it is too large
Load Diff
8
src/assets/models/export_obj.py
Normal file
8
src/assets/models/export_obj.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import bpy
|
||||||
|
|
||||||
|
bpy.ops.export_scene.obj(
|
||||||
|
filepath="emulator.obj",
|
||||||
|
use_triangles=True,
|
||||||
|
use_materials=True,
|
||||||
|
use_normals=True,
|
||||||
|
)
|
BIN
src/assets/models/screen-texture.png
Normal file
BIN
src/assets/models/screen-texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 311 B |
@ -4,7 +4,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
|
|
||||||
const Errors = error { UnknownInstruction };
|
const Errors = error { UnknownInstruction };
|
||||||
|
|
||||||
allocator: *const Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
display: []bool,
|
display: []bool,
|
||||||
display_width: u8,
|
display_width: u8,
|
||||||
@ -43,7 +43,7 @@ fn get_inst_n(inst: u16) u4 {
|
|||||||
return @truncate(inst & 0x000F);
|
return @truncate(inst & 0x000F);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(allocator: *const Allocator) !Self {
|
pub fn init(allocator: Allocator) !Self {
|
||||||
const seed_bits: u128 = @bitCast(std.time.nanoTimestamp());
|
const seed_bits: u128 = @bitCast(std.time.nanoTimestamp());
|
||||||
const seed: u64 = @truncate(seed_bits);
|
const seed: u64 = @truncate(seed_bits);
|
||||||
|
|
||||||
|
19
src/global-context.zig
Normal file
19
src/global-context.zig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const Self = @This();
|
||||||
|
const rl = @import("raylib");
|
||||||
|
|
||||||
|
camera: rl.Camera3D,
|
||||||
|
|
||||||
|
pub fn init() Self {
|
||||||
|
var camera = rl.Camera3D{
|
||||||
|
.position = rl.Vector3.new(0, 0, -10),
|
||||||
|
.target = rl.Vector3.new(0.0, 0.0, 0.0),
|
||||||
|
.up = rl.Vector3.new(0.0, 1.0, 0.0),
|
||||||
|
.fovy = 45.0,
|
||||||
|
.projection = rl.CameraProjection.CAMERA_PERSPECTIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Self {
|
||||||
|
.camera = camera
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
413
src/main-scene.zig
Normal file
413
src/main-scene.zig
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
const Self = @This();
|
||||||
|
const rl = @import("raylib");
|
||||||
|
const std = @import("std");
|
||||||
|
const MemoryView = @import("memory-view.zig").MemoryView;
|
||||||
|
const Range = @import("memory-view.zig").Range;
|
||||||
|
const UI = @import("ui.zig").UI;
|
||||||
|
const GlobalContext = @import("./global-context.zig");
|
||||||
|
|
||||||
|
const ChipContext = @import("chip.zig");
|
||||||
|
const RaylibChip = @import("raylib-chip.zig");
|
||||||
|
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const Tab = enum {
|
||||||
|
MemoryView
|
||||||
|
};
|
||||||
|
|
||||||
|
var edit_mode = false;
|
||||||
|
var tab = Tab.MemoryView;
|
||||||
|
|
||||||
|
ctx: *GlobalContext,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
model: rl.Model,
|
||||||
|
model_bbox: rl.BoundingBox,
|
||||||
|
model_position: rl.Vector3,
|
||||||
|
|
||||||
|
camera_turn_vel: rl.Vector3 = rl.Vector3{ .x = 0, .y = 0, .z = 0 },
|
||||||
|
camera_target_orientation: ?rl.Vector3 = null,
|
||||||
|
previous_click_time: f64 = 0.0,
|
||||||
|
|
||||||
|
shader: rl.Shader,
|
||||||
|
light: Light,
|
||||||
|
|
||||||
|
chip: *ChipContext,
|
||||||
|
raylib_chip: RaylibChip,
|
||||||
|
chip_sound: rl.Sound,
|
||||||
|
|
||||||
|
screen_texture: rl.RenderTexture2D,
|
||||||
|
|
||||||
|
pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
|
||||||
|
assert(wave.sampleSize == 16); // Only 16 bits are supported
|
||||||
|
|
||||||
|
const sample_rate: f32 = @floatFromInt(wave.sampleRate);
|
||||||
|
const sample_size: u5 = @truncate(wave.sampleSize);
|
||||||
|
const max_sample_value: f32 = @floatFromInt(@shlExact(@as(u32, 1), sample_size - 1));
|
||||||
|
|
||||||
|
const data: [*]i16 = @ptrCast(@alignCast(wave.data));
|
||||||
|
for (0..wave.frameCount) |i| {
|
||||||
|
const i_f32: f32 = @floatFromInt(i);
|
||||||
|
const sin_value: f32 = @sin(std.math.pi*2*frequency/sample_rate*i_f32);
|
||||||
|
data[i] = @intFromFloat(sin_value*max_sample_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Light = struct {
|
||||||
|
const LightType = enum(i32) {
|
||||||
|
DIRECTIONAL = 0,
|
||||||
|
POINT = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
type: LightType,
|
||||||
|
enabled: bool,
|
||||||
|
position: rl.Vector3,
|
||||||
|
target: rl.Vector3,
|
||||||
|
color: rl.Color,
|
||||||
|
attenuation: f32 = 0.0,
|
||||||
|
|
||||||
|
enabledLoc: i32,
|
||||||
|
typeLoc: i32,
|
||||||
|
positionLoc: i32,
|
||||||
|
targetLoc: i32,
|
||||||
|
colorLoc: i32,
|
||||||
|
attenuationLoc: i32 = 0,
|
||||||
|
|
||||||
|
fn create(light_type: LightType, postion: rl.Vector3, target: rl.Vector3, color: rl.Color, shader: rl.Shader) Light {
|
||||||
|
var light = Light{
|
||||||
|
.type = light_type,
|
||||||
|
.enabled = true,
|
||||||
|
.position = postion,
|
||||||
|
.target = target,
|
||||||
|
.color = color,
|
||||||
|
.enabledLoc = rl.GetShaderLocation(shader, "lights[0].enabled"),
|
||||||
|
.typeLoc = rl.GetShaderLocation(shader, "lights[0].type"),
|
||||||
|
.positionLoc = rl.GetShaderLocation(shader, "lights[0].position"),
|
||||||
|
.targetLoc = rl.GetShaderLocation(shader, "lights[0].target"),
|
||||||
|
.colorLoc = rl.GetShaderLocation(shader, "lights[0].color"),
|
||||||
|
};
|
||||||
|
light.update_values(shader);
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_values(light: *Light, shader: rl.Shader) void {
|
||||||
|
const enabled: i32 = @intFromBool(light.enabled);
|
||||||
|
rl.SetShaderValue(shader, light.enabledLoc, &enabled, rl.ShaderUniformDataType.SHADER_UNIFORM_INT);
|
||||||
|
|
||||||
|
const lightType: i32 = @intFromEnum(light.type);
|
||||||
|
rl.SetShaderValue(shader, light.typeLoc, &lightType, rl.ShaderUniformDataType.SHADER_UNIFORM_INT);
|
||||||
|
|
||||||
|
const position = [3]f32{ light.position.x, light.position.y, light.position.z };
|
||||||
|
rl.SetShaderValue(shader, light.positionLoc, &position, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC3);
|
||||||
|
|
||||||
|
const target = [3]f32{ light.target.x, light.target.y, light.target.z };
|
||||||
|
rl.SetShaderValue(shader, light.targetLoc, &target, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC3);
|
||||||
|
|
||||||
|
const color = [4]f32{
|
||||||
|
@as(f32, @floatFromInt(light.color.r)) / 255.0,
|
||||||
|
@as(f32, @floatFromInt(light.color.g)) / 255.0,
|
||||||
|
@as(f32, @floatFromInt(light.color.b)) / 255.0,
|
||||||
|
@as(f32, @floatFromInt(light.color.a)) / 255.0,
|
||||||
|
};
|
||||||
|
rl.SetShaderValue(shader, light.colorLoc, &color, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn get_camera_projection(camera: *const rl.Camera3D) rl.Matrix {
|
||||||
|
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||||
|
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||||
|
|
||||||
|
if (camera.projection == .CAMERA_PERSPECTIVE) {
|
||||||
|
return rl.MatrixPerspective(camera.fovy*rl.DEG2RAD, screen_width/screen_height, rl.RL_CULL_DISTANCE_NEAR, rl.RL_CULL_DISTANCE_FAR);
|
||||||
|
} else if (camera.projection == .CAMERA_ORTHOGRAPHIC) {
|
||||||
|
const aspect = screen_width/screen_height;
|
||||||
|
const top = camera.fovy/2.0;
|
||||||
|
const right = top*aspect;
|
||||||
|
|
||||||
|
return rl.MatrixOrtho(-right, right, -top, top, rl.RL_CULL_DISTANCE_NEAR, rl.RL_CULL_DISTANCE_FAR);
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_screen_direction_from_camera(mat_proj: *const rl.Matrix, mat_view: *const rl.Matrix, point: rl.Vector2) rl.Vector3 {
|
||||||
|
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||||
|
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||||
|
|
||||||
|
const ndc_x = (2.0*point.x) / screen_width - 1.0;
|
||||||
|
const ndc_y = 1.0 - (2.0*point.y) / screen_height;
|
||||||
|
|
||||||
|
var near_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 0.0 }, mat_proj.*, mat_view.*);
|
||||||
|
var far_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 1.0 }, mat_proj.*, mat_view.*);
|
||||||
|
|
||||||
|
return rl.Vector3Subtract(far_point, near_point).normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_preffered_distance_to_box(camera: *const rl.Camera3D, box: rl.BoundingBox) f32 {
|
||||||
|
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||||
|
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||||
|
const margin = @min(screen_width, screen_height)*0.1;
|
||||||
|
|
||||||
|
const box_size = box.max.sub(box.min);
|
||||||
|
|
||||||
|
const max_model_scale = @min((screen_width-2*margin)/box_size.x, (screen_height-2*margin)/box_size.y);
|
||||||
|
// const model_screen_width = box_size.x * max_model_scale;
|
||||||
|
const model_screen_height = box_size.y * max_model_scale;
|
||||||
|
|
||||||
|
const mat_proj = get_camera_projection(camera);
|
||||||
|
const mat_view = rl.MatrixIdentity(); // rl.MatrixLookAt(camera.position, camera.target, camera.up);
|
||||||
|
|
||||||
|
const screen_middle = rl.Vector2{ .x = screen_width/2, .y = screen_height/2 };
|
||||||
|
const box_top_middle = screen_middle.add(.{ .y = -model_screen_height/2 });
|
||||||
|
|
||||||
|
const middle_dir = get_screen_direction_from_camera(&mat_proj, &mat_view, screen_middle);
|
||||||
|
const top_middle_dir = get_screen_direction_from_camera(&mat_proj, &mat_view, box_top_middle);
|
||||||
|
const angle = top_middle_dir.angleBetween(middle_dir);
|
||||||
|
const distance = 1/@tan(angle) * (box_size.y/2) + box_size.z/4;
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, ctx: *GlobalContext) !Self {
|
||||||
|
// var temp_mem = [1]u8{0xAA} ** (16*80 + 10);
|
||||||
|
// var memory_view = MemoryView.init(&temp_mem, &font, 32);
|
||||||
|
// var memory_view = MemoryView.init(chip.memory, &font, font_size, &allocator);
|
||||||
|
// var selected_memory = Range{};
|
||||||
|
// var ui = UI.init();
|
||||||
|
|
||||||
|
var model = rl.LoadModel("src/assets/models/emulator.obj");
|
||||||
|
var model_bbox = rl.GetModelBoundingBox(model);
|
||||||
|
|
||||||
|
var model_position = rl.Vector3{ };
|
||||||
|
|
||||||
|
const shader = rl.LoadShader("src/shaders/main.vs", "src/shaders/main.fs");
|
||||||
|
shader.locs.?[@intFromEnum(rl.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW)] = rl.GetShaderLocation(shader, "viewPos");
|
||||||
|
|
||||||
|
const ambientLoc = rl.GetShaderLocation(shader, "ambient");
|
||||||
|
rl.SetShaderValue(shader, ambientLoc, &[4]f32{ 0.2, 0.2, 0.2, 1.0 }, .SHADER_UNIFORM_VEC4);
|
||||||
|
|
||||||
|
var light = Light.create(.DIRECTIONAL, rl.Vector3.new(0.2, 0, -0.2), rl.Vector3.zero(), rl.WHITE, shader);
|
||||||
|
|
||||||
|
for (0..@intCast(model.materialCount)) |i| {
|
||||||
|
model.materials.?[i].shader = shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
var chip = try allocator.create(ChipContext);
|
||||||
|
chip.* = try ChipContext.init(allocator);
|
||||||
|
|
||||||
|
const sample_rate = 44100;
|
||||||
|
var data = try allocator.alloc(i16, sample_rate);
|
||||||
|
defer allocator.free(data);
|
||||||
|
var chip_wave = rl.Wave{
|
||||||
|
.frameCount = sample_rate,
|
||||||
|
.sampleRate = sample_rate,
|
||||||
|
.sampleSize = 16,
|
||||||
|
.channels = 1,
|
||||||
|
.data = @ptrCast(data.ptr),
|
||||||
|
};
|
||||||
|
|
||||||
|
gen_sin_wave(&chip_wave, 440);
|
||||||
|
var chip_sound = rl.LoadSoundFromWave(chip_wave);
|
||||||
|
rl.SetSoundVolume(chip_sound, 0.2);
|
||||||
|
var raylib_chip = RaylibChip.init(chip, chip_sound);
|
||||||
|
|
||||||
|
var screen_texture = rl.LoadRenderTexture(chip.display_width, chip.display_height);
|
||||||
|
|
||||||
|
// TODO: Don't use the fourth material, use name of material to get its index. Or some other more reliable method
|
||||||
|
rl.SetMaterialTexture(@ptrCast(&model.materials.?[3]), rl.MATERIAL_MAP_DIFFUSE, screen_texture.texture);
|
||||||
|
|
||||||
|
var self = Self {
|
||||||
|
.allocator = allocator,
|
||||||
|
.ctx = ctx,
|
||||||
|
.model = model,
|
||||||
|
.model_bbox = model_bbox,
|
||||||
|
.model_position = model_position,
|
||||||
|
.shader = shader,
|
||||||
|
.light = light,
|
||||||
|
|
||||||
|
.chip = chip,
|
||||||
|
.raylib_chip = raylib_chip,
|
||||||
|
.chip_sound = chip_sound,
|
||||||
|
|
||||||
|
.screen_texture = screen_texture,
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
rl.UnloadRenderTexture(self.screen_texture);
|
||||||
|
rl.UnloadSound(self.chip_sound);
|
||||||
|
self.chip.deinit();
|
||||||
|
self.allocator.destroy(self.chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_camera(self: *Self, dt: f32) void {
|
||||||
|
const mouse_delta = rl.GetMouseDelta();
|
||||||
|
const camera = &self.ctx.camera;
|
||||||
|
|
||||||
|
if (rl.IsWindowResized()) {
|
||||||
|
const distance = get_preffered_distance_to_box(camera, self.model_bbox);
|
||||||
|
const direction = camera.position.sub(self.model_position).normalize();
|
||||||
|
camera.position = self.model_position.add(direction.scale(distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rl.Vector3Equals(camera.position, rl.Vector3Zero()) == 1) {
|
||||||
|
const distance = get_preffered_distance_to_box(camera, self.model_bbox);
|
||||||
|
camera.target = self.model_position;
|
||||||
|
camera.position = self.model_position.sub(rl.Vector3.new(0, 0, 1).scale(distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
var camera_turn_acc = rl.Vector3Zero();
|
||||||
|
if (rl.IsMouseButtonDown(rl.MouseButton.MOUSE_BUTTON_LEFT)) {
|
||||||
|
if (@fabs(mouse_delta.x) > 5) {
|
||||||
|
const rotation_speed = 2; // Radians/second
|
||||||
|
camera_turn_acc.x = -rotation_speed*mouse_delta.x;
|
||||||
|
}
|
||||||
|
if (@fabs(mouse_delta.x) < 5) {
|
||||||
|
self.camera_turn_vel = self.camera_turn_vel.scale(0.90); // Holding drag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rl.IsMouseButtonPressed(rl.MouseButton.MOUSE_BUTTON_LEFT)) {
|
||||||
|
self.camera_target_orientation = null;
|
||||||
|
|
||||||
|
const now = rl.GetTime();
|
||||||
|
const duration_between_clicks = now - self.previous_click_time;
|
||||||
|
if (duration_between_clicks < 0.3) {
|
||||||
|
const ray = rl.GetMouseRay(rl.GetMousePosition(), camera.*);
|
||||||
|
const collision = rl.GetRayCollisionBox(ray, self.model_bbox);
|
||||||
|
if (collision.hit) {
|
||||||
|
const front_face_normal = rl.Vector3.new(0, 0, -1);
|
||||||
|
const back_face_normal = rl.Vector3.new(0, 0, 1);
|
||||||
|
if (rl.Vector3Equals(collision.normal, front_face_normal) == 1) {
|
||||||
|
self.camera_target_orientation = front_face_normal;
|
||||||
|
} else if (rl.Vector3Equals(collision.normal, back_face_normal) == 1) {
|
||||||
|
self.camera_target_orientation = back_face_normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.previous_click_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.camera_target_orientation) |target| {
|
||||||
|
const current_direction = camera.position.sub(self.model_position).normalize();
|
||||||
|
const current_angle = std.math.atan2(f32, current_direction.z, current_direction.x);
|
||||||
|
const target_angle = std.math.atan2(f32, target.z, target.x);
|
||||||
|
const diff_angle = std.math.pi - @mod((target_angle - current_angle) + 3*std.math.pi, 2*std.math.pi);
|
||||||
|
if (@fabs(diff_angle) < 0.001) {
|
||||||
|
self.camera_turn_vel.x = 0;
|
||||||
|
self.camera_target_orientation = null;
|
||||||
|
} else {
|
||||||
|
self.camera_turn_vel.x = diff_angle*3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.camera_turn_vel = self.camera_turn_vel.scale(0.95); // Ambient drag
|
||||||
|
self.camera_turn_vel = self.camera_turn_vel.add(camera_turn_acc.scale(dt));
|
||||||
|
|
||||||
|
const camera_min_vel = 0;
|
||||||
|
if (self.camera_turn_vel.length() > camera_min_vel) {
|
||||||
|
const rotation = rl.MatrixRotate(camera.up.normalize(), self.camera_turn_vel.x*dt);
|
||||||
|
var view = rl.Vector3Subtract(camera.position, camera.target);
|
||||||
|
view = rl.Vector3Transform(view, rotation);
|
||||||
|
camera.position = rl.Vector3Add(camera.target, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self: *Self, dt: f32) void {
|
||||||
|
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_TAB)) {
|
||||||
|
edit_mode = !edit_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edit_mode) {
|
||||||
|
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_ONE)) {
|
||||||
|
tab = .MemoryView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_camera(dt);
|
||||||
|
|
||||||
|
const camera = &self.ctx.camera;
|
||||||
|
const cameraPos = [3]f32{ camera.position.x, camera.position.y, camera.position.z };
|
||||||
|
rl.SetShaderValue(self.shader, self.shader.locs.?[@intFromEnum(rl.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW)], &cameraPos, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC3);
|
||||||
|
self.light.update_values(self.shader);
|
||||||
|
|
||||||
|
rl.BeginTextureMode(self.screen_texture);
|
||||||
|
self.raylib_chip.render();
|
||||||
|
rl.EndTextureMode();
|
||||||
|
|
||||||
|
// {
|
||||||
|
// var matProj = rl.MatrixIdentity();
|
||||||
|
// // projection = CAMERA_PERSPECTIVE
|
||||||
|
// matProj = rl.MatrixPerspective(camera.fovy*rl.DEG2RAD, (screen_width/screen_height), rl.RL_CULL_DISTANCE_NEAR, rl.RL_CULL_DISTANCE_FAR);
|
||||||
|
//
|
||||||
|
// var matView = rl.MatrixLookAt(camera.position, camera.target, camera.up);
|
||||||
|
// // Convert world position vector to quaternion
|
||||||
|
// var worldPos = rl.Vector4{ .x = position.x, .y = position.y, .z = position.z, .w = 1.0 };
|
||||||
|
//
|
||||||
|
// std.debug.print("worldPos {}\n", .{worldPos});
|
||||||
|
// // Transform world position to view
|
||||||
|
// worldPos = rl.QuaternionTransform(worldPos, matView);
|
||||||
|
//
|
||||||
|
// // Transform result to projection (clip space position)
|
||||||
|
// worldPos = rl.QuaternionTransform(worldPos, matProj);
|
||||||
|
//
|
||||||
|
// // Calculate normalized device coordinates (inverted y)
|
||||||
|
// var ndcPos = rl.Vector3.new( worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w );
|
||||||
|
//
|
||||||
|
// // Calculate 2d screen position vector
|
||||||
|
// screen_position = rl.Vector2{ .x = (ndcPos.x + 1.0)/2.0*screen_width, .y = (ndcPos.y + 1.0)/2.0*screen_height };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const target_screen_position = rl.Vector2{ .x = screen_width/2, .y = screen_height*0.1 };
|
||||||
|
// {
|
||||||
|
// var matProj = get_camera_projection(&camera);
|
||||||
|
// var matView = rl.MatrixLookAt(camera.position, camera.target, camera.up);
|
||||||
|
//
|
||||||
|
// const ndc_x = (2.0*target_screen_position.x) / screen_width - 1.0;
|
||||||
|
// const ndc_y = 1.0 - (2.0*target_screen_position.y) / screen_height;
|
||||||
|
//
|
||||||
|
// var near_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 0.0 }, matProj, matView);
|
||||||
|
// var far_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 1.0 }, matProj, matView);
|
||||||
|
//
|
||||||
|
// var direction = rl.Vector3Subtract(far_point, near_point).normalize();
|
||||||
|
//
|
||||||
|
// var origin: rl.Vector3 = undefined;
|
||||||
|
// if (camera.projection == .CAMERA_PERSPECTIVE) {
|
||||||
|
// origin = camera.position;
|
||||||
|
// } else {
|
||||||
|
// origin = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = -1.0 }, matProj, matView);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var world_pos = origin.add(direction.scale(3));
|
||||||
|
//
|
||||||
|
// model_position = world_pos;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: *Self) void {
|
||||||
|
rl.DrawModel(self.model, self.model_position, 1.0, rl.WHITE);
|
||||||
|
// rl.DrawMesh(self.screen_mesh, self.screen_material, self.screen_transform);
|
||||||
|
|
||||||
|
// if (!edit_mode) {
|
||||||
|
// rl.ClearBackground(rl.DARKGRAY);
|
||||||
|
//
|
||||||
|
// const scale_x = @divFloor(screen_width, chip.display_width);
|
||||||
|
// const scale_y = @divFloor(screen_height, chip.display_height);
|
||||||
|
// const min_scale = @min(scale_x, scale_y);
|
||||||
|
//
|
||||||
|
// const display_width = chip.display_width * min_scale;
|
||||||
|
// const display_height = chip.display_height * min_scale;
|
||||||
|
// const display_x = @divFloor(screen_width - display_width, 2);
|
||||||
|
// const display_y = @divFloor(screen_height - display_height, 2);
|
||||||
|
// raylib_chip.render(display_x, display_y, display_width, display_height);
|
||||||
|
// } else {
|
||||||
|
// rl.ClearBackground(rl.RAYWHITE);
|
||||||
|
// ui.update();
|
||||||
|
//
|
||||||
|
// if (tab == .MemoryView) {
|
||||||
|
// try memory_view.show(&ui, 0, 0, @floatFromInt(screen_width), @floatFromInt(screen_height), &selected_memory);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
399
src/main.zig
399
src/main.zig
@ -5,169 +5,20 @@ const print = std.debug.print;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ChipContext = @import("chip.zig");
|
const ChipContext = @import("chip.zig");
|
||||||
const RaylibChip = @import("raylib-chip.zig");
|
const RaylibChip = @import("raylib-chip.zig");
|
||||||
const UI = @import("ui.zig").UI;
|
const GlobalContext = @import("./global-context.zig");
|
||||||
|
const MainScene = @import("./main-scene.zig");
|
||||||
const MemoryView = @import("memory-view.zig").MemoryView;
|
|
||||||
const Range = @import("memory-view.zig").Range;
|
|
||||||
|
|
||||||
const assert = std.debug.assert;
|
|
||||||
|
|
||||||
const Tab = enum {
|
|
||||||
MemoryView
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
|
|
||||||
assert(wave.sampleSize == 16); // Only 16 bits are supported
|
|
||||||
|
|
||||||
const sample_rate: f32 = @floatFromInt(wave.sampleRate);
|
|
||||||
const sample_size: u5 = @truncate(wave.sampleSize);
|
|
||||||
const max_sample_value: f32 = @floatFromInt(@shlExact(@as(u32, 1), sample_size - 1));
|
|
||||||
|
|
||||||
const data: [*]i16 = @ptrCast(@alignCast(wave.data));
|
|
||||||
for (0..wave.frameCount) |i| {
|
|
||||||
const i_f32: f32 = @floatFromInt(i);
|
|
||||||
const sin_value: f32 = @sin(std.math.pi*2*frequency/sample_rate*i_f32);
|
|
||||||
data[i] = @intFromFloat(sin_value*max_sample_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn megabytes(amount: usize) usize {
|
fn megabytes(amount: usize) usize {
|
||||||
return amount * 1024 * 1024;
|
return amount * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Light = struct {
|
|
||||||
const LightType = enum(i32) {
|
|
||||||
DIRECTIONAL = 0,
|
|
||||||
POINT = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
type: LightType,
|
|
||||||
enabled: bool,
|
|
||||||
position: rl.Vector3,
|
|
||||||
target: rl.Vector3,
|
|
||||||
color: rl.Color,
|
|
||||||
attenuation: f32 = 0.0,
|
|
||||||
|
|
||||||
enabledLoc: i32,
|
|
||||||
typeLoc: i32,
|
|
||||||
positionLoc: i32,
|
|
||||||
targetLoc: i32,
|
|
||||||
colorLoc: i32,
|
|
||||||
attenuationLoc: i32 = 0,
|
|
||||||
|
|
||||||
fn create(light_type: LightType, postion: rl.Vector3, target: rl.Vector3, color: rl.Color, shader: rl.Shader) Light {
|
|
||||||
var light = Light{
|
|
||||||
.type = light_type,
|
|
||||||
.enabled = true,
|
|
||||||
.position = postion,
|
|
||||||
.target = target,
|
|
||||||
.color = color,
|
|
||||||
.enabledLoc = rl.GetShaderLocation(shader, "lights[0].enabled"),
|
|
||||||
.typeLoc = rl.GetShaderLocation(shader, "lights[0].type"),
|
|
||||||
.positionLoc = rl.GetShaderLocation(shader, "lights[0].position"),
|
|
||||||
.targetLoc = rl.GetShaderLocation(shader, "lights[0].target"),
|
|
||||||
.colorLoc = rl.GetShaderLocation(shader, "lights[0].color"),
|
|
||||||
};
|
|
||||||
light.update_values(shader);
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_values(light: *Light, shader: rl.Shader) void {
|
|
||||||
const enabled: i32 = @intFromBool(light.enabled);
|
|
||||||
rl.SetShaderValue(shader, light.enabledLoc, &enabled, rl.ShaderUniformDataType.SHADER_UNIFORM_INT);
|
|
||||||
|
|
||||||
const lightType: i32 = @intFromEnum(light.type);
|
|
||||||
rl.SetShaderValue(shader, light.typeLoc, &lightType, rl.ShaderUniformDataType.SHADER_UNIFORM_INT);
|
|
||||||
|
|
||||||
const position = [3]f32{ light.position.x, light.position.y, light.position.z };
|
|
||||||
rl.SetShaderValue(shader, light.positionLoc, &position, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC3);
|
|
||||||
|
|
||||||
const target = [3]f32{ light.target.x, light.target.y, light.target.z };
|
|
||||||
rl.SetShaderValue(shader, light.targetLoc, &target, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC3);
|
|
||||||
|
|
||||||
const color = [4]f32{
|
|
||||||
@as(f32, @floatFromInt(light.color.r)) / 255.0,
|
|
||||||
@as(f32, @floatFromInt(light.color.g)) / 255.0,
|
|
||||||
@as(f32, @floatFromInt(light.color.b)) / 255.0,
|
|
||||||
@as(f32, @floatFromInt(light.color.a)) / 255.0,
|
|
||||||
};
|
|
||||||
rl.SetShaderValue(shader, light.colorLoc, &color, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn get_camera_projection(camera: *const rl.Camera3D) rl.Matrix {
|
|
||||||
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
|
||||||
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
|
||||||
|
|
||||||
if (camera.projection == .CAMERA_PERSPECTIVE) {
|
|
||||||
return rl.MatrixPerspective(camera.fovy*rl.DEG2RAD, screen_width/screen_height, rl.RL_CULL_DISTANCE_NEAR, rl.RL_CULL_DISTANCE_FAR);
|
|
||||||
} else if (camera.projection == .CAMERA_ORTHOGRAPHIC) {
|
|
||||||
const aspect = screen_width/screen_height;
|
|
||||||
const top = camera.fovy/2.0;
|
|
||||||
const right = top*aspect;
|
|
||||||
|
|
||||||
return rl.MatrixOrtho(-right, right, -top, top, rl.RL_CULL_DISTANCE_NEAR, rl.RL_CULL_DISTANCE_FAR);
|
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_screen_direction_from_camera(mat_proj: *const rl.Matrix, mat_view: *const rl.Matrix, point: rl.Vector2) rl.Vector3 {
|
|
||||||
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
|
||||||
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
|
||||||
|
|
||||||
const ndc_x = (2.0*point.x) / screen_width - 1.0;
|
|
||||||
const ndc_y = 1.0 - (2.0*point.y) / screen_height;
|
|
||||||
|
|
||||||
var near_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 0.0 }, mat_proj.*, mat_view.*);
|
|
||||||
var far_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 1.0 }, mat_proj.*, mat_view.*);
|
|
||||||
|
|
||||||
return rl.Vector3Subtract(far_point, near_point).normalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_preffered_distance_to_box(camera: *const rl.Camera3D, box: rl.BoundingBox) f32 {
|
|
||||||
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
|
||||||
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
|
||||||
const margin = @min(screen_width, screen_height)*0.1;
|
|
||||||
|
|
||||||
const box_size = box.max.sub(box.min);
|
|
||||||
|
|
||||||
const max_model_scale = @min((screen_width-2*margin)/box_size.x, (screen_height-2*margin)/box_size.y);
|
|
||||||
// const model_screen_width = box_size.x * max_model_scale;
|
|
||||||
const model_screen_height = box_size.y * max_model_scale;
|
|
||||||
|
|
||||||
const mat_proj = get_camera_projection(camera);
|
|
||||||
const mat_view = rl.MatrixIdentity(); // rl.MatrixLookAt(camera.position, camera.target, camera.up);
|
|
||||||
|
|
||||||
const screen_middle = rl.Vector2{ .x = screen_width/2, .y = screen_height/2 };
|
|
||||||
const box_top_middle = screen_middle.add(.{ .y = -model_screen_height/2 });
|
|
||||||
|
|
||||||
const middle_dir = get_screen_direction_from_camera(&mat_proj, &mat_view, screen_middle);
|
|
||||||
const top_middle_dir = get_screen_direction_from_camera(&mat_proj, &mat_view, box_top_middle);
|
|
||||||
const angle = top_middle_dir.angleBetween(middle_dir);
|
|
||||||
const distance = 1/@tan(angle) * (box_size.y/2) + box_size.z/4;
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
var program_memory = try std.heap.page_allocator.alloc(u8, megabytes(2));
|
const memory = try std.heap.page_allocator.alloc(u8, megabytes(5));
|
||||||
var fba = std.heap.FixedBufferAllocator.init(program_memory);
|
var fba = std.heap.FixedBufferAllocator.init(memory);
|
||||||
const allocator = fba.allocator();
|
const allocator = fba.allocator();
|
||||||
|
|
||||||
var chip = try ChipContext.init(&allocator);
|
|
||||||
defer chip.deinit();
|
|
||||||
|
|
||||||
chip.set_memory(0x200, @embedFile("ROMs/br8kout.ch8"));
|
|
||||||
// const file = try std.fs.cwd().openFile("ROMs/morse_demo.ch8", .{ .mode = .read_only });
|
|
||||||
// defer file.close();
|
|
||||||
// try chip.set_memory_from_file(0x200, file);
|
|
||||||
|
|
||||||
const pixel_size = 20;
|
|
||||||
const initial_screen_width: i32 = @as(i32, chip.display_width) * pixel_size;
|
|
||||||
const initial_screen_height: i32 = @as(i32, chip.display_height) * pixel_size;
|
|
||||||
|
|
||||||
rl.SetConfigFlags(rl.ConfigFlags{ .FLAG_WINDOW_RESIZABLE = true });
|
rl.SetConfigFlags(rl.ConfigFlags{ .FLAG_WINDOW_RESIZABLE = true });
|
||||||
rl.InitWindow(initial_screen_width, initial_screen_height, "CHIP-8");
|
rl.InitWindow(1024, 720, "CHIP-8");
|
||||||
defer rl.CloseWindow();
|
defer rl.CloseWindow();
|
||||||
|
|
||||||
rl.InitAudioDevice();
|
rl.InitAudioDevice();
|
||||||
@ -175,243 +26,33 @@ pub fn main() anyerror!void {
|
|||||||
|
|
||||||
rl.SetTargetFPS(60);
|
rl.SetTargetFPS(60);
|
||||||
|
|
||||||
|
var ctx = GlobalContext.init();
|
||||||
|
var main_scene = try MainScene.init(allocator, &ctx);
|
||||||
|
defer main_scene.deinit();
|
||||||
|
|
||||||
|
main_scene.chip.set_memory(0x200, @embedFile("ROMs/br8kout.ch8"));
|
||||||
|
|
||||||
const font_size = 24;
|
const font_size = 24;
|
||||||
const font_ttf_default_numchars = 95; // TTF font generation default charset: 95 glyphs (ASCII 32..126)
|
const font_ttf_default_numchars = 95; // TTF font generation default charset: 95 glyphs (ASCII 32..126)
|
||||||
const font = rl.LoadFontEx("src/fonts/generic-mono.otf", font_size, null, font_ttf_default_numchars);
|
const font = rl.LoadFontEx("src/assets/fonts/generic-mono.otf", font_size, null, font_ttf_default_numchars);
|
||||||
defer rl.UnloadFont(font);
|
defer rl.UnloadFont(font);
|
||||||
|
|
||||||
const sample_rate = 44100;
|
|
||||||
var data = try allocator.alloc(i16, sample_rate);
|
|
||||||
defer allocator.free(data);
|
|
||||||
var chip_wave = rl.Wave{
|
|
||||||
.frameCount = sample_rate,
|
|
||||||
.sampleRate = sample_rate,
|
|
||||||
.sampleSize = 16,
|
|
||||||
.channels = 1,
|
|
||||||
.data = @ptrCast(data.ptr),
|
|
||||||
};
|
|
||||||
gen_sin_wave(&chip_wave, 440);
|
|
||||||
var chip_sound = rl.LoadSoundFromWave(chip_wave);
|
|
||||||
defer rl.UnloadSound(chip_sound);
|
|
||||||
rl.SetSoundVolume(chip_sound, 0.2);
|
|
||||||
|
|
||||||
var raylib_chip = RaylibChip.init(&chip, &chip_sound);
|
|
||||||
// var raylib_chip = RaylibChip.init(&chip, null);
|
|
||||||
raylib_chip.tick_speed = 500;
|
|
||||||
raylib_chip.timer_speed = 60;
|
|
||||||
|
|
||||||
var edit_mode = false;
|
|
||||||
var tab: Tab = .MemoryView;
|
|
||||||
|
|
||||||
// var temp_mem = [1]u8{0xAA} ** (16*80 + 10);
|
|
||||||
// var memory_view = MemoryView.init(&temp_mem, &font, 32);
|
|
||||||
// var memory_view = MemoryView.init(chip.memory, &font, font_size, &allocator);
|
|
||||||
// var selected_memory = Range{};
|
|
||||||
// var ui = UI.init();
|
|
||||||
|
|
||||||
var camera = rl.Camera3D{
|
|
||||||
.position = rl.Vector3.new(0.0, 0, 0.0),
|
|
||||||
.target = rl.Vector3.new(0.0, 0.0, 0.0),
|
|
||||||
.up = rl.Vector3.new(0.0, 1.0, 0.0),
|
|
||||||
.fovy = 45.0,
|
|
||||||
.projection = rl.CameraProjection.CAMERA_PERSPECTIVE,
|
|
||||||
};
|
|
||||||
|
|
||||||
var model = rl.LoadModel("src/assets/models/emulator.obj");
|
|
||||||
var model_bbox = rl.GetModelBoundingBox(model);
|
|
||||||
|
|
||||||
var model_position = rl.Vector3{ };
|
|
||||||
var model_size = model_bbox.max.sub(model_bbox.min);
|
|
||||||
|
|
||||||
const shader = rl.LoadShader("src/shaders/lighting.vs", "src/shaders/lighting.fs");
|
|
||||||
shader.locs.?[@intFromEnum(rl.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW)] = rl.GetShaderLocation(shader, "viewPos");
|
|
||||||
const ambientLoc = rl.GetShaderLocation(shader, "ambient");
|
|
||||||
rl.SetShaderValue(shader, ambientLoc, &[4]f32{ 0.2, 0.2, 0.2, 1.0 }, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4);
|
|
||||||
|
|
||||||
for (0..@intCast(model.materialCount)) |i| {
|
|
||||||
model.materials.?[i].shader = shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
var light = Light.create(Light.LightType.DIRECTIONAL, rl.Vector3.new(0.2, 0, -0.2), rl.Vector3.zero(), rl.WHITE, shader);
|
|
||||||
|
|
||||||
std.debug.print("dimensions {}", .{model_size});
|
|
||||||
|
|
||||||
var camera_turn_vel = rl.Vector3Zero();
|
|
||||||
var camera_target_orientation: ?rl.Vector3 = null;
|
|
||||||
var previous_click_time: f64 = 0.0;
|
|
||||||
|
|
||||||
// rl.DisableCursor();
|
|
||||||
while (!rl.WindowShouldClose()) {
|
while (!rl.WindowShouldClose()) {
|
||||||
var dt = rl.GetFrameTime();
|
var dt = rl.GetFrameTime();
|
||||||
raylib_chip.update(dt);
|
main_scene.raylib_chip.update_input();
|
||||||
|
main_scene.raylib_chip.update(dt);
|
||||||
|
|
||||||
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_TAB)) {
|
main_scene.update(dt);
|
||||||
edit_mode = !edit_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (edit_mode) {
|
|
||||||
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_ONE)) {
|
|
||||||
tab = .MemoryView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mouse_delta = rl.GetMouseDelta();
|
|
||||||
|
|
||||||
if (rl.IsWindowResized()) {
|
|
||||||
const distance = get_preffered_distance_to_box(&camera, model_bbox);
|
|
||||||
const direction = camera.position.sub(model_position).normalize();
|
|
||||||
camera.position = model_position.add(direction.scale(distance));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rl.Vector3Equals(camera.position, rl.Vector3Zero()) == 1) {
|
|
||||||
const distance = get_preffered_distance_to_box(&camera, model_bbox);
|
|
||||||
camera.target = model_position;
|
|
||||||
camera.position = model_position.sub(rl.Vector3.new(0, 0, 1).scale(distance));
|
|
||||||
}
|
|
||||||
|
|
||||||
var camera_turn_acc = rl.Vector3Zero();
|
|
||||||
if (rl.IsMouseButtonDown(rl.MouseButton.MOUSE_BUTTON_LEFT)) {
|
|
||||||
if (@fabs(mouse_delta.x) > 5) {
|
|
||||||
const rotation_speed = 2; // Radians/second
|
|
||||||
camera_turn_acc.x = -rotation_speed*mouse_delta.x;
|
|
||||||
}
|
|
||||||
if (@fabs(mouse_delta.x) < 5) {
|
|
||||||
camera_turn_vel = camera_turn_vel.scale(0.90); // Holding drag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rl.IsMouseButtonPressed(rl.MouseButton.MOUSE_BUTTON_LEFT)) {
|
|
||||||
camera_target_orientation = null;
|
|
||||||
|
|
||||||
const now = rl.GetTime();
|
|
||||||
const duration_between_clicks = now - previous_click_time;
|
|
||||||
if (duration_between_clicks < 0.3) {
|
|
||||||
const ray = rl.GetMouseRay(rl.GetMousePosition(), camera);
|
|
||||||
const collision = rl.GetRayCollisionBox(ray, model_bbox);
|
|
||||||
if (collision.hit) {
|
|
||||||
const front_face_normal = rl.Vector3.new(0, 0, -1);
|
|
||||||
const back_face_normal = rl.Vector3.new(0, 0, 1);
|
|
||||||
if (rl.Vector3Equals(collision.normal, front_face_normal) == 1) {
|
|
||||||
camera_target_orientation = front_face_normal;
|
|
||||||
} else if (rl.Vector3Equals(collision.normal, back_face_normal) == 1) {
|
|
||||||
camera_target_orientation = back_face_normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previous_click_time = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (camera_target_orientation) |target| {
|
|
||||||
const current_direction = camera.position.sub(model_position).normalize();
|
|
||||||
const current_angle = std.math.atan2(f32, current_direction.z, current_direction.x);
|
|
||||||
const target_angle = std.math.atan2(f32, target.z, target.x);
|
|
||||||
const diff_angle = std.math.pi - @mod((target_angle - current_angle) + 3*std.math.pi, 2*std.math.pi);
|
|
||||||
if (@fabs(diff_angle) < 0.001) {
|
|
||||||
camera_turn_vel.x = 0;
|
|
||||||
camera_target_orientation = null;
|
|
||||||
} else {
|
|
||||||
camera_turn_vel.x = diff_angle*3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
camera_turn_vel = camera_turn_vel.scale(0.95); // Ambient drag
|
|
||||||
camera_turn_vel = camera_turn_vel.add(camera_turn_acc.scale(dt));
|
|
||||||
|
|
||||||
const camera_min_vel = 0;
|
|
||||||
if (camera_turn_vel.length() > camera_min_vel) {
|
|
||||||
const rotation = rl.MatrixRotate(camera.up.normalize(), camera_turn_vel.x*dt);
|
|
||||||
var view = rl.Vector3Subtract(camera.position, camera.target);
|
|
||||||
view = rl.Vector3Transform(view, rotation);
|
|
||||||
camera.position = rl.Vector3Add(camera.target, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
// {
|
|
||||||
// var matProj = rl.MatrixIdentity();
|
|
||||||
// // projection = CAMERA_PERSPECTIVE
|
|
||||||
// matProj = rl.MatrixPerspective(camera.fovy*rl.DEG2RAD, (screen_width/screen_height), rl.RL_CULL_DISTANCE_NEAR, rl.RL_CULL_DISTANCE_FAR);
|
|
||||||
//
|
|
||||||
// var matView = rl.MatrixLookAt(camera.position, camera.target, camera.up);
|
|
||||||
// // Convert world position vector to quaternion
|
|
||||||
// var worldPos = rl.Vector4{ .x = position.x, .y = position.y, .z = position.z, .w = 1.0 };
|
|
||||||
//
|
|
||||||
// std.debug.print("worldPos {}\n", .{worldPos});
|
|
||||||
// // Transform world position to view
|
|
||||||
// worldPos = rl.QuaternionTransform(worldPos, matView);
|
|
||||||
//
|
|
||||||
// // Transform result to projection (clip space position)
|
|
||||||
// worldPos = rl.QuaternionTransform(worldPos, matProj);
|
|
||||||
//
|
|
||||||
// // Calculate normalized device coordinates (inverted y)
|
|
||||||
// var ndcPos = rl.Vector3.new( worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w );
|
|
||||||
//
|
|
||||||
// // Calculate 2d screen position vector
|
|
||||||
// screen_position = rl.Vector2{ .x = (ndcPos.x + 1.0)/2.0*screen_width, .y = (ndcPos.y + 1.0)/2.0*screen_height };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const target_screen_position = rl.Vector2{ .x = screen_width/2, .y = screen_height*0.1 };
|
|
||||||
// {
|
|
||||||
// var matProj = get_camera_projection(&camera);
|
|
||||||
// var matView = rl.MatrixLookAt(camera.position, camera.target, camera.up);
|
|
||||||
//
|
|
||||||
// const ndc_x = (2.0*target_screen_position.x) / screen_width - 1.0;
|
|
||||||
// const ndc_y = 1.0 - (2.0*target_screen_position.y) / screen_height;
|
|
||||||
//
|
|
||||||
// var near_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 0.0 }, matProj, matView);
|
|
||||||
// var far_point = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = 1.0 }, matProj, matView);
|
|
||||||
//
|
|
||||||
// var direction = rl.Vector3Subtract(far_point, near_point).normalize();
|
|
||||||
//
|
|
||||||
// var origin: rl.Vector3 = undefined;
|
|
||||||
// if (camera.projection == .CAMERA_PERSPECTIVE) {
|
|
||||||
// origin = camera.position;
|
|
||||||
// } else {
|
|
||||||
// origin = rl.Vector3Unproject(.{ .x = ndc_x, .y = ndc_y, .z = -1.0 }, matProj, matView);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var world_pos = origin.add(direction.scale(3));
|
|
||||||
//
|
|
||||||
// model_position = world_pos;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const cameraPos = [3]f32{ camera.position.x, camera.position.y, camera.position.z };
|
|
||||||
rl.SetShaderValue(shader, shader.locs.?[@intFromEnum(rl.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW)], &cameraPos, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC3);
|
|
||||||
light.update_values(shader);
|
|
||||||
|
|
||||||
rl.BeginDrawing();
|
rl.BeginDrawing();
|
||||||
|
{
|
||||||
rl.ClearBackground(.{ .r = 33, .g = 33, .b = 33, .a = 255 });
|
rl.ClearBackground(.{ .r = 33, .g = 33, .b = 33, .a = 255 });
|
||||||
|
|
||||||
|
rl.BeginMode3D(ctx.camera);
|
||||||
rl.BeginMode3D(camera);
|
main_scene.draw();
|
||||||
rl.DrawModel(model, model_position, 1.0, rl.WHITE);
|
|
||||||
|
|
||||||
// rl.rlPushMatrix();
|
|
||||||
// rl.rlSetLineWidth(2);
|
|
||||||
// rl.rlTranslatef(model_position.x, model_position.y, model_position.z);
|
|
||||||
// rl.DrawBoundingBox(model_bbox, rl.GREEN);
|
|
||||||
// rl.rlPopMatrix();
|
|
||||||
rl.EndMode3D();
|
rl.EndMode3D();
|
||||||
|
}
|
||||||
|
rl.EndDrawing();
|
||||||
|
|
||||||
rl.EndDrawing();
|
|
||||||
|
|
||||||
// if (!edit_mode) {
|
|
||||||
// rl.ClearBackground(rl.DARKGRAY);
|
|
||||||
//
|
|
||||||
// const scale_x = @divFloor(screen_width, chip.display_width);
|
|
||||||
// const scale_y = @divFloor(screen_height, chip.display_height);
|
|
||||||
// const min_scale = @min(scale_x, scale_y);
|
|
||||||
//
|
|
||||||
// const display_width = chip.display_width * min_scale;
|
|
||||||
// const display_height = chip.display_height * min_scale;
|
|
||||||
// const display_x = @divFloor(screen_width - display_width, 2);
|
|
||||||
// const display_y = @divFloor(screen_height - display_height, 2);
|
|
||||||
// raylib_chip.render(display_x, display_y, display_width, display_height);
|
|
||||||
// } else {
|
|
||||||
// rl.ClearBackground(rl.RAYWHITE);
|
|
||||||
// ui.update();
|
|
||||||
//
|
|
||||||
// if (tab == .MemoryView) {
|
|
||||||
// try memory_view.show(&ui, 0, 0, @floatFromInt(screen_width), @floatFromInt(screen_height), &selected_memory);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,12 @@ on_color: rl.Color,
|
|||||||
off_color: rl.Color,
|
off_color: rl.Color,
|
||||||
timer_speed: f32,
|
timer_speed: f32,
|
||||||
tick_speed: f32,
|
tick_speed: f32,
|
||||||
beep_sound: ?*rl.Sound,
|
beep_sound: ?rl.Sound,
|
||||||
|
|
||||||
tick_time: f32,
|
tick_time: f32,
|
||||||
timer_time: f32,
|
timer_time: f32,
|
||||||
|
|
||||||
pub fn init(chip: *ChipContext, beep_sound: ?*rl.Sound) Self {
|
pub fn init(chip: *ChipContext, beep_sound: ?rl.Sound) Self {
|
||||||
return Self{
|
return Self{
|
||||||
.chip = chip,
|
.chip = chip,
|
||||||
.off_color = rl.BLACK,
|
.off_color = rl.BLACK,
|
||||||
@ -22,7 +22,7 @@ pub fn init(chip: *ChipContext, beep_sound: ?*rl.Sound) Self {
|
|||||||
.tick_speed = 500,
|
.tick_speed = 500,
|
||||||
.tick_time = 0,
|
.tick_time = 0,
|
||||||
.timer_time = 0,
|
.timer_time = 0,
|
||||||
.beep_sound = beep_sound
|
.beep_sound = beep_sound,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,29 +72,24 @@ pub fn update(self: *Self, dt: f32) void {
|
|||||||
|
|
||||||
if (self.beep_sound) |beep_sound| {
|
if (self.beep_sound) |beep_sound| {
|
||||||
if (self.chip.ST > 0) {
|
if (self.chip.ST > 0) {
|
||||||
if (!rl.IsSoundPlaying(beep_sound.*)) {
|
if (!rl.IsSoundPlaying(beep_sound)) {
|
||||||
rl.PlaySound(beep_sound.*);
|
rl.PlaySound(beep_sound);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (rl.IsSoundPlaying(beep_sound.*)) {
|
if (rl.IsSoundPlaying(beep_sound)) {
|
||||||
rl.StopSound(beep_sound.*);
|
rl.StopSound(beep_sound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Self, x: i32, y: i32, width: i32, height: i32) void {
|
pub fn render(self: *Self) void {
|
||||||
const pixel_width = @divFloor(width, self.chip.display_width);
|
rl.DrawRectangle(0, 0, self.chip.display_width, self.chip.display_height, self.off_color);
|
||||||
const pixel_height = @divFloor(height, self.chip.display_height);
|
|
||||||
|
|
||||||
rl.DrawRectangle(x, y, width, height, self.off_color);
|
for (0..self.chip.display_height) |y| {
|
||||||
|
for (0..self.chip.display_width) |x| {
|
||||||
for (0..self.chip.display_height) |oy| {
|
if (self.chip.display_get(@intCast(x), @intCast(y))) {
|
||||||
for (0..self.chip.display_width) |ox| {
|
rl.DrawPixel(@intCast(x), @intCast(y), self.on_color);
|
||||||
if (self.chip.display_get(@intCast(ox), @intCast(oy))) {
|
|
||||||
const ix = x + @as(i32, @intCast(ox)) * pixel_width;
|
|
||||||
const iy = y + @as(i32, @intCast(oy)) * pixel_height;
|
|
||||||
rl.DrawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,6 @@ void main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0)));
|
finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0)));
|
||||||
finalColor += texelColor*(ambient/10.0)*colDiffuse;
|
|
||||||
|
|
||||||
// Gamma correction
|
// Gamma correction
|
||||||
finalColor = pow(finalColor, vec4(1.0/1.9));
|
finalColor = pow(finalColor, vec4(1.0/1.9));
|
Loading…
Reference in New Issue
Block a user