Compare commits
2 Commits
df4dadc29d
...
ce31614950
Author | SHA1 | Date | |
---|---|---|---|
ce31614950 | |||
8a1f40cb62 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "libs/raylib-zig"]
|
[submodule "libs/raylib-zig"]
|
||||||
path = libs/raylib-zig
|
path = libs/raylib-zig
|
||||||
url = git@github.com:Not-Nik/raylib-zig.git
|
url = git@github.com:Not-Nik/raylib-zig.git
|
||||||
|
[submodule "libs/raylib"]
|
||||||
|
path = libs/raylib
|
||||||
|
url = https://github.com/ryupold/raylib.zig
|
||||||
|
11
build.zig
11
build.zig
@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const rl = @import("libs/raylib-zig/build.zig");
|
// const rl = @import("libs/raylib-zig/build.zig");
|
||||||
|
const raylib = @import("libs/raylib/build.zig");
|
||||||
|
|
||||||
pub fn build(b: *std.Build) !void {
|
pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
@ -31,9 +32,11 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
const exe = b.addExecutable(.{ .name = "chip8-zig", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target });
|
const exe = b.addExecutable(.{ .name = "chip8-zig", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target });
|
||||||
|
|
||||||
rl.link(b, exe, target, optimize);
|
raylib.addTo(b, exe, target, optimize);
|
||||||
exe.addModule("raylib", rl.getModule(b, "libs/raylib-zig"));
|
|
||||||
exe.addModule("raylib-math", rl.math.getModule(b, "libs/raylib-zig"));
|
// rl.link(b, exe, target, optimize);
|
||||||
|
// exe.addModule("raylib", rl.getModule(b, "libs/raylib-zig"));
|
||||||
|
// exe.addModule("raylib-math", rl.math.getModule(b, "libs/raylib-zig"));
|
||||||
|
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
const run_step = b.step("run", "Run chip8-zig");
|
const run_step = b.step("run", "Run chip8-zig");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.{
|
.{
|
||||||
.name = "zig-raylib",
|
.name = "chip8-zig",
|
||||||
.version = "0.1.0",
|
.version = "0.1.0",
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.raylib = .{
|
.raylib = .{
|
||||||
|
1
libs/raylib
Submodule
1
libs/raylib
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 642458242078d4e3299ea31c9a6c415c65a4e907
|
BIN
src/ROMs/br8kout.ch8
Normal file
BIN
src/ROMs/br8kout.ch8
Normal file
Binary file not shown.
BIN
src/assets/fonts/generic-mono.otf
Normal file
BIN
src/assets/fonts/generic-mono.otf
Normal file
Binary file not shown.
BIN
src/assets/models/Buttons texture.png
Normal file
BIN
src/assets/models/Buttons texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 504 KiB |
BIN
src/assets/models/emulator.blend
Normal file
BIN
src/assets/models/emulator.blend
Normal file
Binary file not shown.
42
src/assets/models/emulator.mtl
Normal file
42
src/assets/models/emulator.mtl
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Blender 3.5.1 MTL File: 'emulator.blend'
|
||||||
|
# www.blender.org
|
||||||
|
|
||||||
|
newmtl Behind_buttons
|
||||||
|
Ns 0.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.001734 0.004824 0.007141
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.550000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Button
|
||||||
|
Ns 250.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Kd Buttons texture.png
|
||||||
|
|
||||||
|
newmtl Case
|
||||||
|
Ns 298.057037
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.057402 0.087001 0.228570
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Screen
|
||||||
|
Ns 250.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.010867 0.020340 0.012183
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
4082
src/assets/models/emulator.obj
Normal file
4082
src/assets/models/emulator.obj
Normal file
File diff suppressed because it is too large
Load Diff
15
src/chip.zig
15
src/chip.zig
@ -50,13 +50,15 @@ pub fn init(allocator: *const Allocator) !Self {
|
|||||||
const display_width = 64;
|
const display_width = 64;
|
||||||
const display_height = 32;
|
const display_height = 32;
|
||||||
const memory_size = 4096;
|
const memory_size = 4096;
|
||||||
|
const memory = try allocator.alloc(u8, memory_size);
|
||||||
|
@memset(memory, 0);
|
||||||
|
|
||||||
var self = Self {
|
var self = Self {
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.display = try allocator.alloc(bool, display_width * display_height),
|
.display = try allocator.alloc(bool, display_width * display_height),
|
||||||
.display_width = display_width,
|
.display_width = display_width,
|
||||||
.display_height = display_height,
|
.display_height = display_height,
|
||||||
.memory = try allocator.alloc(u8, memory_size),
|
.memory = memory,
|
||||||
.stack = [1]u16{0} ** 16,
|
.stack = [1]u16{0} ** 16,
|
||||||
.V = [1]u8{0} ** 16,
|
.V = [1]u8{0} ** 16,
|
||||||
.I = 0,
|
.I = 0,
|
||||||
@ -388,12 +390,11 @@ pub fn run_instruction(self: *Self, inst: u16) !void {
|
|||||||
self.V[x] = (self.V[x] >> 1);
|
self.V[x] = (self.V[x] >> 1);
|
||||||
|
|
||||||
} else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN Vx, Vy
|
} else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN Vx, Vy
|
||||||
const Vx = get_inst_x(inst);
|
const x = get_inst_x(inst);
|
||||||
const Vy = get_inst_y(inst);
|
const y = get_inst_y(inst);
|
||||||
const sub: i16 = self.V[Vy] - self.V[Vx];
|
const result = @subWithOverflow(self.V[y], self.V[x]);
|
||||||
const not_borrow = @intFromBool(sub > 0);
|
self.V[x] = result[0];
|
||||||
self.V[Vx] = @intCast(sub & 0x00FF);
|
self.V[0xF] = (1 - result[1]);
|
||||||
self.V[0xF] = not_borrow;
|
|
||||||
|
|
||||||
} else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1
|
} else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1
|
||||||
const Vx = get_inst_x(inst);
|
const Vx = get_inst_x(inst);
|
||||||
|
380
src/main.zig
380
src/main.zig
@ -1,11 +1,21 @@
|
|||||||
const rl = @import("raylib");
|
const rl = @import("raylib");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const print = std.debug.print;
|
||||||
|
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 MemoryView = @import("memory-view.zig").MemoryView;
|
||||||
|
const Range = @import("memory-view.zig").Range;
|
||||||
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
|
const Tab = enum {
|
||||||
|
MemoryView
|
||||||
|
};
|
||||||
|
|
||||||
pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
|
pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
|
||||||
assert(wave.sampleSize == 16); // Only 16 bits are supported
|
assert(wave.sampleSize == 16); // Only 16 bits are supported
|
||||||
|
|
||||||
@ -25,6 +35,120 @@ 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));
|
var program_memory = try std.heap.page_allocator.alloc(u8, megabytes(2));
|
||||||
var fba = std.heap.FixedBufferAllocator.init(program_memory);
|
var fba = std.heap.FixedBufferAllocator.init(program_memory);
|
||||||
@ -33,25 +157,28 @@ pub fn main() anyerror!void {
|
|||||||
var chip = try ChipContext.init(&allocator);
|
var chip = try ChipContext.init(&allocator);
|
||||||
defer chip.deinit();
|
defer chip.deinit();
|
||||||
|
|
||||||
chip.set_memory(0x200, @embedFile("ROMs/morse_demo.ch8"));
|
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();
|
||||||
// const file = try std.fs.cwd().openFile("ROMs/morse_demo.ch8", .{ .mode = .read_only });
|
// try chip.set_memory_from_file(0x200, file);
|
||||||
// defer file.close();
|
|
||||||
// try chip.set_memory_from_file(0x200, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pixel_size = 20;
|
const pixel_size = 20;
|
||||||
const screen_width: i32 = @as(i32, chip.display_width) * pixel_size;
|
const initial_screen_width: i32 = @as(i32, chip.display_width) * pixel_size;
|
||||||
const screen_height: i32 = @as(i32, chip.display_height) * pixel_size;
|
const initial_screen_height: i32 = @as(i32, chip.display_height) * pixel_size;
|
||||||
|
|
||||||
rl.initWindow(screen_width, screen_height, "CHIP-8");
|
rl.SetConfigFlags(rl.ConfigFlags{ .FLAG_WINDOW_RESIZABLE = true });
|
||||||
defer rl.closeWindow();
|
rl.InitWindow(initial_screen_width, initial_screen_height, "CHIP-8");
|
||||||
|
defer rl.CloseWindow();
|
||||||
|
|
||||||
rl.initAudioDevice();
|
rl.InitAudioDevice();
|
||||||
defer rl.closeAudioDevice();
|
defer rl.CloseAudioDevice();
|
||||||
|
|
||||||
rl.setTargetFPS(60);
|
rl.SetTargetFPS(60);
|
||||||
|
|
||||||
|
const font_size = 24;
|
||||||
|
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);
|
||||||
|
defer rl.UnloadFont(font);
|
||||||
|
|
||||||
const sample_rate = 44100;
|
const sample_rate = 44100;
|
||||||
var data = try allocator.alloc(i16, sample_rate);
|
var data = try allocator.alloc(i16, sample_rate);
|
||||||
@ -64,24 +191,227 @@ pub fn main() anyerror!void {
|
|||||||
.data = @ptrCast(data.ptr),
|
.data = @ptrCast(data.ptr),
|
||||||
};
|
};
|
||||||
gen_sin_wave(&chip_wave, 440);
|
gen_sin_wave(&chip_wave, 440);
|
||||||
var chip_sound = rl.loadSoundFromWave(chip_wave);
|
var chip_sound = rl.LoadSoundFromWave(chip_wave);
|
||||||
defer rl.unloadSound(chip_sound);
|
defer rl.UnloadSound(chip_sound);
|
||||||
rl.setSoundVolume(chip_sound, 0.2);
|
rl.SetSoundVolume(chip_sound, 0.2);
|
||||||
|
|
||||||
var raylib_chip = RaylibChip.init(&chip, &chip_sound);
|
var raylib_chip = RaylibChip.init(&chip, &chip_sound);
|
||||||
|
// var raylib_chip = RaylibChip.init(&chip, null);
|
||||||
raylib_chip.tick_speed = 500;
|
raylib_chip.tick_speed = 500;
|
||||||
raylib_chip.timer_speed = 60;
|
raylib_chip.timer_speed = 60;
|
||||||
|
|
||||||
while (!rl.windowShouldClose()) {
|
var edit_mode = false;
|
||||||
var dt = rl.getFrameTime();
|
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()) {
|
||||||
|
var dt = rl.GetFrameTime();
|
||||||
raylib_chip.update(dt);
|
raylib_chip.update(dt);
|
||||||
|
|
||||||
{
|
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_TAB)) {
|
||||||
rl.beginDrawing();
|
edit_mode = !edit_mode;
|
||||||
defer rl.endDrawing();
|
|
||||||
|
|
||||||
rl.clearBackground(rl.Color.white);
|
|
||||||
raylib_chip.render(0, 0, screen_width, screen_height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.ClearBackground(.{ .r = 33, .g = 33, .b = 33, .a = 255 });
|
||||||
|
|
||||||
|
|
||||||
|
rl.BeginMode3D(camera);
|
||||||
|
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.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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
340
src/memory-view.zig
Normal file
340
src/memory-view.zig
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
const std = @import("std");
|
||||||
|
const UILib = @import("ui.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const UI = UILib.UI;
|
||||||
|
const HorizontalLayout = UILib.HorizontalLayout;
|
||||||
|
|
||||||
|
fn nibble_to_char(nibble: u4) u8 {
|
||||||
|
if (0 <= nibble and nibble <= 9) {
|
||||||
|
return '0' + @as(u8, nibble);
|
||||||
|
} else {
|
||||||
|
return 'A' + @as(u8, nibble - 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hex_to_strz(str: [:0]u8, number: u32) void {
|
||||||
|
var i: i32 = @intCast(str.len-1);
|
||||||
|
var leftover = number;
|
||||||
|
while (leftover > 0 and i >= 0): (leftover >>= 4) {
|
||||||
|
const nibble: u4 = @intCast(leftover & 0b1111);
|
||||||
|
str[@intCast(i)] = nibble_to_char(nibble);
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i >= 0): (i -= 1) {
|
||||||
|
str[@intCast(i)] = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp(value: f32, min: f32, max: f32) f32 {
|
||||||
|
return @min(@max(value, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Range = struct { start: u32 = 0, end: u32 = 0 };
|
||||||
|
|
||||||
|
pub const MemoryView = struct {
|
||||||
|
const Highlight = struct {
|
||||||
|
range: Range,
|
||||||
|
color: rl.Color
|
||||||
|
};
|
||||||
|
|
||||||
|
font: *const rl.Font,
|
||||||
|
font_size: f32,
|
||||||
|
|
||||||
|
base_address: u32,
|
||||||
|
memory: []u8,
|
||||||
|
scroll: f32 = 0,
|
||||||
|
|
||||||
|
scrolling: bool = false,
|
||||||
|
selecting: bool = false,
|
||||||
|
editing: bool = false,
|
||||||
|
|
||||||
|
editing_byte: u32 = 0,
|
||||||
|
selection_pivot: u32 = 0,
|
||||||
|
allocator: *const Allocator,
|
||||||
|
|
||||||
|
grey_out_zeros: bool = true,
|
||||||
|
|
||||||
|
text_color: rl.Color = rl.BLACK,
|
||||||
|
dim_text_color: rl.Color = rl.GRAY,
|
||||||
|
|
||||||
|
row_width: u5 = 16,
|
||||||
|
|
||||||
|
pub fn init(memory: []u8, font: *const rl.Font, font_size: f32, allocator: *const Allocator) MemoryView {
|
||||||
|
return MemoryView{
|
||||||
|
.allocator = allocator,
|
||||||
|
.base_address = 0,
|
||||||
|
.memory = memory,
|
||||||
|
.font = font,
|
||||||
|
.font_size = font_size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_memory_row_count(self: *const MemoryView) f32 {
|
||||||
|
return @ceil(@as(f32, @floatFromInt(self.memory.len)) / @as(f32, @floatFromInt(self.row_width)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_visible_row_count(self: *const MemoryView, height: f32) f32 {
|
||||||
|
return @min(height / self.font_size, self.get_memory_row_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_scroll(self: *const MemoryView, height: f32) f32 {
|
||||||
|
return @max(self.get_memory_row_count() - self.get_visible_row_count(height), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(self: *MemoryView, ui: *UI, x: f32, y: f32, width: f32, height: f32, selection: *Range) !void {
|
||||||
|
if (ui.is_mouse_inside(x, y, width, height)) {
|
||||||
|
self.scroll -= rl.GetMouseWheelMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scroll = clamp(self.scroll, 0, self.get_max_scroll(height));
|
||||||
|
|
||||||
|
const from_row: u32 = @intFromFloat(@floor(self.scroll));
|
||||||
|
const to_row: u32 = @intFromFloat(@ceil(self.scroll + self.get_visible_row_count(height)));
|
||||||
|
|
||||||
|
const scroll_offset = @rem(self.scroll, 1) * self.font_size;
|
||||||
|
var layout = HorizontalLayout.init(x, y - scroll_offset, width, height);
|
||||||
|
|
||||||
|
layout.push(try self.show_address_column(ui, layout.next_x(), layout.next_y(), from_row, to_row));
|
||||||
|
layout.push(self.show_hex_column(ui, layout.next_x(), layout.next_y(), selection, from_row, to_row));
|
||||||
|
layout.push(self.show_ascii_column(ui, layout.next_x(), layout.next_y(), selection, from_row, to_row));
|
||||||
|
layout.push(self.show_scrollbar(ui, layout.next_x(), y, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_address_column(self: *MemoryView, ui: *UI, x: f32, y: f32, from_row: u32, to_row: u32) !f32 {
|
||||||
|
const font = self.font.*;
|
||||||
|
const font_size = self.font_size;
|
||||||
|
const margin = font_size/2;
|
||||||
|
|
||||||
|
const memory_size_f32: f32 = @floatFromInt(self.memory.len);
|
||||||
|
const memory_size_log10: u32 = @intFromFloat(@floor(@log2(memory_size_f32)/2));
|
||||||
|
const address_column_max_chars: u32 = memory_size_log10 + 1;
|
||||||
|
const row_count = (to_row - from_row);
|
||||||
|
|
||||||
|
var label_buf = try self.allocator.allocSentinel(u8, address_column_max_chars, 0);
|
||||||
|
defer self.allocator.free(label_buf);
|
||||||
|
|
||||||
|
hex_to_strz(label_buf, 0);
|
||||||
|
const column_width = rl.MeasureTextEx(font, label_buf, font_size, 0).x + 2*margin;
|
||||||
|
|
||||||
|
ui.pushTransformT(.{ .ox = x, .oy = y });
|
||||||
|
for (0..row_count) |i| {
|
||||||
|
const row = from_row + @as(u32, @intCast(i));
|
||||||
|
const row_address = self.base_address + row*self.row_width;
|
||||||
|
hex_to_strz(label_buf, row_address);
|
||||||
|
|
||||||
|
const cell_rect = rl.Rectangle {
|
||||||
|
.x = 0,
|
||||||
|
.y = self.font_size * @as(f32, @floatFromInt(i)),
|
||||||
|
.height = self.font_size,
|
||||||
|
.width = column_width
|
||||||
|
};
|
||||||
|
if (ui.is_mouse_inside_rect(cell_rect)) {
|
||||||
|
rl.DrawRectangleRec(cell_rect, rl.RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const text_pos = rl.Vector2{ .x = cell_rect.x + margin, .y = cell_rect.y };
|
||||||
|
rl.DrawTextEx(font, label_buf, text_pos, font_size, 0, self.text_color);
|
||||||
|
}
|
||||||
|
ui.popTransform();
|
||||||
|
|
||||||
|
return column_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_in_range(x: u32, from: u32, to: u32) bool {
|
||||||
|
return from <= x and x < to;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_hex_column(self: *MemoryView, ui: *UI, x: f32, y: f32, selection: *Range, from_row: u32, to_row: u32) f32 {
|
||||||
|
assert(self.row_width <= 16);
|
||||||
|
|
||||||
|
const font = self.font.*;
|
||||||
|
const font_size = self.font_size;
|
||||||
|
const margin = font_size/6;
|
||||||
|
|
||||||
|
var middle_margin: f32 = 0.0;
|
||||||
|
if (self.row_width == 16) {
|
||||||
|
middle_margin = font_size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cell_width = rl.MeasureTextEx(font, "00", font_size, 0).x;
|
||||||
|
const row_count = (to_row - from_row);
|
||||||
|
|
||||||
|
const highlights = [_]Highlight{
|
||||||
|
Highlight{ .range = .{ .start = 16, .end = 17, }, .color = rl.RED },
|
||||||
|
Highlight{ .range = selection.*, .color = rl.RED },
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.pushTransformT(.{ .ox = x, .oy = y });
|
||||||
|
var cell_bufz = [_:0]u8{0} ** 2;
|
||||||
|
for (0..row_count) |i| {
|
||||||
|
const row = from_row + @as(u32, @intCast(i));
|
||||||
|
const row_memory_idx: u32 = @intCast(row*self.row_width);
|
||||||
|
|
||||||
|
var cell_rects: [16]rl.Rectangle = undefined;
|
||||||
|
const to_column = @min(self.memory.len - row*self.row_width, self.row_width);
|
||||||
|
for (0..to_column) |column| {
|
||||||
|
var cell_rect = rl.Rectangle {
|
||||||
|
.x = (cell_width + 2*margin) * @as(f32, @floatFromInt(column)),
|
||||||
|
.y = 0,
|
||||||
|
.width = cell_width + 2*margin,
|
||||||
|
.height = self.font_size
|
||||||
|
};
|
||||||
|
|
||||||
|
if (column >= self.row_width/2) {
|
||||||
|
cell_rect.x += middle_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_rects[column] = cell_rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (highlights) |highlight| {
|
||||||
|
const highlight_start = highlight.range.start;
|
||||||
|
const highlight_end = highlight.range.end;
|
||||||
|
if (row_memory_idx >= highlight_end) continue;
|
||||||
|
if (row_memory_idx+self.row_width <= highlight_start) continue;
|
||||||
|
|
||||||
|
var highlight_from_column: u32 = undefined;
|
||||||
|
if (row_memory_idx > highlight_start) {
|
||||||
|
highlight_from_column = 0;
|
||||||
|
} else {
|
||||||
|
highlight_from_column = @mod(highlight_start, self.row_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
var highlight_to_column: u32 = undefined;
|
||||||
|
if (row_memory_idx+self.row_width <= highlight_end) {
|
||||||
|
highlight_to_column = self.row_width-1;
|
||||||
|
} else {
|
||||||
|
highlight_to_column = @mod(highlight_end-1, self.row_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
const from_cell = cell_rects[highlight_from_column];
|
||||||
|
const to_cell = cell_rects[highlight_to_column];
|
||||||
|
rl.DrawRectangleRec(rl.Rectangle{
|
||||||
|
.x = from_cell.x,
|
||||||
|
.y = from_cell.y,
|
||||||
|
.width = (to_cell.x+to_cell.width) - from_cell.x,
|
||||||
|
.height = (to_cell.y+to_cell.height) - from_cell.y,
|
||||||
|
}, highlight.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0..to_column) |column| {
|
||||||
|
const memory_idx: u32 = row_memory_idx + @as(u32, @intCast(column));
|
||||||
|
if (ui.is_holding_mouse(cell_rects[column], &self.selecting)) {
|
||||||
|
if (self.selecting) {
|
||||||
|
self.selection_pivot = memory_idx;
|
||||||
|
selection.start = memory_idx;
|
||||||
|
selection.end = memory_idx+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.selecting) {
|
||||||
|
if (ui.is_mouse_inside_rect(cell_rects[column])) {
|
||||||
|
if (memory_idx > self.selection_pivot) {
|
||||||
|
selection.start = self.selection_pivot;
|
||||||
|
selection.end = memory_idx+1;
|
||||||
|
} else {
|
||||||
|
selection.start = memory_idx;
|
||||||
|
selection.end = self.selection_pivot+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const text_pos = rl.Vector2{ .x = cell_rects[column].x + margin };
|
||||||
|
const value = self.memory[memory_idx];
|
||||||
|
hex_to_strz(&cell_bufz, value);
|
||||||
|
var color = self.text_color;
|
||||||
|
if (self.grey_out_zeros and value == 0) {
|
||||||
|
color = self.dim_text_color;
|
||||||
|
}
|
||||||
|
rl.DrawTextEx(font, &cell_bufz, text_pos, font_size, 0, color);
|
||||||
|
}
|
||||||
|
ui.translate(0, font_size);
|
||||||
|
}
|
||||||
|
ui.popTransform();
|
||||||
|
|
||||||
|
return (cell_width + 2*margin)*@as(f32, @floatFromInt(self.row_width)) + middle_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_ascii_column(self: *MemoryView, ui: *UI, x: f32, y: f32, selection: *Range, from_row: u32, to_row: u32) f32 {
|
||||||
|
const font = self.font.*;
|
||||||
|
const font_size = self.font_size;
|
||||||
|
const margin = font_size/2;
|
||||||
|
|
||||||
|
const cell_width = rl.MeasureTextEx(font, ".", font_size, 0).x;
|
||||||
|
const row_count = (to_row - from_row);
|
||||||
|
|
||||||
|
ui.pushTransformT(.{ .ox = x+margin, .oy = y });
|
||||||
|
for (0..row_count) |i| {
|
||||||
|
const row = from_row + @as(u32, @intCast(i));
|
||||||
|
|
||||||
|
const to_column = @min(self.memory.len - row*self.row_width, self.row_width);
|
||||||
|
for (0..to_column) |column| {
|
||||||
|
const memory_idx: u32 = @intCast(row*self.row_width + column);
|
||||||
|
const value = self.memory[memory_idx];
|
||||||
|
var char: u8 = 'A';
|
||||||
|
if (std.ascii.isPrint(value)) {
|
||||||
|
char = value;
|
||||||
|
} else {
|
||||||
|
char = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
const pos_x = cell_width * @as(f32, @floatFromInt(column));
|
||||||
|
if (selection.start <= memory_idx and memory_idx < selection.end) {
|
||||||
|
const cell_rect = rl.Rectangle{
|
||||||
|
.x = pos_x,
|
||||||
|
.y = 0,
|
||||||
|
.height = self.font_size,
|
||||||
|
.width = cell_width
|
||||||
|
};
|
||||||
|
rl.DrawRectangleRec(cell_rect, rl.RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_str = [2]u8 { char, 0 };
|
||||||
|
rl.DrawTextEx(font, char_str[0..1 :0], rl.Vector2{ .x = pos_x }, font_size, 0, self.text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.translate(0, font_size);
|
||||||
|
}
|
||||||
|
ui.popTransform();
|
||||||
|
|
||||||
|
return cell_width*@as(f32, @floatFromInt(self.row_width)) + 2*margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_scrollbar(self: *MemoryView, ui: *UI, x: f32, y: f32, height: f32) f32 {
|
||||||
|
const max_scroll = self.get_max_scroll(height);
|
||||||
|
if (max_scroll == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible_row_count = self.get_visible_row_count(height);
|
||||||
|
const memory_row_count = self.get_memory_row_count();
|
||||||
|
const visible_percent = visible_row_count / memory_row_count;
|
||||||
|
const scroll_percent = self.scroll / max_scroll;
|
||||||
|
const scrollbar_height = visible_percent * height;
|
||||||
|
|
||||||
|
const scrollbar = rl.Rectangle {
|
||||||
|
.x = x,
|
||||||
|
.y = y + scroll_percent * (height - scrollbar_height),
|
||||||
|
.width = self.font_size,
|
||||||
|
.height = scrollbar_height
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var color = rl.BLACK;
|
||||||
|
_ = ui.is_holding_mouse(scrollbar, &self.scrolling);
|
||||||
|
if (self.scrolling) {
|
||||||
|
const mouse_dy = rl.GetMouseDelta().y;
|
||||||
|
self.scroll += (mouse_dy / (height - scrollbar_height) * max_scroll);
|
||||||
|
self.scroll = clamp(self.scroll, 0, max_scroll);
|
||||||
|
color = rl.DARKGRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.DrawRectangleRec(scrollbar, color);
|
||||||
|
|
||||||
|
return scrollbar.width;
|
||||||
|
}
|
||||||
|
};
|
@ -8,16 +8,16 @@ 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.Color.black,
|
.off_color = rl.BLACK,
|
||||||
.on_color = rl.Color.ray_white,
|
.on_color = rl.RAYWHITE,
|
||||||
.timer_speed = 60,
|
.timer_speed = 60,
|
||||||
.tick_speed = 500,
|
.tick_speed = 500,
|
||||||
.tick_time = 0,
|
.tick_time = 0,
|
||||||
@ -28,26 +28,26 @@ pub fn init(chip: *ChipContext, beep_sound: *rl.Sound) Self {
|
|||||||
|
|
||||||
pub fn update_input(self: *Self) void {
|
pub fn update_input(self: *Self) void {
|
||||||
const keys = [16]rl.KeyboardKey{
|
const keys = [16]rl.KeyboardKey{
|
||||||
.key_x,
|
.KEY_X,
|
||||||
.key_one,
|
.KEY_ONE,
|
||||||
.key_two,
|
.KEY_TWO,
|
||||||
.key_three,
|
.KEY_THREE,
|
||||||
.key_q,
|
.KEY_Q,
|
||||||
.key_w,
|
.KEY_W,
|
||||||
.key_e,
|
.KEY_E,
|
||||||
.key_a,
|
.KEY_A,
|
||||||
.key_s,
|
.KEY_S,
|
||||||
.key_d,
|
.KEY_D,
|
||||||
.key_z,
|
.KEY_Z,
|
||||||
.key_c,
|
.KEY_C,
|
||||||
.key_four,
|
.KEY_FOUR,
|
||||||
.key_r,
|
.KEY_R,
|
||||||
.key_f,
|
.KEY_F,
|
||||||
.key_v,
|
.KEY_V,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (0.., keys) |i, key| {
|
for (0.., keys) |i, key| {
|
||||||
self.chip.input[i] = rl.isKeyDown(key);
|
self.chip.input[i] = rl.IsKeyDown(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,14 +70,15 @@ pub fn update(self: *Self, dt: f32) void {
|
|||||||
self.timer_time -= 1/self.timer_speed;
|
self.timer_time -= 1/self.timer_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const beep_sound = self.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.*);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,14 +87,14 @@ pub fn render(self: *Self, x: i32, y: i32, width: i32, height: i32) void {
|
|||||||
const pixel_width = @divFloor(width, self.chip.display_width);
|
const pixel_width = @divFloor(width, self.chip.display_width);
|
||||||
const pixel_height = @divFloor(height, self.chip.display_height);
|
const pixel_height = @divFloor(height, self.chip.display_height);
|
||||||
|
|
||||||
rl.drawRectangle(0, 0, width, height, self.off_color);
|
rl.DrawRectangle(x, y, width, height, self.off_color);
|
||||||
|
|
||||||
for (0..self.chip.display_height) |oy| {
|
for (0..self.chip.display_height) |oy| {
|
||||||
for (0..self.chip.display_width) |ox| {
|
for (0..self.chip.display_width) |ox| {
|
||||||
if (self.chip.display_get(@intCast(ox), @intCast(oy))) {
|
if (self.chip.display_get(@intCast(ox), @intCast(oy))) {
|
||||||
const ix = x + @as(i32, @intCast(ox)) * pixel_width;
|
const ix = x + @as(i32, @intCast(ox)) * pixel_width;
|
||||||
const iy = y + @as(i32, @intCast(oy)) * pixel_height;
|
const iy = y + @as(i32, @intCast(oy)) * pixel_height;
|
||||||
rl.drawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);
|
rl.DrawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
82
src/shaders/lighting.fs
Normal file
82
src/shaders/lighting.fs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// Input vertex attributes (from vertex shader)
|
||||||
|
in vec3 fragPosition;
|
||||||
|
in vec2 fragTexCoord;
|
||||||
|
//in vec4 fragColor;
|
||||||
|
in vec3 fragNormal;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
uniform vec4 colDiffuse;
|
||||||
|
|
||||||
|
// Output fragment color
|
||||||
|
out vec4 finalColor;
|
||||||
|
|
||||||
|
// NOTE: Add here your custom variables
|
||||||
|
|
||||||
|
#define MAX_LIGHTS 1
|
||||||
|
#define LIGHT_DIRECTIONAL 0
|
||||||
|
#define LIGHT_POINT 1
|
||||||
|
|
||||||
|
struct MaterialProperty {
|
||||||
|
vec3 color;
|
||||||
|
int useSampler;
|
||||||
|
sampler2D sampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Light {
|
||||||
|
int enabled;
|
||||||
|
int type;
|
||||||
|
vec3 position;
|
||||||
|
vec3 target;
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Input lighting values
|
||||||
|
uniform Light lights[MAX_LIGHTS];
|
||||||
|
uniform vec4 ambient;
|
||||||
|
uniform vec3 viewPos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Texel color fetching from texture sampler
|
||||||
|
vec4 texelColor = texture(texture0, fragTexCoord);
|
||||||
|
vec3 lightDot = vec3(0.0);
|
||||||
|
vec3 normal = normalize(fragNormal);
|
||||||
|
vec3 viewD = normalize(viewPos - fragPosition);
|
||||||
|
vec3 specular = vec3(0.0);
|
||||||
|
|
||||||
|
// NOTE: Implement here your fragment shader code
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||||
|
{
|
||||||
|
if (lights[i].enabled == 1)
|
||||||
|
{
|
||||||
|
vec3 light = vec3(0.0);
|
||||||
|
|
||||||
|
if (lights[i].type == LIGHT_DIRECTIONAL)
|
||||||
|
{
|
||||||
|
light = -normalize(lights[i].target - lights[i].position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lights[i].type == LIGHT_POINT)
|
||||||
|
{
|
||||||
|
light = normalize(lights[i].position - fragPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
float NdotL = max(dot(normal, light), 0.0);
|
||||||
|
lightDot += lights[i].color.rgb*NdotL;
|
||||||
|
|
||||||
|
float specCo = 0.0;
|
||||||
|
if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16.0); // 16 refers to shine
|
||||||
|
specular += specCo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0)));
|
||||||
|
finalColor += texelColor*(ambient/10.0)*colDiffuse;
|
||||||
|
|
||||||
|
// Gamma correction
|
||||||
|
finalColor = pow(finalColor, vec4(1.0/1.9));
|
||||||
|
}
|
32
src/shaders/lighting.vs
Normal file
32
src/shaders/lighting.vs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// Input vertex attributes
|
||||||
|
in vec3 vertexPosition;
|
||||||
|
in vec2 vertexTexCoord;
|
||||||
|
in vec3 vertexNormal;
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform mat4 mvp;
|
||||||
|
uniform mat4 matModel;
|
||||||
|
uniform mat4 matNormal;
|
||||||
|
|
||||||
|
// Output vertex attributes (to fragment shader)
|
||||||
|
out vec3 fragPosition;
|
||||||
|
out vec2 fragTexCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
out vec3 fragNormal;
|
||||||
|
|
||||||
|
// NOTE: Add here your custom variables
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Send vertex attributes to fragment shader
|
||||||
|
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
|
||||||
|
fragTexCoord = vertexTexCoord;
|
||||||
|
fragColor = vertexColor;
|
||||||
|
fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0)));
|
||||||
|
|
||||||
|
// Calculate final vertex position
|
||||||
|
gl_Position = mvp*vec4(vertexPosition, 1.0);
|
||||||
|
}
|
230
src/ui.zig
Normal file
230
src/ui.zig
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
const assert = @import("std").debug.assert;
|
||||||
|
|
||||||
|
pub const UI = struct {
|
||||||
|
const TransformFrame = struct {
|
||||||
|
ox: f32 = 0,
|
||||||
|
oy: f32 = 0,
|
||||||
|
sx: f32 = 1,
|
||||||
|
sy: f32 = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
frames: [16]TransformFrame,
|
||||||
|
top_frame: u32,
|
||||||
|
|
||||||
|
mouse: rl.Vector2,
|
||||||
|
mouse_delta: rl.Vector2,
|
||||||
|
|
||||||
|
pub fn init() UI {
|
||||||
|
return UI{
|
||||||
|
.frames = [1]TransformFrame{ TransformFrame{} } ** 16,
|
||||||
|
.top_frame = 0,
|
||||||
|
.mouse = rl.Vector2.zero(),
|
||||||
|
.mouse_delta = rl.Vector2.zero(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushTransform(self: *UI) void {
|
||||||
|
assert(self.top_frame < self.frames.len-1);
|
||||||
|
|
||||||
|
rl.rlPushMatrix();
|
||||||
|
self.top_frame += 1;
|
||||||
|
self.frames[self.top_frame] = self.frames[self.top_frame - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushTransformT(self: *UI, transform: TransformFrame) void {
|
||||||
|
self.pushTransform();
|
||||||
|
self.translate(transform.ox, transform.oy);
|
||||||
|
self.scale(transform.sx, transform.sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(self: *UI, x: f32, y: f32) void {
|
||||||
|
const top_frame = &self.frames[self.top_frame];
|
||||||
|
top_frame.ox += x * top_frame.sx;
|
||||||
|
top_frame.oy += y * top_frame.sy;
|
||||||
|
rl.rlTranslatef(x, y, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale(self: *UI, x: f32, y: f32) void {
|
||||||
|
const top_frame = &self.frames[self.top_frame];
|
||||||
|
top_frame.sx *= x;
|
||||||
|
top_frame.sy *= y;
|
||||||
|
rl.rlScalef(x, y, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popTransform(self: *UI) void {
|
||||||
|
rl.rlPopMatrix();
|
||||||
|
self.top_frame -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self: *UI) void {
|
||||||
|
assert(self.top_frame == 0); // Check if 'pushTransform()' and 'popTransform()' are paired
|
||||||
|
|
||||||
|
self.frames[0] = .{};
|
||||||
|
self.mouse = rl.GetMousePosition();
|
||||||
|
self.mouse_delta = rl.GetMouseDelta();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Screen space -> UI space
|
||||||
|
pub fn apply_transform(self: *UI, vec2: rl.Vector2) rl.Vector2 {
|
||||||
|
const top_frame = &self.frames[self.top_frame];
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = (vec2.x - top_frame.ox) * top_frame.sx,
|
||||||
|
.y = (vec2.y - top_frame.oy) * top_frame.sy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_scale(self: *UI, vec2: rl.Vector2) rl.Vector2 {
|
||||||
|
const top_frame = &self.frames[self.top_frame];
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = vec2.x * top_frame.sx,
|
||||||
|
.y = vec2.y * top_frame.sy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mouse(self: *UI) rl.Vector2 {
|
||||||
|
return self.apply_transform(self.mouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mouse_delta(self: *UI) rl.Vector2 {
|
||||||
|
return self.apply_scale(self.mouse_delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mouse_inside(self: *UI, x: f32, y: f32, width: f32, height: f32) bool {
|
||||||
|
const mouse = self.get_mouse();
|
||||||
|
return (x <= mouse.x and mouse.x < x+width) and (y <= mouse.y and mouse.y < y+height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mouse_inside_rect(self: *UI, rect: rl.Rectangle) bool {
|
||||||
|
return self.is_mouse_inside(rect.x, rect.y, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mouse_down() bool {
|
||||||
|
return rl.IsMouseButtonDown(rl.MouseButton.MOUSE_BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mouse_up() bool {
|
||||||
|
return rl.IsMouseButtonUp(rl.MouseButton.MOUSE_BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn was_secondary_mouse_pressed() bool {
|
||||||
|
return rl.IsMouseButtonPressed(rl.MouseButton.MOUSE_BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_holding_mouse(self: *UI, rect: rl.Rectangle, state: *bool) bool {
|
||||||
|
if (!state.* and UI.is_mouse_down() and self.is_mouse_inside_rect(rect)) {
|
||||||
|
state.* = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (UI.is_mouse_up()) {
|
||||||
|
state.* = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UIBox = struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
|
||||||
|
vert_margin: f32,
|
||||||
|
horz_margin: f32,
|
||||||
|
|
||||||
|
pub fn init(x: f32, y: f32, width: f32, height: f32) UIBox {
|
||||||
|
return UIBox {
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.vert_margin = 0,
|
||||||
|
.horz_margin = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_rect(rectangle: rl.Rectangle) UIBox {
|
||||||
|
return UIBox.init(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_x(self: *UIBox) f32 {
|
||||||
|
return self.x + self.horz_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_y(self: *UIBox) f32 {
|
||||||
|
return self.y + self.vert_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_width(self: *UIBox) f32 {
|
||||||
|
return self.width - 2*self.horz_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_height(self: *UIBox) f32 {
|
||||||
|
return self.height - 2*self.vert_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_rect(self: *UIBox) rl.Rectangle {
|
||||||
|
return rl.Rectangle{
|
||||||
|
.x = self.body_x(),
|
||||||
|
.y = self.body_y(),
|
||||||
|
.width = self.body_width(),
|
||||||
|
.height = self.body_height()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rect(self: *UIBox) rl.Rectangle {
|
||||||
|
return rl.Rectangle{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y,
|
||||||
|
.width = self.width,
|
||||||
|
.height = self.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const HorizontalLayout = struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
|
||||||
|
used_width: f32,
|
||||||
|
|
||||||
|
pub fn init(x: f32, y: f32, width: f32, height: f32) HorizontalLayout {
|
||||||
|
return HorizontalLayout{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.used_width = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_x(self: *HorizontalLayout) f32 {
|
||||||
|
return self.x + self.used_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_y(self: *HorizontalLayout) f32 {
|
||||||
|
return self.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_rect(self: *HorizontalLayout, width: f32) rl.Rectangle {
|
||||||
|
const rect = rl.Rectangle{
|
||||||
|
.x = self.next_x(),
|
||||||
|
.y = self.next_y(),
|
||||||
|
.width = width,
|
||||||
|
.height = self.height
|
||||||
|
};
|
||||||
|
self.push(width);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: *HorizontalLayout, width: f32) void {
|
||||||
|
self.used_width += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn used_size(self: *HorizontalLayout) rl.Vector2 {
|
||||||
|
return rl.Vector2{ .x = self.used_width, .y = self.height };
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user