diff --git a/README.md b/README.md index 1936a73..40c89b2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 🟢 Yelbegen - Zig Oyun Motoru -**Yelbegen**, öğrenme ve deney amaçlı olarak geliştirilen **modüler bir oyun motoru**dur. +**Yelbegen**, öğrenme ve deney amaçlı olarak geliştirilen **modüler bir simülatör motoru**dur. Motor, **Zig** programlama dili kullanılarak yazılmakta ve Raylib ile RayGUI kütüphanelerini kullanmaktadır. --- diff --git a/build.zig b/build.zig index 24329f9..5569f99 100644 --- a/build.zig +++ b/build.zig @@ -91,6 +91,23 @@ pub fn build(b: *std.Build) void { }, }); + const mod4 = b.addModule("InputOps", .{ + // The root source file is the "entry point" of this module. Users of + // this module will only be able to access public declarations contained + // in this file, which means that if you have declarations that you + // intend to expose to consumers that were defined in other files part + // of this module, you will have to make sure to re-export them from + // the root file. + .root_source_file = b.path("src/input/base_input.zig"), + // Later on we'll use this module as the root module of a test executable + // which requires us to specify a target. + .target = target, + .imports = &.{ + .{.name = "raylib", .module = raylib}, + .{.name = "Cam", .module = mod2}, + }, + }); + // Here we define an executable. An executable needs to have a root module // which needs to expose a `main` function. While we could add a main function // to the module defined above, it's sometimes preferable to split business @@ -129,8 +146,9 @@ pub fn build(b: *std.Build) void { // can be extremely useful in case of collisions (which can happen // importing modules from different packages). .{ .name = "Drawers", .module = mod }, - .{ .name = "Cam", .module = mod2}, - .{ .name="Gui", .module = mod3}, + .{ .name = "Cam", .module = mod2 }, + .{ .name= "Gui", .module = mod3 }, + .{ .name= "InputOps", .module = mod4 }, }, }), }); diff --git a/src/graphics/gui.zig b/src/graphics/gui.zig index 1ba6f7f..a38e450 100644 --- a/src/graphics/gui.zig +++ b/src/graphics/gui.zig @@ -2,29 +2,34 @@ const gui = @import("raygui"); const rl = @import("raylib"); const std = @import("std"); +pub var is_cursor_hidden: bool = undefined; + pub const GUI = struct { - pos : rl.Rectangle, - gui_pos : rl.Rectangle, + height: f32, + width: f32, + guiPos: rl.Rectangle, - pub fn init(pos : rl.Rectangle) GUI { - var Gui__ = GUI { - .pos = pos, - .gui_pos = rl.Rectangle.init(0, 0, 0, 0), + pub fn init(width_: f32, height_: f32) GUI { + var Gui__ = GUI{ + .height = height_, + .width = width_, + .guiPos = rl.Rectangle.init(0, 0, 0, 0), }; - Gui__.addRightSide(); return Gui__; } - fn addRightSide(self : *GUI) void { - self.gui_pos.height = self.pos.height; - self.gui_pos.width = self.pos.width / 4; - self.gui_pos.x = self.pos.x/4 * 3; - self.gui_pos.y = 0; + fn addRightSide(self: *GUI) void { + // menü ekranın sağında olmalıdır ve absolute bir + // pozisyondan kaçınır + self.guiPos.height = self.height; + self.guiPos.width = self.width / 4.0; + self.guiPos.x = self.width / 4.0 * 3.0; + self.guiPos.y = 0.0; } - pub fn draw(self : *GUI) void { + pub fn draw(self: *GUI) void { //panel - rl.drawRectangleRec(self.gui_pos, .dark_gray); + rl.drawRectangleRec(self.guiPos, .red); } -}; \ No newline at end of file +}; diff --git a/src/input/base_input.zig b/src/input/base_input.zig index e69de29..75d25cb 100644 --- a/src/input/base_input.zig +++ b/src/input/base_input.zig @@ -0,0 +1,195 @@ +const std = @import("std"); +const rl = @import("raylib"); +const cm = @import("Cam"); + + +pub var is_object_dragging : bool = undefined; // drag işlemi için gerekli +pub var freeMode = false; +// ışın ve ışına bağlı çarpışma ray ve raycollison değişkenleri ile yapılır. + +var selected_shape_index:?usize = 0; + +pub const ShapeType = enum { + cube, + rectangle, + sphere, + cylinder, +}; //tipler + +pub const Shape = struct { + position: rl.Vector3, + color: rl.Color, + + data: union(enum) { + // 'box' terimi hem küpü hem de dikdörtgen prizmayı kapsar + cube: struct { + width: f32, + height: f32, + length: f32, + }, + sphere: struct { + radius: f32, + }, + cylinder: struct { + radius: f32, + height: f32, + }, + }, + + pub fn initCube(position: rl.Vector3, color: rl.Color, width: f32, height: f32, length: f32) Shape { + return Shape{ + .position = position, + .color = color, + .data = .{ .cube = .{ .width = width, .height = height, .length = length } }, + }; + } + + // Küre için özel init metodu + pub fn initSphere(position: rl.Vector3, color: rl.Color, radius: f32) Shape { + return Shape{ + .position = position, + .color = color, + .data = .{ .sphere = .{ .radius = radius } }, + }; + } + + // Silindir için özel init metodu + pub fn initCylinder(position: rl.Vector3, color: rl.Color, radius: f32, height: f32) Shape { + return Shape{ + .position = position, + .color = color, + .data = .{ .cylinder = .{ .radius = radius, .height = height } }, + }; + } +}; + + +// Raylib döngüsünde kullanmak için çizim fonksiyonu +pub fn drawShapes(shapes : *std.ArrayList(Shape)) void { + for (shapes.items, 0..) |shape, index| { + // Seçili nesneyi işaretle + if (selected_shape_index) |sel_index| { + if (index == sel_index) { + // Seçili nesnenin sınırlarını çiz + switch (shape.data) { + .cube => |d| { + //const box = getBoundingBoxForCube(shape.position, d.width, d.height, d.length); + //rl.drawCubeWiresV(box.min, box.max, .yellow); + rl.drawCubeWires(shape.position, d.width + 0.5, d.height + 0.5, d.length + 0.5, .green); + }, + .sphere => |d| rl.drawSphereWires(shape.position, d.radius + 0.5, 16, 16, .green), + .cylinder => |d| rl.drawCylinderWires(shape.position, d.radius + 0.5, d.radius + 0.5, d.height, 10, .green), + } + } + } + + // Asıl nesneyi çiz + switch (shape.data) { + .cube => |d| rl.drawCubeV(shape.position, rl.Vector3.init(d.width, d.height, d.length), shape.color), + .sphere => |d| rl.drawSphere(shape.position, d.radius, shape.color), + .cylinder => |d| rl.drawCylinder(shape.position, d.radius, d.radius, d.height, 10, shape.color), + } + } +} + +pub fn findClosestHitObject( + mouse_position: rl.Vector2, + camera: rl.Camera3D, + shapes: *std.ArrayList(Shape), +) void { + // Fare tuşuna basılıyorsa kontrol et + if (!rl.isMouseButtonPressed(.left)) { + return; + } + + const ray = rl.getScreenToWorldRay(mouse_position, camera); + + var closest_hit_index: ?usize = null; + var closest_distance = std.math.floatMax(f32); + + for (shapes.items, 0..) |shape, index| { + var hit_info = rl.RayCollision{.hit = false, .distance = 0.0, + .normal = rl.Vector3.init(0, 0, 0), + .point = rl.Vector3.init(0, 0, 0)}; + + switch (shape.data) { + .cube => |cube_data| { + const box = getBoundingBoxForCube(shape.position, + cube_data.width, cube_data.height, cube_data.length); + hit_info = rl.getRayCollisionBox(ray, box); + }, + .sphere => |sphere_data| { + hit_info = rl.getRayCollisionSphere(ray, shape.position, sphere_data.radius); + }, + .cylinder => |cylinder_data| { + // Performans için BoundingBox çarpışma testini kullan + const box = getBoundingBoxForCylinder(shape.position, cylinder_data.radius, cylinder_data.height); + hit_info = rl.getRayCollisionBox(ray, box); + }, + } + + if (hit_info.hit and hit_info.distance < closest_distance) { + closest_hit_index = index; + closest_distance = hit_info.distance; + } + } + + selected_shape_index = closest_hit_index; +} + + +pub fn getBoundingBoxForCube(position: rl.Vector3, width: f32, height: f32, length: f32) rl.BoundingBox { + // Nesnenin boyutlarının yarısını hesapla + const half_width = width / 2.0; + const half_height = height / 2.0; + const half_length = length / 2.0; + + // Minimum köşeyi (sol alt arka) hesapla + const min_x = position.x - half_width; + const min_y = position.y - half_height; + const min_z = position.z - half_length; + + // Maksimum köşeyi (sağ üst ön) hesapla + const max_x = position.x + half_width; + const max_y = position.y + half_height; + const max_z = position.z + half_length; + + return rl.BoundingBox{ + .min = rl.Vector3.init(min_x, min_y, min_z), + .max = rl.Vector3.init(max_x, max_y, max_z), + }; +} + + +pub fn getBoundingBoxForCylinder(position: rl.Vector3, radius: f32, height: f32) rl.BoundingBox { + // Yüksekliğin yarısını hesapla + const half_height = height / 2.0; + + // Minimum köşe noktasını (min x, min y, min z) hesapla + const min_x = position.x - radius; + const min_y = position.y - half_height; + const min_z = position.z - radius; + + // Maksimum köşe noktasını (max x, max y, max z) hesapla + const max_x = position.x + radius; + const max_y = position.y + half_height; + const max_z = position.z + radius; + + // BoundingBox yapısını döndür + return rl.BoundingBox{ + .min = rl.Vector3.init(min_x, min_y, min_z), + .max = rl.Vector3.init(max_x, max_y, max_z), + }; +} + +pub fn inputControls() void { + + if (rl.isMouseButtonPressed(.right)) { + freeMode = !freeMode; + if (freeMode) { + rl.disableCursor(); + } else { + rl.enableCursor(); + } + } +} \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index 5bcc141..42629c2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,38 +4,56 @@ const gi = @import("raygui"); const gr = @import("Drawers"); const cm = @import("Cam"); const panel = @import("Gui"); +const io = @import("InputOps"); var text: [:0]u8 = undefined; +const screen_width = 1920; +const screen_height = 1080; +const allocator = std.heap.c_allocator; // C deki malloc a ulaşır + pub fn main() anyerror!void { + + var shapes = try std.ArrayList(io.Shape).initCapacity(allocator, 1024); + defer shapes.deinit(allocator); - const base_cube = rl.Vector3.init(-20, 20, 0); + var my_gui = panel.GUI.init(@floatFromInt(screen_width), @floatFromInt(screen_height)); + + const my_cube = io.Shape.initCube(rl.Vector3.init(0, 0, 0), + .red, 2.0, 2.0, 2.0); + const my_sphere = io.Shape.initSphere(rl.Vector3.init(2.0, 0, 0), .gold, 1.0); + const my_cylinder = io.Shape.initCylinder(rl.Vector3.init(-2.0, 0, 0), .pink, 1, 1); - const screen_height = 1200; - const screen_width = 800; + try shapes.append(allocator, my_cube); + try shapes.append(allocator, my_sphere); + try shapes.append(allocator, my_cylinder); - var my_gui = panel.GUI.init(rl.Rectangle.init(0, 0, screen_width, screen_height)); - - rl.initWindow(screen_height, screen_width, "YELBEGEN"); + panel.is_cursor_hidden = false; + io.is_object_dragging = false; + + rl.initWindow(screen_width, screen_height, "YELBEGEN"); + defer rl.closeWindow(); var cam = rl.Camera3D{ - .position = rl.Vector3.init(290, 120, 250), - .target = rl.Vector3.init(20,20,20), - .up = rl.Vector3.init(0, 2.0, 0), + .position = rl.Vector3.init(10, 10, 10), + .target = rl.Vector3.init(0,0,0), + .up = rl.Vector3.init(0, 1.0, 0), .fovy = 45.0, .projection = rl.CameraProjection.perspective, }; + + rl.setTargetFPS(60); + + while (!rl.windowShouldClose()) { - //update - if (rl.isKeyDown(.a)) { - cam.position.x += 0.01; - } else if (rl.isKeyDown(.b)) { - cam.position.y += 0.01; - } else if (rl.isKeyDown(.c)) { - cam.position.z += 0.01; - } + io.inputControls(); + io.findClosestHitObject(rl.getMousePosition(), cam, &shapes); + + if (io.freeMode) { + rl.updateCamera(&cam, .free); + } rl.beginDrawing(); defer rl.endDrawing(); @@ -43,12 +61,19 @@ pub fn main() anyerror!void { rl.clearBackground(.ray_white); rl.beginMode3D(cam); - rl.drawCube(base_cube, 40, 40, 40, .red); - rl.drawCube(rl.Vector3.zero(), 300, 1, 300, .gray); + io.drawShapes(&shapes); + rl.drawGrid(10, 1.0); rl.endMode3D(); rl.drawText(rl.textFormat("x:%.2f y:%.2f z:%.2f", .{cam.position.x, cam.position.y, cam.position.z}), 180, 200, 20, .light_gray); + rl.drawText(rl.textFormat("x : %d y : %d", .{rl.getScreenWidth(), rl.getScreenHeight()}), 50, 50, 20, .red); my_gui.draw(); + rl.drawFPS(200, 200); + if (io.is_object_dragging) { + rl.drawText("Nesne drag...", 10, 10, 20, .dark_gray); + } else { + rl.drawText("Nesne hazır...", 10, 10, 20, .dark_gray); + } }