0xc3 3 недель назад
Родитель
Сommit
e6154bf86c
3 измененных файлов с 194 добавлено и 26 удалено
  1. BIN
      res/images/001.png
  2. 46 0
      src/cmd/sandbox/assets.odin
  3. 148 26
      src/cmd/sandbox/main.odin

BIN
res/images/001.png


+ 46 - 0
src/cmd/sandbox/assets.odin

@@ -0,0 +1,46 @@
+package main
+
+import "core:fmt"
+import "core:image/png"
+import "core:os"
+import sg "third-party:sokol/gfx"
+
+Texture_Asset :: struct {
+	img:  sg.Image,
+	view: sg.View,
+	w, h: i32,
+}
+
+load_texture :: proc(path: string) -> (Texture_Asset, bool) {
+	// 1. Загружаем файл в память
+	data, ok := os.read_entire_file(path, context.temp_allocator)
+	if !ok {
+		fmt.println("Failed to read file:", path)
+		return {}, false
+	}
+
+	// 2. Декодируем PNG
+	img, err := png.load_from_bytes(data, {.alpha_add_if_missing}, context.temp_allocator)
+	if err != nil {
+		fmt.println("Failed to decode PNG:", path, err)
+		return {}, false
+	}
+
+	// 3. Создаем Sokol Image
+	sg_img := sg.make_image(
+		{
+			width = i32(img.width),
+			height = i32(img.height),
+			pixel_format = .RGBA8,
+			data = {
+				mip_levels = {0 = {ptr = raw_data(img.pixels.buf), size = len(img.pixels.buf)}},
+			},
+		},
+	)
+
+	// 4. Создаем Sokol View (для шейдера)
+	sg_view := sg.make_view({texture = {image = sg_img}})
+
+	return Texture_Asset{img = sg_img, view = sg_view, w = i32(img.width), h = i32(img.height)},
+		true
+}

+ 148 - 26
src/cmd/sandbox/main.odin

@@ -17,7 +17,8 @@ import sglue "third-party:sokol/glue"
 import slog "third-party:sokol/log"
 
 // --- Constants ---
-MAX_ENTITIES :: 100_000
+MAX_ENTITIES :: 1024
+MAX_BOX :: 32
 
 // --- Data Types ---
 
@@ -31,6 +32,49 @@ Mesh :: struct {
 	indices:  [dynamic]u16,
 }
 
