1
0

add power switch

This commit is contained in:
Rokas Puzonas 2023-12-30 17:56:51 +02:00
parent 8c7e341832
commit 094e4a7f09
6 changed files with 241 additions and 58 deletions

Binary file not shown.

View File

@ -70,6 +70,20 @@ pub fn init(allocator: Allocator) !Self {
return self;
}
pub fn reset(self: *Self) void {
@memset(self.memory, 0);
@memset(&self.input, false);
@memset(&self.stack, 0);
@memset(&self.V, 0);
@memset(self.display, false);
self.I = 0;
self.PC = 0x200;
self.SP = 0;
self.DT = 0;
self.ST = 0;
}
pub fn deinit(self: *Self) void {
self.allocator.free(self.display);
self.allocator.free(self.memory);

View File

@ -18,9 +18,9 @@ power_switch: *rl.Model,
bbox: rl.BoundingBox,
position: rl.Vector3,
screen_texture: rl.RenderTexture2D,
rl_chip: *const RaylibChip,
button_state: *[16]bool,
powered: *bool,
fn getExtensionByMimeType(mime_type: []const u8) ?[:0]const u8 {
if (std.mem.eql(u8, mime_type, "image/png")) {
@ -243,7 +243,7 @@ fn loadGltfMesh(materials: []rl.Material, gltf: Gltf, node: Gltf.Node) !rl.Model
return model;
}
pub fn init(allocator: Allocator, rl_chip: *const RaylibChip) !Self {
pub fn init(allocator: Allocator, powered: *bool, button_state: *[16]bool, screen_texture: rl.RenderTexture2D) !Self {
var gltf = Gltf.init(allocator);
defer gltf.deinit();
try gltf.parse(@embedFile("assets/models/emulator.glb"));
@ -278,6 +278,7 @@ pub fn init(allocator: Allocator, rl_chip: *const RaylibChip) !Self {
var static_models = std.ArrayList(*rl.Model).init(allocator);
errdefer static_models.deinit();
var buttons: [16]*rl.Model = undefined;
var power_switch: *rl.Model = undefined;
@ -286,7 +287,7 @@ pub fn init(allocator: Allocator, rl_chip: *const RaylibChip) !Self {
if (node.mesh == null) continue;
models.appendAssumeCapacity(try loadGltfMesh(materials, gltf, node));
const model = &models.items[models.items.len-1];
var model: *rl.Model = &models.items[models.items.len-1];
if (std.mem.eql(u8, node.name, "Power switch")) {
power_switch = model;
@ -297,20 +298,11 @@ pub fn init(allocator: Allocator, rl_chip: *const RaylibChip) !Self {
} else {
try static_models.append(model);
}
}
const screen_texture = rl.LoadRenderTexture(rl_chip.chip.display_width, rl_chip.chip.display_height);
errdefer rl.UnloadRenderTexture(screen_texture);
{ // Link screen render target to shader
var screen_mtl_idx: ?usize = null;
for (1.., gltf.data.materials.items) |idx, mtl| {
if (std.mem.eql(u8, mtl.name, "Screen")) {
screen_mtl_idx = idx;
break;
}
if (std.mem.eql(u8, node.name, "Screen")) {
const screen_material = &model.materials.?[0];
rl.SetMaterialTexture(@ptrCast(screen_material), rl.MATERIAL_MAP_DIFFUSE, screen_texture.texture);
}
rl.SetMaterialTexture(@ptrCast(&materials[screen_mtl_idx.?]), rl.MATERIAL_MAP_DIFFUSE, screen_texture.texture);
}
return Self{
@ -322,11 +314,11 @@ pub fn init(allocator: Allocator, rl_chip: *const RaylibChip) !Self {
.models = models,
.buttons = buttons,
.power_switch = power_switch,
.powered = powered,
.button_state = button_state,
.bbox = rl.GetModelBoundingBox(static_models.items[0].*),
.screen_texture = screen_texture,
.position = rl.Vector3{ .x = 0, .y = 0, .z = 0 },
.rl_chip = rl_chip,
};
}
@ -350,35 +342,140 @@ pub fn deinit(self: *Self) void {
self.allocator.free(self.materials);
self.static_models.deinit();
self.models.deinit();
rl.UnloadRenderTexture(self.screen_texture);
}
pub fn updateDisplay(self: *Self) void {
rl.BeginTextureMode(self.screen_texture);
self.rl_chip.render();
rl.EndTextureMode();
fn getRayCollisionModel(ray: rl.Ray, model: rl.Model, position: rl.Vector3) rl.RayCollision {
var closest_hit_info = std.mem.zeroes(rl.RayCollision);
const transform = rl.MatrixMultiply(model.transform, rl.MatrixTranslate(position.x, position.y, position.z));
for (0..@intCast(model.meshCount)) |i| {
if (model.meshes == null) break;
const mesh = model.meshes.?[i];
const hit_info = rl.GetRayCollisionMesh(ray, mesh, transform);
if (!hit_info.hit) continue;
if ((!closest_hit_info.hit) or (closest_hit_info.distance > hit_info.distance)) {
closest_hit_info = hit_info;
}
}
return closest_hit_info;
}
pub fn isMouseOverPowerSwitch(self: *const Self) bool {
_ = self;
const ModelCollision = struct { collision: rl.RayCollision, model: *rl.Model };
fn getRayCollisionModels(ray: rl.Ray, models: []rl.Model, position: rl.Vector3) ?ModelCollision {
var closest_hit_info: ?rl.RayCollision = null;
var closest_model: ?*rl.Model = null;
for (models) |*model| {
const hit_info = getRayCollisionModel(ray, model.*, position);
if (!hit_info.hit) continue;
if ((closest_hit_info == null) or (closest_hit_info.?.distance > hit_info.distance)) {
closest_hit_info = hit_info;
closest_model = model;
}
}
if (closest_hit_info == null) {
return null;
}
return ModelCollision{
.collision = closest_hit_info.?,
.model = closest_model.?
};
}
pub fn isOverPowerSwitch(self: *const Self, ray: rl.Ray) bool {
const collision = getRayCollisionModels(ray, self.models.items, self.position);
if (collision) |c| {
return c.model == self.power_switch;
}
return false;
}
pub fn setPowerSwitch(self: *const Self, enabled: bool) void {
_ = enabled;
_ = self;
return false;
fn matrixGetTranslation(mat: rl.Matrix) rl.Vector3 {
return rl.Vector3{
.x = mat.m12,
.y = mat.m13,
.z = mat.m14,
};
}
pub fn getPowerSwitch(self: *const Self) bool {
_ = self;
return false;
fn matrixGetScale(mat: rl.Matrix) rl.Vector3 {
return rl.Vector3{
.x = rl.Vector3Length(rl.Vector3{ .x = mat.m0, .y = mat.m1, .z = mat.m2 }),
.y = rl.Vector3Length(rl.Vector3{ .x = mat.m4, .y = mat.m5, .z = mat.m6 }),
.z = rl.Vector3Length(rl.Vector3{ .x = mat.m8, .y = mat.m9, .z = mat.m10 }),
};
}
// https://stackoverflow.com/a/64336115
fn matrixGetRotation(mat: rl.Matrix) rl.Vector3 {
const scale = matrixGetScale(mat);
const R11 = mat.m0 / scale.x;
const R12 = mat.m1 / scale.x;
const R13 = mat.m2 / scale.x;
const R21 = mat.m4 / scale.y;
// const R22 = mat.m5 / scale.y;
// const R23 = mat.m6 / scale.y;
const R31 = mat.m8 / scale.z;
const R32 = mat.m9 / scale.z;
const R33 = mat.m10 / scale.z;
const asin = std.math.asin;
const cos = std.math.cos;
const atan2 = std.math.atan2;
const pi = rl.PI;
var roll: f32 = undefined;
var pitch: f32 = undefined;
var yaw: f32 = undefined;
if (R31 != 1 and R31 != -1) {
const pitch_1 = -1 * asin(R31);
// const pitch_2 = pi - pitch_1;
const roll_1 = atan2(f32, R32 / cos(pitch_1), R33 / cos(pitch_1));
// const roll_2 = atan2( R32 / cos(pitch_2) , R33 / cos(pitch_2));
const yaw_1 = atan2(f32, R21 / cos(pitch_1), R11 / cos(pitch_1));
// const yaw_2 = atan2( R21 / cos(pitch_2) , R11 / cos(pitch_2));
// IMPORTANT NOTE here, there is more than one solution but we choose the first for this case for simplicity !
// You can insert your own domain logic here on how to handle both solutions appropriately (see the reference publication link for more info).
pitch = pitch_1;
roll = roll_1;
yaw = yaw_1;
} else {
yaw = 0; // anything (we default this to zero)
if (R31 == -1) {
pitch = pi / 2;
roll = yaw + atan2(f32, R12, R13);
} else {
pitch = -pi / 2;
roll = -1*yaw + atan2(f32, -1*R12, -1*R13);
}
}
// convert from radians to degrees
roll = roll * rl.RAD2DEG;
pitch = pitch * rl.RAD2DEG;
yaw = yaw * rl.RAD2DEG;
return rl.Vector3{
.x = roll,
.y = pitch,
.z = yaw,
};
}
pub fn draw(self: *Self) void {
for (self.buttons, 0..) |button, i| {
var position = self.position;
if (self.rl_chip.chip.is_input_pressed(@intCast(i))) {
if (self.button_state[i]) {
position.z += 0.035;
}
rl.DrawModel(button.*, position, 1.0, rl.WHITE);
@ -388,5 +485,18 @@ pub fn draw(self: *Self) void {
rl.DrawModel(model.*, self.position, 1.0, rl.WHITE);
}
rl.DrawModel(self.power_switch.*, self.position, 1.0, rl.WHITE);
{ // Power switch
const on_angle: f32 = 45;
const off_angle: f32 = -45;
const target_angle = if (self.powered.*) on_angle else off_angle;
var transform = self.power_switch.transform;
const rotation = matrixGetRotation(transform);
const dt = rl.GetFrameTime();
const delta_angle = rotation.z - target_angle;
transform = rl.MatrixMultiply(rl.MatrixRotateZ((delta_angle * dt * 0.2)), transform);
self.power_switch.transform = transform;
rl.DrawModel(self.power_switch.*, self.position, 1.0, rl.WHITE);
}
}

View File

@ -4,6 +4,7 @@ const std = @import("std");
const Gltf = @import("zgltf");
const GlobalContext = @import("./global-context.zig");
const EmulatorModel = @import("./emulator-model.zig");
const ROM = @import("./roms.zig").ROM;
const ChipContext = @import("chip.zig");
const RaylibChip = @import("raylib-chip.zig");
@ -24,9 +25,12 @@ previous_click_time: f64 = 0.0,
shader: rl.Shader,
lights: [2]Light,
powered: *bool,
chip: *ChipContext,
raylib_chip: *RaylibChip,
chip_sound: rl.Sound,
screen_texture: rl.RenderTexture2D,
rom: ?ROM = null,
pub fn genSinWave(wave: *rl.Wave, frequency: f32) void {
assert(wave.sampleSize == 16); // Only 16 bits are supported
@ -195,7 +199,14 @@ pub fn init(allocator: Allocator, ctx: *GlobalContext) !Self {
var raylib_chip = try allocator.create(RaylibChip);
raylib_chip.* = RaylibChip.init(chip, chip_sound);
var emulator = try EmulatorModel.init(allocator, raylib_chip);
const screen_texture = rl.LoadRenderTexture(raylib_chip.chip.display_width, raylib_chip.chip.display_height);
errdefer rl.UnloadRenderTexture(screen_texture);
const powered = try allocator.create(bool);
errdefer allocator.destroy(powered);
var emulator = try EmulatorModel.init(allocator, powered, &chip.input, screen_texture);
errdefer emulator.deinit();
emulator.setShader(shader);
return Self {
@ -208,6 +219,8 @@ pub fn init(allocator: Allocator, ctx: *GlobalContext) !Self {
.chip = chip,
.raylib_chip = raylib_chip,
.chip_sound = chip_sound,
.screen_texture = screen_texture,
.powered = powered
};
}
@ -215,11 +228,40 @@ pub fn deinit(self: *Self) void {
self.emulator.deinit();
rl.UnloadShader(self.shader);
rl.UnloadSound(self.chip_sound);
rl.UnloadRenderTexture(self.screen_texture);
self.chip.deinit();
self.allocator.destroy(self.powered);
self.allocator.destroy(self.raylib_chip);
self.allocator.destroy(self.chip);
}
pub fn set_rom(self: *Self, rom: ROM) void {
self.rom = rom;
self.chip.reset();
self.chip.set_memory(0x200, rom.data);
}
pub fn turn_on(self: *Self) void {
self.powered.* = true;
}
pub fn turn_off(self: *Self) void {
self.powered.* = false;
self.chip.reset();
if (self.rom) |rom| {
self.chip.set_memory(0x200, rom.data);
}
}
pub fn toggle_power(self: *Self) void {
if (self.powered.*) {
self.turn_off();
} else {
self.turn_on();
}
}
fn updateCamera(self: *Self, dt: f32) void {
const mouse_delta = rl.GetMouseDelta();
const camera = &self.ctx.camera;
@ -304,7 +346,23 @@ pub fn update(self: *Self, dt: f32) void {
light.update_values(self.shader);
}
self.emulator.updateDisplay();
const ray = rl.GetMouseRay(rl.GetMousePosition(), self.ctx.camera);
if (self.emulator.isOverPowerSwitch(ray)) {
if (rl.IsMouseButtonPressed(rl.MouseButton.MOUSE_BUTTON_LEFT)) {
self.toggle_power();
}
}
if (self.powered.*) {
self.raylib_chip.update_input();
self.raylib_chip.update(dt);
}
rl.BeginTextureMode(self.screen_texture);
{
self.raylib_chip.render();
}
rl.EndTextureMode();
// {
// var matProj = rl.MatrixIdentity();

View File

@ -7,17 +7,12 @@ const ChipContext = @import("chip.zig");
const RaylibChip = @import("raylib-chip.zig");
const GlobalContext = @import("./global-context.zig");
const MainScene = @import("./main-scene.zig");
const options = @import("options");
const listROMs = @import("./roms.zig").listROMs;
fn megabytes(amount: usize) usize {
return amount * 1024 * 1024;
}
const ROM = struct {
name: []const u8,
data: []const u8,
};
pub fn main() anyerror!void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
@ -38,19 +33,8 @@ pub fn main() anyerror!void {
var main_scene = try MainScene.init(allocator, &ctx);
defer main_scene.deinit();
comptime var roms = [1]ROM{ undefined } ** options.roms.len;
comptime {
var i = 0;
for (options.roms) |file| {
roms[i] = ROM{
.name = file,
.data = @embedFile(file)
};
i += 1;
}
}
main_scene.chip.set_memory(0x200, roms[2].data);
const roms = comptime listROMs();
main_scene.set_rom(roms[3]);
const font_size = 24;
const font_ttf_default_numchars = 95; // TTF font generation default charset: 95 glyphs (ASCII 32..126)
@ -59,9 +43,6 @@ pub fn main() anyerror!void {
while (!rl.WindowShouldClose()) {
var dt = rl.GetFrameTime();
main_scene.raylib_chip.update_input();
main_scene.raylib_chip.update(dt);
main_scene.update(dt);
rl.BeginDrawing();

20
src/roms.zig Normal file
View File

@ -0,0 +1,20 @@
const options = @import("options");
const rom_count = options.roms.len;
pub const ROM = struct {
name: []const u8,
data: []const u8,
};
pub fn listROMs() [options.roms.len]ROM {
var roms: [options.roms.len]ROM = undefined;
for (0.., options.roms) |i, file| {
roms[i] = ROM{
.name = file,
.data = @embedFile(file)
};
}
return roms;
}