init
This commit is contained in:
commit
e45f121fb9
89 changed files with 336069 additions and 0 deletions
199
demo/scripts/mesh_library_builder.gd
Normal file
199
demo/scripts/mesh_library_builder.gd
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
class_name MeshLibraryBuilder
|
||||
extends RefCounted
|
||||
|
||||
# Builds a MeshLibrary at runtime for the 3D dungeon demo. No art assets —
|
||||
# BoxMesh / PlaneMesh primitives with tinted StandardMaterial3D. Each tile
|
||||
# ID here matches a terrain enum the renderer cares about.
|
||||
|
||||
# Tile IDs (exposed as constants so demo_3d.gd can pass them to
|
||||
# GridMap.set_cell_item).
|
||||
const TILE_FLOOR := 0
|
||||
const TILE_WALL := 1
|
||||
const TILE_DOOR := 2
|
||||
const TILE_CORRIDOR := 3
|
||||
const TILE_WATER := 4
|
||||
const TILE_LAVA := 5
|
||||
const TILE_BRIMSTONE := 6
|
||||
const TILE_BRIDGE := 7
|
||||
const TILE_STAIRS_UP := 8
|
||||
const TILE_STAIRS_DOWN := 9
|
||||
|
||||
# Floors and liquid surfaces are thin slabs centered at the cell origin.
|
||||
# Walls are tall enough that the player (camera ~2.6u) can't see over them.
|
||||
# Doors are chest-height so they read as entry markers without blocking view
|
||||
# along corridors — can raise to full height once we have swinging doors.
|
||||
const CELL_SIZE := 1.0
|
||||
const WALL_HEIGHT := 3.0
|
||||
const FLOOR_THICKNESS := 0.05
|
||||
const LIQUID_THICKNESS := 0.05
|
||||
const DOOR_HEIGHT := 0.8
|
||||
const BRIDGE_THICKNESS := 0.1
|
||||
const STAIRS_HEIGHT := 0.7
|
||||
|
||||
static func build(with_collision: bool = true) -> MeshLibrary:
|
||||
var lib := MeshLibrary.new()
|
||||
_add_slab(lib, TILE_FLOOR, "Floor", _mat(Color(0.64, 0.54, 0.42)), FLOOR_THICKNESS, 0.0)
|
||||
_add_cube(lib, TILE_WALL, "Wall", _mat(Color(0.28, 0.28, 0.32)), WALL_HEIGHT, WALL_HEIGHT * 0.5)
|
||||
_add_cube(lib, TILE_DOOR, "Door", _mat(Color(0.72, 0.36, 0.20)), DOOR_HEIGHT, DOOR_HEIGHT * 0.5)
|
||||
_add_slab(lib, TILE_CORRIDOR, "Corridor", _mat(Color(0.44, 0.37, 0.29)), FLOOR_THICKNESS, 0.0)
|
||||
_add_slab(lib, TILE_WATER, "Water", _mat_trans(Color(0.18, 0.42, 0.78, 0.75)), LIQUID_THICKNESS, -0.05)
|
||||
_add_slab(lib, TILE_LAVA, "Lava", _mat_emissive(Color(0.90, 0.30, 0.12), Color(1.00, 0.45, 0.15), 1.5), LIQUID_THICKNESS, -0.05)
|
||||
_add_slab(lib, TILE_BRIMSTONE, "Brimstone", _mat_emissive(Color(0.72, 0.50, 0.22), Color(0.95, 0.65, 0.25), 0.8), LIQUID_THICKNESS, -0.05)
|
||||
_add_slab(lib, TILE_BRIDGE, "Bridge", _mat(Color(0.55, 0.38, 0.22)), BRIDGE_THICKNESS, 0.0)
|
||||
_add_ramp(lib, TILE_STAIRS_UP, "StairsUp", _mat(Color(0.74, 0.88, 0.94)), true)
|
||||
_add_ramp(lib, TILE_STAIRS_DOWN, "StairsDown", _mat(Color(0.20, 0.44, 0.80)), false)
|
||||
|
||||
if with_collision:
|
||||
_attach_collision(lib)
|
||||
return lib
|
||||
|
||||
# Bake the library to a .tres so the CLI and runtime can share the same asset
|
||||
# instead of rebuilding it each time a scene loads.
|
||||
static func save_resource(path: String, with_collision: bool = true) -> Error:
|
||||
return ResourceSaver.save(build(with_collision), path)
|
||||
|
||||
# --- collision shapes ---
|
||||
|
||||
# Every tile except chasm (which has no tile at all) gets a box collider
|
||||
# sized to match its mesh. Chasms therefore let the player fall through
|
||||
# cleanly onto the level below. Stairs are treated as solid blocks; walking
|
||||
# onto them works well enough with gravity + move_and_slide.
|
||||
static func _attach_collision(lib: MeshLibrary) -> void:
|
||||
# Shape, y_offset pairs, in the same geometry as the meshes above.
|
||||
var floor_shape := _box_shape(Vector3(CELL_SIZE, FLOOR_THICKNESS, CELL_SIZE))
|
||||
var wall_shape := _box_shape(Vector3(CELL_SIZE, WALL_HEIGHT, CELL_SIZE))
|
||||
var door_shape := _box_shape(Vector3(CELL_SIZE, DOOR_HEIGHT, CELL_SIZE))
|
||||
var liquid_shape := _box_shape(Vector3(CELL_SIZE, LIQUID_THICKNESS, CELL_SIZE))
|
||||
var bridge_shape := _box_shape(Vector3(CELL_SIZE, BRIDGE_THICKNESS, CELL_SIZE))
|
||||
var stairs_shape := _box_shape(Vector3(CELL_SIZE, STAIRS_HEIGHT, CELL_SIZE))
|
||||
|
||||
_set_shape(lib, TILE_FLOOR, floor_shape, FLOOR_THICKNESS * 0.5)
|
||||
_set_shape(lib, TILE_WALL, wall_shape, WALL_HEIGHT * 0.5)
|
||||
_set_shape(lib, TILE_DOOR, door_shape, DOOR_HEIGHT * 0.5)
|
||||
_set_shape(lib, TILE_CORRIDOR, floor_shape, FLOOR_THICKNESS * 0.5)
|
||||
_set_shape(lib, TILE_WATER, liquid_shape, -0.05 + LIQUID_THICKNESS * 0.5)
|
||||
_set_shape(lib, TILE_LAVA, liquid_shape, -0.05 + LIQUID_THICKNESS * 0.5)
|
||||
_set_shape(lib, TILE_BRIMSTONE, liquid_shape, -0.05 + LIQUID_THICKNESS * 0.5)
|
||||
_set_shape(lib, TILE_BRIDGE, bridge_shape, BRIDGE_THICKNESS * 0.5)
|
||||
_set_shape(lib, TILE_STAIRS_UP, stairs_shape, STAIRS_HEIGHT * 0.5)
|
||||
_set_shape(lib, TILE_STAIRS_DOWN, stairs_shape, STAIRS_HEIGHT * 0.5)
|
||||
|
||||
static func _box_shape(size: Vector3) -> BoxShape3D:
|
||||
var b := BoxShape3D.new()
|
||||
b.size = size
|
||||
return b
|
||||
|
||||
static func _set_shape(lib: MeshLibrary, id: int, shape: Shape3D, y: float) -> void:
|
||||
var t := Transform3D.IDENTITY
|
||||
t.origin = Vector3(0, y, 0)
|
||||
lib.set_item_shapes(id, [shape, t])
|
||||
|
||||
# --- material helpers ---
|
||||
|
||||
static func _mat(base: Color) -> StandardMaterial3D:
|
||||
var m := StandardMaterial3D.new()
|
||||
m.albedo_color = base
|
||||
m.roughness = 0.8
|
||||
m.metallic = 0.0
|
||||
return m
|
||||
|
||||
static func _mat_trans(base: Color) -> StandardMaterial3D:
|
||||
var m := _mat(base)
|
||||
m.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
return m
|
||||
|
||||
static func _mat_emissive(base: Color, emis: Color, strength: float) -> StandardMaterial3D:
|
||||
var m := _mat(base)
|
||||
m.emission_enabled = true
|
||||
m.emission = emis
|
||||
m.emission_energy_multiplier = strength
|
||||
return m
|
||||
|
||||
# --- mesh builders ---
|
||||
|
||||
# A flat slab filling the cell's XZ footprint, with the given thickness and
|
||||
# base at y_base (the slab extends UP from y_base by `thickness`).
|
||||
static func _add_slab(lib: MeshLibrary, id: int, name: String,
|
||||
mat: StandardMaterial3D,
|
||||
thickness: float, y_base: float) -> void:
|
||||
var mesh := BoxMesh.new()
|
||||
mesh.size = Vector3(CELL_SIZE, thickness, CELL_SIZE)
|
||||
mesh.material = mat
|
||||
var t := Transform3D.IDENTITY
|
||||
t.origin = Vector3(0, y_base + thickness * 0.5, 0)
|
||||
_install(lib, id, name, mesh, t)
|
||||
|
||||
# A cube centered on the cell. y_center is the Y of the cube's center.
|
||||
static func _add_cube(lib: MeshLibrary, id: int, name: String,
|
||||
mat: StandardMaterial3D,
|
||||
height: float, y_center: float) -> void:
|
||||
var mesh := BoxMesh.new()
|
||||
mesh.size = Vector3(CELL_SIZE, height, CELL_SIZE)
|
||||
mesh.material = mat
|
||||
var t := Transform3D.IDENTITY
|
||||
t.origin = Vector3(0, y_center, 0)
|
||||
_install(lib, id, name, mesh, t)
|
||||
|
||||
# Simple visual ramp using two cubes at different heights. Not geometrically
|
||||
# correct stairs — just enough to read as "stairs" from flight.
|
||||
# going_up=true means the tall step is at +Z; false means the tall step is
|
||||
# at -Z (visually going down).
|
||||
static func _add_ramp(lib: MeshLibrary, id: int, name: String,
|
||||
mat: StandardMaterial3D, going_up: bool) -> void:
|
||||
# GridMap.set_mesh expects a single Mesh. Fake the steps by stacking two
|
||||
# BoxMeshes into an ArrayMesh via SurfaceTool.
|
||||
var st := SurfaceTool.new()
|
||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||
st.set_material(mat)
|
||||
|
||||
var tall := STAIRS_HEIGHT
|
||||
var short := STAIRS_HEIGHT * 0.35
|
||||
var half := CELL_SIZE * 0.25
|
||||
|
||||
# Step 1: back (taller going up, shorter going down).
|
||||
var z_back := half if going_up else -half
|
||||
_append_box(st, Vector3(0, (tall if going_up else short) * 0.5, z_back),
|
||||
Vector3(CELL_SIZE, (tall if going_up else short), CELL_SIZE * 0.5))
|
||||
# Step 2: front.
|
||||
var z_front := -half if going_up else half
|
||||
_append_box(st, Vector3(0, (short if going_up else tall) * 0.5, z_front),
|
||||
Vector3(CELL_SIZE, (short if going_up else tall), CELL_SIZE * 0.5))
|
||||
|
||||
var arr := st.commit()
|
||||
_install(lib, id, name, arr, Transform3D.IDENTITY)
|
||||
|
||||
static func _append_box(st: SurfaceTool, center: Vector3, size: Vector3) -> void:
|
||||
var h := size * 0.5
|
||||
var c := center
|
||||
var v := [
|
||||
c + Vector3(-h.x, -h.y, -h.z), # 0
|
||||
c + Vector3( h.x, -h.y, -h.z), # 1
|
||||
c + Vector3( h.x, h.y, -h.z), # 2
|
||||
c + Vector3(-h.x, h.y, -h.z), # 3
|
||||
c + Vector3(-h.x, -h.y, h.z), # 4
|
||||
c + Vector3( h.x, -h.y, h.z), # 5
|
||||
c + Vector3( h.x, h.y, h.z), # 6
|
||||
c + Vector3(-h.x, h.y, h.z), # 7
|
||||
]
|
||||
# 6 faces, each 2 tris, wound CCW with outward normals.
|
||||
var faces := [
|
||||
[0, 1, 2, 3, Vector3(0, 0, -1)],
|
||||
[5, 4, 7, 6, Vector3(0, 0, 1)],
|
||||
[1, 5, 6, 2, Vector3( 1, 0, 0)],
|
||||
[4, 0, 3, 7, Vector3(-1, 0, 0)],
|
||||
[3, 2, 6, 7, Vector3(0, 1, 0)],
|
||||
[4, 5, 1, 0, Vector3(0, -1, 0)],
|
||||
]
|
||||
for f in faces:
|
||||
var n: Vector3 = f[4]
|
||||
for i in [0, 1, 2, 0, 2, 3]:
|
||||
st.set_normal(n)
|
||||
st.add_vertex(v[f[i]])
|
||||
|
||||
# Register a mesh + transform with the library under the given ID.
|
||||
static func _install(lib: MeshLibrary, id: int, name: String,
|
||||
mesh: Mesh, t: Transform3D) -> void:
|
||||
lib.create_item(id)
|
||||
lib.set_item_name(id, name)
|
||||
lib.set_item_mesh(id, mesh)
|
||||
lib.set_item_mesh_transform(id, t)
|
||||
Loading…
Add table
Add a link
Reference in a new issue