+Item_Kind :: enum u16 {
+	None,
+	Sword,
+	Potion,
+	Stone,
+}
+
+Item_Info :: struct {
+	name:      string,
+	max_stack: u32,
+}
+
+item_db := [Item_Kind]Item_Info {
+	.None   = {"Empty", 0},
+	.Sword  = {"Iron Sword", 1},
+	.Potion = {"Health Potion", 64},
+	.Stone  = {"Cobblestone", 64},
+}
+
+Box_Id :: distinct u32
+
+Box_Slot :: struct {
+	kind:  Item_Kind,
+	count: u32,
+}
+
+Box_System :: struct {
+	is_open: bool,
+	slots:   #soa[dynamic]Box_Slot,
+}
+
+// --- API ---
+
+box_system_push :: proc(size: u32) -> Box_Id {
+	box := Box_System {
+		slots = make(#soa[dynamic]Box_Slot, 0, size),
+	}
+	id := len(state.box_sys)
+	append_soa(&state.box_sys, box)
+	return Box_Id(id)
+}
+
+
 Entity_Kind :: enum u8 {
 	Player,
 	Enemy,
@@ -38,11 +82,12 @@ Entity_Kind :: enum u8 {
 }
 
 Entity :: struct {
-	pos:   linalg.Vector3f32,
-	vel:   linalg.Vector3f32,
-	color: linalg.Vector3f32,
-	kind:  Entity_Kind,
-	scale: f32,
+	pos:    linalg.Vector3f32,
+	vel:    linalg.Vector3f32,
+	color:  linalg.Vector3f32,
+	kind:   Entity_Kind,
+	scale:  f32,
+	box_id: Box_Id,
 }
 
 Camera :: struct {
@@ -52,7 +97,7 @@ Camera :: struct {
 }
 
 Input_State :: struct {
-	keys:         map[sapp.Keycode]bool,
+	keys:         #sparse[sapp.Keycode]bool,
 	mouse_dx:     f32,
 	mouse_dy:     f32,
 	mouse_x:      f32,
@@ -62,21 +107,23 @@ Input_State :: struct {
 }
 
 App_State :: struct {
-	arena:          virtual.Arena,
-	allocator:      mem.Allocator,
-	pass_action:    sg.Pass_Action,
-	mu_ctx:         mu.Context,
-	atlas_img:      sg.Image,
-	atlas_view:     sg.View,
-	atlas_smp:      sg.Sampler,
-	ui_pip:         sgl.Pipeline,
-	scene_pip:      sgl.Pipeline,
-	mesh_cube:      Mesh,
-	entities:       #soa[dynamic]Entity,
-	dpi_scale:      f32,
-	camera:         Camera,
-	input:          Input_State,
-	selected_color: [3]f32,
+	arena:             virtual.Arena,
+	allocator:         mem.Allocator,
+	pass_action:       sg.Pass_Action,
+	mu_ctx:            mu.Context,
+	atlas_img:         sg.Image,
+	atlas_view:        sg.View,
+	atlas_smp:         sg.Sampler,
+	ui_pip:            sgl.Pipeline,
+	scene_pip:         sgl.Pipeline,
+	mesh_cube:         Mesh,
+	box_sys:           #soa[dynamic]Box_System,
+	entities:          #soa[dynamic]Entity,
+	dpi_scale:         f32,
+	camera:            Camera,
+	last_input, input: Input_State,
+	selected_color:    [3]f32,
+	texture01:         Texture_Asset,
 }
 
 state: App_State
@@ -171,6 +218,11 @@ system_input_player :: proc(dt: f32) {
 	if state.input.keys[.SPACE] && player.pos.y <= 0.51 {
 		player.vel.y = 10.0
 	}
+
+	if state.last_input.keys[.TAB] && !state.input.keys[.TAB] {
+		state.box_sys[player.box_id].is_open = !state.box_sys[player.box_id].is_open
+		fmt.println("box.is_open: {}", state.box_sys[player.box_id].is_open)
+	}
 }
 
 system_builder :: proc(view, proj: linalg.Matrix4f32) {
@@ -355,6 +407,12 @@ init :: proc "c" () {
 	sg.setup({environment = sglue.environment(), logger = {func = slog.func}})
 	sgl.setup({logger = {func = slog.func}})
 
+	if tex, ok := load_texture("res/images/001.png"); ok {
+		state.texture01 = tex
+	} else {
+		fmt.println("File not found, using fallback color")
+	}
+
 	mu.init(&state.mu_ctx)
 	state.mu_ctx.text_width = mu.default_atlas_text_width
 	state.mu_ctx.text_height = mu.default_atlas_text_height
@@ -397,8 +455,12 @@ init :: proc "c" () {
 
 	state.mesh_cube = make_cube_mesh(state.allocator)
 	state.entities = make(#soa[dynamic]Entity, 0, MAX_ENTITIES, state.allocator)
+	fmt.printfln("entity.cap: {}", cap(state.entities))
+
+	state.box_sys = make(#soa[dynamic]Box_System, 0, MAX_BOX, state.allocator)
 
 	spawn_entity({0, 5, 0}, .Player)
+	state.entities[0].box_id = box_system_push(32)
 	state.camera.dist = 10.0
 	state.camera.pitch = 0.5
 
@@ -408,6 +470,8 @@ init :: proc "c" () {
 		y := rand.float32_range(10, 50)
 		spawn_entity({x, y, z}, .Enemy)
 	}
+
+	fmt.printfln("entity.size: {}", len(state.entities))
 }
 
 frame :: proc "c" () {
@@ -423,10 +487,6 @@ frame :: proc "c" () {
 	view, proj := get_camera_matrices()
 	system_builder(view, proj)
 
-	state.input.mouse_dx = 0
-	state.input.mouse_dy = 0
-	state.input.left_clicked = false
-
 	mu.begin(&state.mu_ctx)
 
 	if mu.begin_window(&state.mu_ctx, "Stats", {10, 10, 200, 120}) {
@@ -457,6 +517,23 @@ frame :: proc "c" () {
 
 		mu.end_window(&state.mu_ctx)
 	}
+
+	if state.box_sys[state.entities[0].box_id].is_open {
+		w := i32(sapp.widthf() / 2) / i32(state.dpi_scale) - (200 / 2)
+		h := i32(sapp.heightf() / 2) / i32(state.dpi_scale) - (120 / 2)
+		if mu.begin_window(&state.mu_ctx, "Box", {w, h, 200, 120}) {
+			// 1. Рисуем загруженную картинку
+			mu.label(&state.mu_ctx, "Loaded Texture:")
+			mu.image(&state.mu_ctx, state.texture01.view.id, 64, 64)
+			mu.image_button(&state.mu_ctx, state.texture01.view.id, 64, 64)
+
+			// 2. Рисуем отсутствующую картинку (ID = 0) -> будет красный квадрат
+			mu.label(&state.mu_ctx, "Missing Texture (Fallback):")
+			mu.image(&state.mu_ctx, 0, 64, 64, {255, 0, 255, 255})
+			mu.end_window(&state.mu_ctx)
+		}
+	}
+
 	mu.end(&state.mu_ctx)
 
 	system_render(view, proj)
@@ -466,6 +543,17 @@ frame :: proc "c" () {
 	sgl.draw()
 	sg.end_pass()
 	sg.commit()
+
+	// 3. Подготовка к СЛЕДУЮЩЕМУ кадру (End of Frame)
+
+	// Сначала сохраняем текущее состояние как "прошлое"
+	state.last_input = state.input
+
+	// Затем сбрасываем дельты (движение мыши и клики живут 1 кадр)
+	// Но НЕ сбрасываем keys, так как клавиши могут быть зажаты долго
+	state.input.mouse_dx = 0
+	state.input.mouse_dy = 0
+	state.input.left_clicked = false
 }
 
 // --- Render Impl ---
@@ -536,6 +624,12 @@ render_ui :: proc() {
 				true,
 			)
 			sgl.begin_quads()
+		case ^mu.Command_Image:
+			if c.id == 0 {
+				draw_rect(c.rect, c.color)
+			} else {
+				draw_image_rect(c.rect, c.id, c.color)
+			}
 		}
 	}
 	sgl.end()
@@ -546,6 +640,34 @@ render_ui :: proc() {
 	sgl.pop_pipeline()
 }
 
+draw_image_rect :: proc(r: mu.Rect, view_id: u32, c: mu.Color) {
+	sgl.end()
+
+	view := sg.View {
+		id = view_id,
+	}
+
+	sgl.enable_texture()
+	sgl.texture(view, state.atlas_smp)
+
+	sgl.begin_quads()
+
+	x0, y0 := f32(r.x), f32(r.y)
+	x1, y1 := f32(r.x + r.w), f32(r.y + r.h)
+
+	sgl.c4b(c.r, c.g, c.b, c.a)
+	sgl.v2f_t2f(x0, y0, 0, 0)
+	sgl.v2f_t2f(x1, y0, 1, 0)
+	sgl.v2f_t2f(x1, y1, 1, 1)
+	sgl.v2f_t2f(x0, y1, 0, 1)
+
+	sgl.end()
+
+	sgl.enable_texture()
+	sgl.texture(state.atlas_view, state.atlas_smp)
+	sgl.begin_quads()
+}
+
 draw_rect :: proc(r: mu.Rect, c: mu.Color) {
 	push_quad(r, mu.default_atlas[mu.DEFAULT_ATLAS_WHITE], c)
 }