0xc3 2 săptămâni în urmă
părinte
comite
cd450d1952
1 a modificat fișierele cu 147 adăugiri și 68 ștergeri
  1. 147 68
      src/cmd/sandbox/main.odin

+ 147 - 68
src/cmd/sandbox/main.odin

@@ -160,7 +160,11 @@ Transform_Component :: struct {
 }
 
 Physics_Component :: struct {
-	velocity: Vec3,
+	velocity:      Vec3,
+	jump_timer:    f32,
+	jump_duration: f32,
+	start_pos:     Vec3,
+	is_jumping:    bool,
 }
 
 Render_Component :: struct {}
@@ -506,33 +510,47 @@ push_entity :: proc() -> Entity_Id {
 // Проверка пересечения луча и AABB (Axis Aligned Bounding Box)
 // Возвращает hit (попадание) и dist (дистанцию до точки входа)
 ray_aabb_intersect :: proc(origin, dir: Vec3, box_min, box_max: Vec3) -> (bool, f32) {
-	t_min := (box_min.x - origin.x) / dir.x
-	t_max := (box_max.x - origin.x) / dir.x
-
-	if t_min > t_max do t_min, t_max = t_max, t_min
-
-	ty_min := (box_min.y - origin.y) / dir.y
-	ty_max := (box_max.y - origin.y) / dir.y
-
-	if ty_min > ty_max do ty_min, ty_max = ty_max, ty_min
-
-	if (t_min > ty_max) || (ty_min > t_max) do return false, 0
-
-	if ty_min > t_min do t_min = ty_min
-	if ty_max < t_max do t_max = ty_max
-
-	tz_min := (box_min.z - origin.z) / dir.z
-	tz_max := (box_max.z - origin.z) / dir.z
-
-	if tz_min > tz_max do tz_min, tz_max = tz_max, tz_min
+	t_min: f32 = -math.F32_MAX
+	t_max: f32 = math.F32_MAX
+
+	// X axis
+	if math.abs(dir.x) > 0.0001 {
+		tx_min := (box_min.x - origin.x) / dir.x
+		tx_max := (box_max.x - origin.x) / dir.x
+		if tx_min > tx_max do tx_min, tx_max = tx_max, tx_min
+		t_min = math.max(t_min, tx_min)
+		t_max = math.min(t_max, tx_max)
+	} else if origin.x < box_min.x || origin.x > box_max.x {
+		return false, 0
+	}
 
-	if (t_min > tz_max) || (tz_min > t_max) do return false, 0
+	// Y axis
+	if math.abs(dir.y) > 0.0001 {
+		ty_min := (box_min.y - origin.y) / dir.y
+		ty_max := (box_max.y - origin.y) / dir.y
+		if ty_min > ty_max do ty_min, ty_max = ty_max, ty_min
+		t_min = math.max(t_min, ty_min)
+		t_max = math.min(t_max, ty_max)
+	} else if origin.y < box_min.y || origin.y > box_max.y {
+		return false, 0
+	}
 
-	if tz_min > t_min do t_min = tz_min
-	if tz_max < t_max do t_max = tz_max
+	// Z axis
+	if math.abs(dir.z) > 0.0001 {
+		tz_min := (box_min.z - origin.z) / dir.z
+		tz_max := (box_max.z - origin.z) / dir.z
+		if tz_min > tz_max do tz_min, tz_max = tz_max, tz_min
+		t_min = math.max(t_min, tz_min)
+		t_max = math.min(t_max, tz_max)
+	} else if origin.z < box_min.z || origin.z > box_max.z {
+		return false, 0
+	}
 
-	if t_min < 0 do return false, 0 // Пересечение сзади луча
+	// Проверяем пересечение
+	if t_max < t_min || t_max < 0 do return false, 0
 
+	// Возвращаем ближайшую точку входа
+	if t_min < 0 do return true, t_max // Луч начинается внутри бокса
 	return true, t_min
 }
 
@@ -639,7 +657,7 @@ init :: proc "c" () {
 	ui_pip_desc.depth.write_enabled = false
 	ui_pip_desc.depth.compare = .ALWAYS
 	state.ui_pip = sgl.make_pipeline(ui_pip_desc)
-	
+
 	// Grid pipeline - no depth write, but test against depth
 	grid_pip_desc: sg.Pipeline_Desc
 	grid_pip_desc.depth.write_enabled = false
@@ -648,19 +666,28 @@ init :: proc "c" () {
 
 	// Create 3D shader and pipeline
 	state.scene_shader = sg.make_shader(scene_shader_desc(sg.query_backend()))
-	
+
 	scene_pip_desc: sg.Pipeline_Desc
 	scene_pip_desc.shader = state.scene_shader
 	scene_pip_desc.layout.buffers[0].stride = i32(size_of(Vertex))
-	scene_pip_desc.layout.attrs[ATTR_scene_position] = {format = .FLOAT3, offset = i32(offset_of(Vertex, pos))}
-	scene_pip_desc.layout.attrs[ATTR_scene_normal0] = {format = .FLOAT3, offset = i32(offset_of(Vertex, normal))}
-	scene_pip_desc.layout.attrs[ATTR_scene_color0] = {format = .FLOAT3, offset = i32(offset_of(Vertex, color))}
+	scene_pip_desc.layout.attrs[ATTR_scene_position] = {
+		format = .FLOAT3,
+		offset = i32(offset_of(Vertex, pos)),
+	}
+	scene_pip_desc.layout.attrs[ATTR_scene_normal0] = {
+		format = .FLOAT3,
+		offset = i32(offset_of(Vertex, normal)),
+	}
+	scene_pip_desc.layout.attrs[ATTR_scene_color0] = {
+		format = .FLOAT3,
+		offset = i32(offset_of(Vertex, color)),
+	}
 	scene_pip_desc.index_type = .UINT16
 	scene_pip_desc.depth.write_enabled = true
 	scene_pip_desc.depth.compare = .LESS_EQUAL
 	scene_pip_desc.cull_mode = .FRONT
 	state.scene_pip = sg.make_pipeline(scene_pip_desc)
-	
+
 	// Upload mesh data to GPU
 	upload_mesh(&state.mesh_cube)
 	upload_mesh(&state.debug_line_mesh)
@@ -672,6 +699,10 @@ init :: proc "c" () {
 
 	state.player_ent_id = push_entity()
 	add_transofrm_component(state.player_ent_id)
+	add_collider_component(state.player_ent_id, {
+						type = .AABB,
+						half = {0.5, 0.5, 0.5}, // Половина размера куба (т.к. меш от -0.5 до 0.5)
+					})
 
 	// Garden_Grid
 	{
@@ -683,9 +714,11 @@ init :: proc "c" () {
 				t.position = {(1.0 * f32(x)) + offset.x, -0.5, (1.0 * f32(z)) + offset.z}
 				t.scale.y = 0.1
 
+				add_physics_component(ent_id) // Добавляем физику для анимации
+
 				add_collider_component(
 					ent_id,
-					Collider_Component {
+					{
 						type = .AABB,
 						half = {0.5, 0.5, 0.5}, // Половина размера куба (т.к. меш от -0.5 до 0.5)
 					},
@@ -706,13 +739,19 @@ init :: proc "c" () {
 // Основная функция отрисовки всех коллайдеров
 system_render_debug :: proc(view, proj: linalg.Matrix4f32) {
 	vp := proj * view
-	
+
 	// Create debug line pipeline
 	debug_pip_desc: sg.Pipeline_Desc
 	debug_pip_desc.shader = state.scene_shader
-	debug_pip_desc.layout.attrs[ATTR_scene_position] = {format = .FLOAT3}
-	debug_pip_desc.layout.attrs[ATTR_scene_normal0] = {format = .FLOAT3}
-	debug_pip_desc.layout.attrs[ATTR_scene_color0] = {format = .FLOAT3}
+	debug_pip_desc.layout.attrs[ATTR_scene_position] = {
+		format = .FLOAT3,
+	}
+	debug_pip_desc.layout.attrs[ATTR_scene_normal0] = {
+		format = .FLOAT3,
+	}
+	debug_pip_desc.layout.attrs[ATTR_scene_color0] = {
+		format = .FLOAT3,
+	}
 	debug_pip_desc.index_type = .UINT16
 	debug_pip_desc.depth.write_enabled = false
 	debug_pip_desc.depth.compare = .LESS_EQUAL
@@ -720,7 +759,7 @@ system_render_debug :: proc(view, proj: linalg.Matrix4f32) {
 	debug_pip_desc.primitive_type = .LINES
 	debug_pip := sg.make_pipeline(debug_pip_desc)
 	defer sg.destroy_pipeline(debug_pip)
-	
+
 	sg.apply_pipeline(debug_pip)
 
 	for ent in state.entities {
@@ -733,27 +772,27 @@ system_render_debug :: proc(view, proj: linalg.Matrix4f32) {
 		case .AABB:
 			// Зеленый для AABB
 			final_half := c.half * t.scale
-			
+
 			// Calculate model matrix
 			model := linalg.matrix4_translate_f32(t.position)
 			model = model * linalg.matrix4_scale_f32(final_half * 2.0)
 			mvp := vp * model
-			
+
 			vs_params := Vs_Params{}
 			vs_params.mvp = transmute([16]f32)mvp
-			
+
 			bindings := sg.Bindings {
 				vertex_buffers = {0 = state.debug_line_mesh.vbuf},
 				index_buffer = state.debug_line_mesh.ibuf,
 			}
 			sg.apply_bindings(bindings)
 			sg.apply_uniforms(UB_vs_params, {ptr = &vs_params, size = size_of(Vs_Params)})
-			
+
 			sg.draw(0, i32(len(state.debug_line_mesh.indices)), 1)
 
 		case .Sphere:
-			// Красный для Сфер - пока пропустим, нужен отдельный меш
-			// TODO: create sphere wireframe mesh
+		// Красный для Сфер - пока пропустим, нужен отдельный меш
+		// TODO: create sphere wireframe mesh
 		}
 	}
 }
@@ -780,17 +819,24 @@ frame :: proc "c" () {
 		if hit {
 			fmt.println("Hit Entity ID:", hit_id, "at distance:", dist)
 
-			// Пример: Удалить сущность при клике
-			// (Внимание: удаление из dynamic array может инвалидировать индексы,
-			//  лучше помечать как 'dead' или использовать swap_remove аккуратно)
-
-			// Пример: Изменить масштаб при клике
-			if t, ok := get_transform_component(hit_id); ok {
-				t.position *= 1.2
+			// Проверяем, есть ли у сущности физика
+			if phys, phys_ok := get_physics_component(hit_id); phys_ok {
+				if t, t_ok := get_transform_component(hit_id); t_ok {
+					// Запускаем анимацию прыжка только если не прыгает
+					if !phys.is_jumping {
+						phys.jump_timer = 0.0
+						phys.jump_duration = 0.5 // 0.5 секунды на прыжок
+						phys.start_pos = t.position
+						phys.is_jumping = true
+					}
+				}
 			}
 		}
 	}
 
+	// Система анимации прыжков
+	system_jump_animation(dt)
+
 	// Garden_Grid
 	{
 		for id in state.garden_grid.items {
@@ -819,29 +865,29 @@ frame :: proc "c" () {
 
 	sgl.pop_matrix(); sgl.matrix_mode_projection(); sgl.pop_matrix(); sgl.pop_pipeline()
 	sg.begin_pass({action = state.pass_action, swapchain = sglue.swapchain()})
-	
+
 	// Render 3D scene FIRST
 	system_render(view, proj)
-	
+
 	// Render debug (colliders)
 	system_render_debug(view, proj)
-	
+
 	// Render simple grid with sgl (no depth write)
 	sgl.defaults()
 	sgl.load_pipeline(state.grid_pip)
 	sgl.matrix_mode_projection(); sgl.load_matrix(cast(^f32)&proj)
 	sgl.matrix_mode_modelview(); sgl.load_matrix(cast(^f32)&view)
-	
+
 	// Draw simple grid
 	sgl.begin_lines()
 	grid_size: f32 = 50.0
 	grid_spacing: f32 = 1.0
 	half := grid_size * grid_spacing * 0.5
 	y: f32 = -0.501
-	
+
 	for i in 0 ..= i32(grid_size) {
 		p := -half + f32(i) * grid_spacing
-		
+
 		// Center axes
 		if math.abs(p) < 0.0001 {
 			// X axis (red)
@@ -852,21 +898,21 @@ frame :: proc "c" () {
 			sgl.v3f(0, y, -half); sgl.v3f(0, y, half)
 			continue
 		}
-		
+
 		// Grid lines
 		if i % 10 == 0 {
 			sgl.c3f(0.4, 0.4, 0.4)
 		} else {
 			sgl.c3f(0.6, 0.6, 0.6)
 		}
-		
+
 		// Line along X
 		sgl.v3f(-half, y, p); sgl.v3f(half, y, p)
 		// Line along Z
 		sgl.v3f(p, y, -half); sgl.v3f(p, y, half)
 	}
 	sgl.end()
-	
+
 	// Render UI on top
 	sgl.defaults()
 	sgl.load_pipeline(state.ui_pip)
@@ -874,7 +920,7 @@ frame :: proc "c" () {
 	sgl.ortho(0.0, w, h, 0.0, -1.0, +1.0)
 	sgl.matrix_mode_modelview()
 	sgl.draw()
-	
+
 	sg.end_pass()
 	sg.commit()
 
@@ -921,26 +967,59 @@ system_input_player :: proc(dt: f32) {
 	}
 }
 
+system_jump_animation :: proc(dt: f32) {
+	JUMP_HEIGHT: f32 : 0.5 // Высота прыжка
+
+	for ent in state.entities {
+		phys, phys_ok := get_physics_component(ent.id)
+		if !phys_ok do continue
+
+		t, t_ok := get_transform_component(ent.id)
+		if !t_ok do continue
+
+		// Если анимация активна
+		if phys.is_jumping {
+			phys.jump_timer += dt
+
+			// Нормализованное время (0..1)
+			progress := math.clamp(phys.jump_timer / phys.jump_duration, 0.0, 1.0)
+
+			// Параболическая траектория (sin для плавности)
+			jump_offset := math.sin(progress * math.PI) * JUMP_HEIGHT
+
+			// Обновляем позицию
+			t.position.y = phys.start_pos.y + jump_offset
+
+			// Завершаем анимацию
+			if progress >= 1.0 {
+				t.position.y = phys.start_pos.y // Гарантируем точное возвращение
+				phys.is_jumping = false
+				phys.jump_timer = 0.0
+			}
+		}
+	}
+}
+
 system_render :: proc(view, proj: linalg.Matrix4f32) {
 	v := view; p := proj
 	vp := p * v
-	
+
 	// Draw entities with proper shader
 	sg.apply_pipeline(state.scene_pip)
-	
+
 	for ent in state.entities {
 		t, is_found := get_transform_component(ent.id)
 		if !is_found do continue
-		
+
 		// Calculate model matrix
 		model := linalg.matrix4_translate_f32(t.position)
 		model = model * linalg.matrix4_scale_f32(t.scale)
 		mvp := vp * model
-		
+
 		// Set uniforms
 		vs_params := Vs_Params{}
 		vs_params.mvp = transmute([16]f32)mvp
-		
+
 		// Apply bindings
 		bindings := sg.Bindings {
 			vertex_buffers = {0 = state.mesh_cube.vbuf},
@@ -948,7 +1027,7 @@ system_render :: proc(view, proj: linalg.Matrix4f32) {
 		}
 		sg.apply_bindings(bindings)
 		sg.apply_uniforms(UB_vs_params, {ptr = &vs_params, size = size_of(Vs_Params)})
-		
+
 		// Draw
 		sg.draw(0, i32(len(state.mesh_cube.indices)), 1)
 	}
@@ -1187,7 +1266,7 @@ upload_mesh :: proc(m: ^Mesh, allocator := context.temp_allocator) {
 	// Create interleaved vertex data
 	vertex_data := make([]Vertex, len(m.vertices), allocator)
 	defer if allocator == context.temp_allocator do delete(vertex_data, allocator)
-	
+
 	for i in 0 ..< len(m.vertices) {
 		vertex_data[i] = Vertex {
 			pos    = m.vertices.pos[i],
@@ -1195,7 +1274,7 @@ upload_mesh :: proc(m: ^Mesh, allocator := context.temp_allocator) {
 			color  = m.vertices.color[i],
 		}
 	}
-	
+
 	// Upload vertices
 	m.vbuf = sg.make_buffer(
 		{
@@ -1203,7 +1282,7 @@ upload_mesh :: proc(m: ^Mesh, allocator := context.temp_allocator) {
 			usage = {vertex_buffer = true, immutable = true},
 		},
 	)
-	
+
 	// Upload indices
 	m.ibuf = sg.make_buffer(
 		{