to hell with 3d models, simplify
This commit is contained in:
parent
c4c28f3b7a
commit
3fb9ea38eb
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "libs/zgltf"]
|
||||
path = libs/zgltf
|
||||
url = https://github.com/kooparse/zgltf.git
|
||||
[submodule "libs/raylib/raylib"]
|
||||
path = libs/raylib/raylib
|
||||
url = git@github.com:raysan5/raylib.git
|
||||
|
18
build.zig
18
build.zig
@ -15,10 +15,6 @@ pub fn build(b: *std.Build) !void {
|
||||
.target = target
|
||||
});
|
||||
|
||||
exe.addModule("zgltf", b.createModule(.{
|
||||
.source_file = .{ .path = "libs/zgltf/src/main.zig" },
|
||||
}));
|
||||
|
||||
// Provide filenames of all files in 'src/ROMs' to program as options
|
||||
{
|
||||
var files = std.ArrayList([]const u8).init(b.allocator);
|
||||
@ -39,20 +35,6 @@ pub fn build(b: *std.Build) !void {
|
||||
|
||||
raylib.addTo(b, exe, target, optimize, .{});
|
||||
|
||||
{
|
||||
var build_models_step = b.step("models", "Export .blend files");
|
||||
var build_models = b.addSystemCommand(&[_][]const u8{ "blender" });
|
||||
build_models.addFileArg(.{ .path = "src/assets/models/emulator.blend" });
|
||||
build_models.addArg("--background");
|
||||
build_models.addArg("--python");
|
||||
build_models.addFileArg(.{ .path = "src/assets/models/export.py" });
|
||||
build_models.addArg("--");
|
||||
build_models.addArg("src/assets/models/emulator.glb");
|
||||
|
||||
build_models_step.dependOn(&build_models.step);
|
||||
exe.step.dependOn(build_models_step);
|
||||
}
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
||||
const run_step = b.step("run", "Run chip8-zig");
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit f9ed05023db75484333b6c7125a8c02a99cf3a14
|
Binary file not shown.
Before Width: | Height: | Size: 504 KiB |
Binary file not shown.
@ -1,13 +0,0 @@
|
||||
import bpy
|
||||
import sys
|
||||
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
assert len(argv) >= 1
|
||||
|
||||
output_path = argv[0]
|
||||
assert output_path.endswith(".gltf") or output_path.endswith(".glb")
|
||||
bpy.ops.export_scene.gltf(
|
||||
filepath=output_path,
|
||||
use_visible=True,
|
||||
)
|
Binary file not shown.
Before Width: | Height: | Size: 311 B |
@ -1,523 +0,0 @@
|
||||
const Self = @This();
|
||||
const Gltf = @import("zgltf");
|
||||
const rl = @import("raylib");
|
||||
const std = @import("std");
|
||||
const RaylibChip = @import("raylib-chip.zig");
|
||||
const Light = @import("./light.zig");
|
||||
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
allocator: Allocator,
|
||||
|
||||
materials: []rl.Material,
|
||||
|
||||
models: std.ArrayList(rl.Model),
|
||||
static_models: std.ArrayList(*rl.Model),
|
||||
buttons: [16]*rl.Model,
|
||||
power_switch: *rl.Model,
|
||||
power_light_position: rl.Vector3,
|
||||
|
||||
bbox: rl.BoundingBox,
|
||||
position: rl.Vector3,
|
||||
|
||||
button_state: *[16]bool,
|
||||
powered: *bool,
|
||||
|
||||
fn getExtensionByMimeType(mime_type: []const u8) ?[:0]const u8 {
|
||||
if (std.mem.eql(u8, mime_type, "image/png")) {
|
||||
return ".png\x00";
|
||||
} else if (std.mem.eql(u8, mime_type, "image/jpg")) {
|
||||
return ".jpg\x00";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn loadGltfImage(image: Gltf.Image) !rl.Image {
|
||||
assert(image.uri == null);
|
||||
assert(image.data != null);
|
||||
assert(image.mime_type != null);
|
||||
|
||||
const image_data = image.data.?;
|
||||
const mime_type = image.mime_type.?;
|
||||
|
||||
const file_type = getExtensionByMimeType(mime_type);
|
||||
assert(file_type != null);
|
||||
|
||||
const rl_image = rl.LoadImageFromMemory(file_type.?, @ptrCast(image_data.ptr), @intCast(image_data.len));
|
||||
if (@as(?*anyopaque, @ptrCast(rl_image.data)) == null) {
|
||||
return error.Failed;
|
||||
}
|
||||
return rl_image;
|
||||
}
|
||||
|
||||
fn loadGltfMaterial(data: Gltf.Data, material: Gltf.Material) !rl.Material {
|
||||
var rl_material = rl.LoadMaterialDefault();
|
||||
errdefer rl.UnloadMaterial(rl_material);
|
||||
|
||||
var albedo_map: *rl.MaterialMap = &rl_material.maps.?[@intFromEnum(rl.MaterialMapIndex.MATERIAL_MAP_ALBEDO)];
|
||||
if (material.metallic_roughness.base_color_texture) |base_color_texture| {
|
||||
const texture = data.textures.items[base_color_texture.index];
|
||||
const image = data.images.items[texture.source.?];
|
||||
|
||||
const rl_image = try loadGltfImage(image);
|
||||
defer rl.UnloadImage(rl_image);
|
||||
albedo_map.texture = rl.LoadTextureFromImage(rl_image);
|
||||
}
|
||||
|
||||
albedo_map.color.r = @intFromFloat(material.metallic_roughness.base_color_factor[0]*255);
|
||||
albedo_map.color.g = @intFromFloat(material.metallic_roughness.base_color_factor[1]*255);
|
||||
albedo_map.color.b = @intFromFloat(material.metallic_roughness.base_color_factor[2]*255);
|
||||
albedo_map.color.a = @intFromFloat(material.metallic_roughness.base_color_factor[3]*255);
|
||||
|
||||
return rl_material;
|
||||
}
|
||||
|
||||
fn loadGltfPrimitive(gltf: Gltf, primitive: Gltf.Primitive) !rl.Mesh {
|
||||
var allocator = std.heap.c_allocator;
|
||||
|
||||
assert(primitive.mode == .triangles);
|
||||
var rl_mesh = std.mem.zeroes(rl.Mesh);
|
||||
errdefer rl.UnloadMesh(rl_mesh);
|
||||
|
||||
var bin = gltf.glb_binary.?;
|
||||
var f32_buffer = std.ArrayList(f32).init(allocator);
|
||||
defer f32_buffer.deinit();
|
||||
|
||||
for (primitive.attributes.items) |attribute| {
|
||||
switch (attribute) {
|
||||
.position => |accessor_index| {
|
||||
const accessor = gltf.data.accessors.items[accessor_index];
|
||||
assert(accessor.component_type == .float);
|
||||
assert(accessor.type == .vec3);
|
||||
f32_buffer.clearAndFree();
|
||||
gltf.getDataFromBufferView(f32, &f32_buffer, accessor, bin);
|
||||
|
||||
var vertices = try allocator.dupe(f32, f32_buffer.items);
|
||||
rl_mesh.vertexCount = @intCast(accessor.count);
|
||||
rl_mesh.vertices = @ptrCast(vertices);
|
||||
},
|
||||
.normal => |accessor_index| {
|
||||
const accessor = gltf.data.accessors.items[accessor_index];
|
||||
assert(accessor.component_type == .float);
|
||||
assert(accessor.type == .vec3);
|
||||
f32_buffer.clearRetainingCapacity();
|
||||
gltf.getDataFromBufferView(f32, &f32_buffer, accessor, bin);
|
||||
|
||||
var normals = try allocator.dupe(f32, f32_buffer.items);
|
||||
rl_mesh.normals = @ptrCast(normals);
|
||||
},
|
||||
.tangent => |accessor_index| {
|
||||
const accessor = gltf.data.accessors.items[accessor_index];
|
||||
assert(accessor.component_type == .float);
|
||||
assert(accessor.type == .vec4);
|
||||
f32_buffer.clearRetainingCapacity();
|
||||
gltf.getDataFromBufferView(f32, &f32_buffer, accessor, bin);
|
||||
|
||||
var tangents = try allocator.dupe(f32, f32_buffer.items);
|
||||
rl_mesh.tangents = @ptrCast(tangents);
|
||||
},
|
||||
.texcoord => |accessor_index| {
|
||||
const accessor = gltf.data.accessors.items[accessor_index];
|
||||
assert(accessor.component_type == .float);
|
||||
assert(accessor.type == .vec2);
|
||||
f32_buffer.clearRetainingCapacity();
|
||||
gltf.getDataFromBufferView(f32, &f32_buffer, accessor, bin);
|
||||
|
||||
var texcoords = try allocator.dupe(f32, f32_buffer.items);
|
||||
rl_mesh.texcoords = @ptrCast(texcoords);
|
||||
},
|
||||
else => {}
|
||||
}
|
||||
}
|
||||
|
||||
if (primitive.indices) |accessor_index| {
|
||||
const accessor = gltf.data.accessors.items[accessor_index];
|
||||
rl_mesh.triangleCount = @divExact(accessor.count, 3);
|
||||
const accessor_count: usize = @intCast(accessor.count);
|
||||
|
||||
var indices = try allocator.alloc(u16, accessor_count);
|
||||
rl_mesh.indices = @ptrCast(indices);
|
||||
|
||||
if (accessor.component_type == Gltf.ComponentType.unsigned_short) {
|
||||
var u16_buffer = std.ArrayList(u16).init(allocator);
|
||||
defer u16_buffer.deinit();
|
||||
gltf.getDataFromBufferView(u16, &u16_buffer, accessor, bin);
|
||||
|
||||
@memcpy(indices, u16_buffer.items);
|
||||
} else if (accessor.component_type == Gltf.ComponentType.unsigned_integer) {
|
||||
var u32_buffer = std.ArrayList(u32).init(allocator);
|
||||
defer u32_buffer.deinit();
|
||||
gltf.getDataFromBufferView(u32, &u32_buffer, accessor, bin);
|
||||
|
||||
for (0..accessor_count) |i| {
|
||||
indices[i] = @truncate(u32_buffer.items[i]);
|
||||
}
|
||||
rl.TraceLog(@intFromEnum(rl.TraceLogLevel.LOG_WARNING), "MODEL: Indices data converted from u32 to u16, possible loss of data");
|
||||
} else {
|
||||
@panic("Unknown GLTF primitives indices component type. Use u16 or u32");
|
||||
}
|
||||
} else {
|
||||
rl_mesh.triangleCount = @divExact(rl_mesh.vertexCount, 3);
|
||||
}
|
||||
|
||||
rl.UploadMesh(@ptrCast(&rl_mesh), false);
|
||||
return rl_mesh;
|
||||
}
|
||||
|
||||
fn loadGltfMesh(materials: []rl.Material, gltf: Gltf, node: Gltf.Node) !rl.Model {
|
||||
const allocator = std.heap.c_allocator;
|
||||
const transform = Gltf.getGlobalTransform(&gltf.data, node);
|
||||
|
||||
var model = std.mem.zeroes(rl.Model);
|
||||
errdefer rl.UnloadModel(model);
|
||||
|
||||
model.transform = rl.Matrix{
|
||||
.m0 = transform[0][0],
|
||||
.m4 = transform[1][0],
|
||||
.m8 = transform[2][0],
|
||||
.m12 = transform[3][0],
|
||||
|
||||
.m1 = transform[0][1],
|
||||
.m5 = transform[1][1],
|
||||
.m9 = transform[2][1],
|
||||
.m13 = transform[3][1],
|
||||
|
||||
.m2 = transform[0][2],
|
||||
.m6 = transform[1][2],
|
||||
.m10 = transform[2][2],
|
||||
.m14 = transform[3][2],
|
||||
|
||||
.m3 = transform[0][3],
|
||||
.m7 = transform[1][3],
|
||||
.m11 = transform[2][3],
|
||||
.m15 = transform[3][3]
|
||||
};
|
||||
|
||||
if (node.mesh) |mesh_idx| {
|
||||
const mesh = gltf.data.meshes.items[mesh_idx];
|
||||
const primitives: []Gltf.Primitive = mesh.primitives.items;
|
||||
|
||||
var meshes = try allocator.alloc(rl.Mesh, primitives.len);
|
||||
model.meshCount = @intCast(primitives.len);
|
||||
model.meshes = @ptrCast(meshes.ptr);
|
||||
|
||||
var mesh_material = try allocator.alloc(i32, primitives.len);
|
||||
model.meshMaterial = @ptrCast(mesh_material.ptr);
|
||||
@memset(mesh_material, 0);
|
||||
|
||||
var used_material_ids = try allocator.alloc(usize, materials.len);
|
||||
var used_materials_count: usize = 0;
|
||||
defer allocator.free(used_material_ids);
|
||||
|
||||
for (0.., primitives) |j, primitive| {
|
||||
meshes[j] = try loadGltfPrimitive(gltf, primitive);
|
||||
|
||||
var mtl: usize = 0;
|
||||
if (primitive.material) |material| {
|
||||
mtl = material + 1;
|
||||
}
|
||||
|
||||
var mtl_index = std.mem.indexOfScalar(usize, used_material_ids[0..used_materials_count], mtl);
|
||||
if (mtl_index == null) {
|
||||
mtl_index = used_materials_count;
|
||||
used_material_ids[used_materials_count] = mtl;
|
||||
used_materials_count += 1;
|
||||
}
|
||||
|
||||
mesh_material[j] = @intCast(mtl_index.?);
|
||||
}
|
||||
|
||||
var used_materials = try allocator.alloc(rl.Material, used_materials_count+1);
|
||||
model.materials = @ptrCast(used_materials);
|
||||
model.materialCount = 0;
|
||||
for (0..used_materials_count) |i| {
|
||||
used_materials[i] = materials[used_material_ids[i]];
|
||||
|
||||
const max_material_maps = 12;
|
||||
const maps = try allocator.dupe(rl.MaterialMap, used_materials[i].maps.?[0..max_material_maps]);
|
||||
used_materials[i].maps = @ptrCast(maps);
|
||||
model.materialCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
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"));
|
||||
|
||||
const scene = gltf.data.scenes.items[gltf.data.scene.?];
|
||||
const scene_nodes: std.ArrayList(Gltf.Index) = scene.nodes.?;
|
||||
|
||||
const material_count: usize = @intCast(gltf.data.materials.items.len);
|
||||
var materials = try allocator.alloc(rl.Material, material_count+1);
|
||||
@memset(materials, std.mem.zeroes(rl.Material));
|
||||
errdefer allocator.free(materials);
|
||||
|
||||
materials[0] = rl.LoadMaterialDefault();
|
||||
errdefer {
|
||||
for (materials) |mtl| {
|
||||
rl.UnloadMaterial(mtl);
|
||||
}
|
||||
}
|
||||
|
||||
for (0..material_count, gltf.data.materials.items) |i, material| {
|
||||
materials[i+1] = try loadGltfMaterial(gltf.data, material);
|
||||
}
|
||||
|
||||
var models = try std.ArrayList(rl.Model).initCapacity(allocator, scene_nodes.items.len);
|
||||
errdefer models.deinit();
|
||||
errdefer {
|
||||
for (models.items) |model| {
|
||||
rl.UnloadModel(model);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
var power_light: ?*rl.Model = null;
|
||||
|
||||
for (scene_nodes.items) |node_index| {
|
||||
const node = gltf.data.nodes.items[node_index];
|
||||
if (node.mesh == null) continue;
|
||||
|
||||
models.appendAssumeCapacity(try loadGltfMesh(materials, gltf, node));
|
||||
var model: *rl.Model = &models.items[models.items.len-1];
|
||||
|
||||
const name = node.name;
|
||||
if (std.mem.eql(u8, name, "Power switch")) {
|
||||
power_switch = model;
|
||||
} else if (std.mem.startsWith(u8, name, "Buttons ")) {
|
||||
var space = std.mem.indexOfScalar(u8, name, ' ').?;
|
||||
const button_idx = try std.fmt.parseInt(usize, name[space+1..], 16);
|
||||
buttons[button_idx] = model;
|
||||
} else {
|
||||
try static_models.append(model);
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, name, "Screen")) {
|
||||
const screen_material = &model.materials.?[0];
|
||||
rl.SetMaterialTexture(@ptrCast(screen_material), rl.MATERIAL_MAP_DIFFUSE, screen_texture.texture);
|
||||
} else if (std.mem.eql(u8, name, "Power indicator")) {
|
||||
power_light = model;
|
||||
}
|
||||
}
|
||||
|
||||
var power_light_position = rl.Vector3.zero();
|
||||
if (power_light) |model| {
|
||||
power_light_position = matrixGetTranslation(model.transform);
|
||||
}
|
||||
|
||||
return Self{
|
||||
.allocator = allocator,
|
||||
|
||||
.materials = materials,
|
||||
|
||||
.static_models = static_models,
|
||||
.models = models,
|
||||
.buttons = buttons,
|
||||
.power_switch = power_switch,
|
||||
.powered = powered,
|
||||
.button_state = button_state,
|
||||
|
||||
.power_light_position = power_light_position,
|
||||
.bbox = rl.GetModelBoundingBox(static_models.items[0].*),
|
||||
.position = rl.Vector3{ .x = 0, .y = 0, .z = 0 },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setShader(self: *Self, shader: rl.Shader) void {
|
||||
for (self.models.items) |*model| {
|
||||
if (model.materials == null) continue;
|
||||
|
||||
for (0..@intCast(model.materialCount)) |i| {
|
||||
model.materials.?[i].shader = shader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
for (self.models.items) |model| {
|
||||
rl.UnloadModel(model);
|
||||
}
|
||||
for (self.materials) |mtl| {
|
||||
rl.UnloadMaterial(mtl);
|
||||
}
|
||||
self.allocator.free(self.materials);
|
||||
self.static_models.deinit();
|
||||
self.models.deinit();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fn matrixGetTranslation(mat: rl.Matrix) rl.Vector3 {
|
||||
return rl.Vector3{
|
||||
.x = mat.m12,
|
||||
.y = mat.m13,
|
||||
.z = mat.m14,
|
||||
};
|
||||
}
|
||||
|
||||
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.button_state[i]) {
|
||||
position.z += 0.035;
|
||||
}
|
||||
rl.DrawModel(button.*, position, 1.0, rl.WHITE);
|
||||
}
|
||||
|
||||
for (self.static_models.items) |model| {
|
||||
rl.DrawModel(model.*, 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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_power_light_position(self: *Self) rl.Vector3 {
|
||||
return self.power_light_position.add(self.position);
|
||||
}
|
||||
|
||||
pub fn get_power_light_color(self: *Self) rl.Color {
|
||||
_ = self;
|
||||
return rl.GREEN;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
const Self = @This();
|
||||
const rl = @import("raylib");
|
||||
const std = @import("std");
|
||||
|
||||
pub const LightType = enum(i32) {
|
||||
DIRECTIONAL = 0,
|
||||
POINT = 1,
|
||||
};
|
||||
|
||||
shader: rl.Shader,
|
||||
|
||||
enabledLoc: i32,
|
||||
typeLoc: i32,
|
||||
positionLoc: i32,
|
||||
targetLoc: i32,
|
||||
colorLoc: i32,
|
||||
intensityLoc: i32,
|
||||
|
||||
fn getLightShaderLocation(shader: rl.Shader, idx: usize, comptime name: []const u8) i32 {
|
||||
var buf: [128]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
||||
const prop_name = std.fmt.allocPrintZ(fba.allocator(), "lights[{d}]." ++ name, .{idx}) catch unreachable;
|
||||
return rl.GetShaderLocation(shader, prop_name);
|
||||
}
|
||||
|
||||
pub fn init(shader: rl.Shader, idx: usize) Self {
|
||||
return Self{
|
||||
.shader = shader,
|
||||
|
||||
.enabledLoc = getLightShaderLocation(shader, idx, "enabled"),
|
||||
.typeLoc = getLightShaderLocation(shader, idx, "type"),
|
||||
.positionLoc = getLightShaderLocation(shader, idx, "position"),
|
||||
.targetLoc = getLightShaderLocation(shader, idx, "target"),
|
||||
.colorLoc = getLightShaderLocation(shader, idx, "color"),
|
||||
.intensityLoc = getLightShaderLocation(shader, idx, "intensity"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_directional(self: *const Self, color: rl.Color, position: rl.Vector3, intensity: f32, target: rl.Vector3) void {
|
||||
self.set_type(.DIRECTIONAL);
|
||||
self.set_intensity(intensity);
|
||||
self.set_enabled(true);
|
||||
self.set_position(position);
|
||||
self.set_color(color);
|
||||
self.set_target(target);
|
||||
}
|
||||
|
||||
pub fn set_point(self: *const Self, color: rl.Color, position: rl.Vector3, intensity: f32) void {
|
||||
self.set_type(.POINT);
|
||||
self.set_intensity(intensity);
|
||||
self.set_enabled(true);
|
||||
self.set_color(color);
|
||||
self.set_position(position);
|
||||
}
|
||||
|
||||
pub fn set_enabled(self: *const Self, enabled: bool) void {
|
||||
const enabled_i32: i32 = @intFromBool(enabled);
|
||||
rl.SetShaderValue(self.shader, self.enabledLoc, &enabled_i32, .SHADER_UNIFORM_INT);
|
||||
}
|
||||
|
||||
pub fn set_type(self: *const Self, light_type: LightType) void {
|
||||
const light_type_i32: i32 = @intFromEnum(light_type);
|
||||
rl.SetShaderValue(self.shader, self.typeLoc, &light_type_i32, .SHADER_UNIFORM_INT);
|
||||
}
|
||||
|
||||
pub fn set_intensity(self: *const Self, intensity: f32) void {
|
||||
rl.SetShaderValue(self.shader, self.intensityLoc, &intensity, .SHADER_UNIFORM_FLOAT);
|
||||
}
|
||||
|
||||
pub fn set_position(self: *const Self, pos: rl.Vector3) void {
|
||||
const position = [3]f32{ pos.x, pos.y, pos.z };
|
||||
rl.SetShaderValue(self.shader, self.positionLoc, &position, .SHADER_UNIFORM_VEC3);
|
||||
}
|
||||
|
||||
pub fn set_target(self: *const Self, target: rl.Vector3) void {
|
||||
const target_f32 = [3]f32{ target.x, target.y, target.z };
|
||||
rl.SetShaderValue(self.shader, self.targetLoc, &target_f32, .SHADER_UNIFORM_VEC3);
|
||||
}
|
||||
|
||||
pub fn set_color(self: *const Self, color: rl.Color) void {
|
||||
const color_f32 = [4]f32{
|
||||
@as(f32, @floatFromInt(color.r)) / 255.0,
|
||||
@as(f32, @floatFromInt(color.g)) / 255.0,
|
||||
@as(f32, @floatFromInt(color.b)) / 255.0,
|
||||
@as(f32, @floatFromInt(color.a)) / 255.0,
|
||||
};
|
||||
rl.SetShaderValue(self.shader, self.colorLoc, &color_f32, .SHADER_UNIFORM_VEC4);
|
||||
}
|
@ -1,11 +1,7 @@
|
||||
const Self = @This();
|
||||
const rl = @import("raylib");
|
||||
const std = @import("std");
|
||||
const Gltf = @import("zgltf");
|
||||
const EmulatorModel = @import("./emulator-model.zig");
|
||||
const ROM = @import("./roms.zig").ROM;
|
||||
const Light = @import("./light.zig");
|
||||
const ShaderCode = @import("./shader-code.zig");
|
||||
|
||||
const ChipContext = @import("chip.zig");
|
||||
const RaylibChip = @import("raylib-chip.zig");
|
||||
@ -14,26 +10,12 @@ const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const StringList = std.ArrayList([]const u8);
|
||||
|
||||
const max_lights: u32 = 2;
|
||||
|
||||
allocator: Allocator,
|
||||
|
||||
emulator: EmulatorModel,
|
||||
|
||||
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,
|
||||
lights: [max_lights]Light,
|
||||
|
||||
powered: *bool,
|
||||
chip: *ChipContext,
|
||||
raylib_chip: *RaylibChip,
|
||||
chip_sound: rl.Sound,
|
||||
screen_texture: rl.RenderTexture2D,
|
||||
rom: ?ROM = null,
|
||||
camera: rl.Camera3D,
|
||||
|
||||
pub fn genSinWave(wave: *rl.Wave, frequency: f32) void {
|
||||
assert(wave.sampleSize == 16); // Only 16 bits are supported
|
||||
@ -50,83 +32,7 @@ pub fn genSinWave(wave: *rl.Wave, frequency: f32) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn getCameraProjection(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 getScreenDirectionFromCamera(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 getPrefferedDistanceToBox(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 = getCameraProjection(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 = getScreenDirectionFromCamera(&mat_proj, &mat_view, screen_middle);
|
||||
const top_middle_dir = getScreenDirectionFromCamera(&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) !Self {
|
||||
const shader_code = try ShaderCode.init(allocator, max_lights);
|
||||
defer shader_code.deinit();
|
||||
const shader = rl.LoadShaderFromMemory(shader_code.vertex, shader_code.fragment);
|
||||
errdefer rl.UnloadShader(shader);
|
||||
|
||||
if (shader.id == rl.rlGetShaderIdDefault()) {
|
||||
return error.CompileShader;
|
||||
}
|
||||
|
||||
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.6, 0.6, 1, 1.0 }, .SHADER_UNIFORM_VEC4);
|
||||
|
||||
var lights: [max_lights]Light = undefined;
|
||||
for (0..max_lights) |i| {
|
||||
lights[i] = Light.init(shader, i);
|
||||
}
|
||||
|
||||
lights[0].set_directional(rl.WHITE, rl.Vector3.new(0.2, 0, -0.2), 1, rl.Vector3.zero());
|
||||
lights[1].set_directional(rl.WHITE, rl.Vector3.new(0.2, 0, 0.2), 1, rl.Vector3.zero());
|
||||
|
||||
var chip = try allocator.create(ChipContext);
|
||||
chip.* = try ChipContext.init(allocator);
|
||||
|
||||
@ -148,45 +54,19 @@ pub fn init(allocator: Allocator) !Self {
|
||||
var raylib_chip = try allocator.create(RaylibChip);
|
||||
raylib_chip.* = RaylibChip.init(chip, chip_sound);
|
||||
|
||||
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 {
|
||||
.allocator = allocator,
|
||||
.emulator = emulator,
|
||||
.shader = shader,
|
||||
.lights = lights,
|
||||
|
||||
.chip = chip,
|
||||
.raylib_chip = raylib_chip,
|
||||
.chip_sound = chip_sound,
|
||||
.screen_texture = screen_texture,
|
||||
.powered = powered,
|
||||
.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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -197,182 +77,12 @@ pub fn set_rom(self: *Self, rom: ROM) void {
|
||||
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.camera;
|
||||
const emulator = &self.emulator;
|
||||
|
||||
if (rl.IsWindowResized()) {
|
||||
const distance = getPrefferedDistanceToBox(camera, emulator.bbox);
|
||||
const direction = camera.position.sub(emulator.position).normalize();
|
||||
camera.position = emulator.position.add(direction.scale(distance));
|
||||
}
|
||||
|
||||
if (rl.Vector3Equals(camera.position, rl.Vector3Zero()) == 1) {
|
||||
const distance = getPrefferedDistanceToBox(camera, self.emulator.bbox);
|
||||
camera.target = emulator.position;
|
||||
camera.position = emulator.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.emulator.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(emulator.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 {
|
||||
self.updateCamera(dt);
|
||||
|
||||
const camera = &self.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, .SHADER_UNIFORM_VEC3);
|
||||
|
||||
const ray = rl.GetMouseRay(rl.GetMousePosition(), self.camera);
|
||||
if (self.emulator.isOverPowerSwitch(ray)) {
|
||||
if (rl.IsMouseButtonPressed(rl.MouseButton.MOUSE_BUTTON_LEFT)) {
|
||||
self.toggle_power();
|
||||
}
|
||||
}
|
||||
|
||||
self.raylib_chip.update_input();
|
||||
if (self.powered.*) {
|
||||
self.raylib_chip.update(dt);
|
||||
}
|
||||
|
||||
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;
|
||||
// }
|
||||
self.raylib_chip.update(dt);
|
||||
}
|
||||
|
||||
pub fn draw(self: *Self) void {
|
||||
rl.ClearBackground(rl.Color{ .r = 33, .g = 33, .b = 33 });
|
||||
|
||||
rl.BeginShaderMode(self.shader);
|
||||
{
|
||||
rl.BeginMode3D(self.camera);
|
||||
self.emulator.draw();
|
||||
rl.EndMode3D();
|
||||
}
|
||||
rl.EndShaderMode();
|
||||
self.raylib_chip.render();
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
const Self = @This();
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
allocator: Allocator,
|
||||
vertex: [:0]u8,
|
||||
fragment: [:0]u8,
|
||||
|
||||
pub fn init(allocator: Allocator, max_lights_count: u32) !Self {
|
||||
const base_vertex_code = @embedFile("shaders/main_vs.glsl");
|
||||
const base_fragment_code = @embedFile("shaders/main_fs.glsl");
|
||||
|
||||
const vertex = try allocator.dupeZ(u8, base_vertex_code);
|
||||
errdefer allocator.free(vertex);
|
||||
|
||||
const newline = std.mem.indexOfScalar(u8, base_fragment_code, '\n') orelse unreachable;
|
||||
const first_line = base_fragment_code[0..newline];
|
||||
const after_first_line = base_fragment_code[(newline+1)..];
|
||||
const fragment = try std.fmt.allocPrintZ(allocator,
|
||||
\\{s}
|
||||
\\#define MAX_LIGHTS {}
|
||||
\\{s}
|
||||
, .{ first_line, max_lights_count, after_first_line });
|
||||
errdefer allocator.free(fragment);
|
||||
|
||||
return Self {
|
||||
.allocator = allocator,
|
||||
.vertex = vertex,
|
||||
.fragment = fragment
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.allocator.free(self.vertex);
|
||||
self.allocator.free(self.fragment);
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
#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
|
||||
layout (location = 0) out vec4 finalColor;
|
||||
layout (location = 1) out vec4 bloomColor;
|
||||
|
||||
struct MaterialProperty {
|
||||
vec3 color;
|
||||
int useSampler;
|
||||
sampler2D sampler;
|
||||
};
|
||||
|
||||
#define LIGHT_DIRECTIONAL 0
|
||||
#define LIGHT_POINT 1
|
||||
|
||||
struct Light {
|
||||
int type;
|
||||
bool enabled;
|
||||
float intensity;
|
||||
vec3 position;
|
||||
vec4 color;
|
||||
|
||||
// Directional light arguments:
|
||||
vec3 target;
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
finalColor = fragColor * texelColor;
|
||||
|
||||
{ // Apply lights
|
||||
vec3 lightDot = vec3(0.0);
|
||||
vec3 normal = normalize(fragNormal);
|
||||
vec3 viewD = normalize(viewPos - fragPosition);
|
||||
vec3 specular = vec3(0.0);
|
||||
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
if (!lights[i].enabled) continue;
|
||||
|
||||
vec3 light = vec3(0.0);
|
||||
if (lights[i].type == LIGHT_DIRECTIONAL) {
|
||||
light = -normalize(lights[i].target - lights[i].position);
|
||||
} else 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 * lights[i].intensity;
|
||||
|
||||
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 *= ((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0) + (ambient/10.0)*colDiffuse);
|
||||
}
|
||||
|
||||
// Gamma correction
|
||||
finalColor = pow(finalColor, vec4(1.0/1.9));
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user