Flux texture pass: CF-generated spaceship interior textures wired in

5 textures from Cloudflare Flux (free tier) in assets/textures: wall
panels, deck floor, bulkhead, blast door face, machinery greeble.
Runtime mirror-fold makes them seamless; triplanar surface materials
with grid fallback when files missing. Doors use the cropped face.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-08-03 20:24:39 +10:00
parent 5f67b3c6b0
commit fec7f419f8
17 changed files with 382 additions and 32 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bvhxix1d6ka2i"
path="res://.godot/imported/bulkhead.jpg-778442090b553bb4d930d2a7bb2227d2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/bulkhead.jpg"
dest_files=["res://.godot/imported/bulkhead.jpg-778442090b553bb4d930d2a7bb2227d2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/textures/door.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://3ekppwafyxd1"
path="res://.godot/imported/door.jpg-b8c4b8bf4ee1820b8867f07c9c81a77c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/door.jpg"
dest_files=["res://.godot/imported/door.jpg-b8c4b8bf4ee1820b8867f07c9c81a77c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://eg5m5mny4ux4"
path="res://.godot/imported/door_face.jpg-b63d50391bbbef15207e9296b1e81a7c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/door_face.jpg"
dest_files=["res://.godot/imported/door_face.jpg-b63d50391bbbef15207e9296b1e81a7c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b4pyrvfdghrrl"
path="res://.godot/imported/door_src.jpg-2b798f391ba5ad155c846627d24b7f25.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/door_src.jpg"
dest_files=["res://.godot/imported/door_src.jpg-2b798f391ba5ad155c846627d24b7f25.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/textures/floor.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://tktukrgw3bot"
path="res://.godot/imported/floor.jpg-1a2c086730eaa37e4b6f8081270af11c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/floor.jpg"
dest_files=["res://.godot/imported/floor.jpg-1a2c086730eaa37e4b6f8081270af11c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/textures/greeble.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ceijjpvl5xo4r"
path="res://.godot/imported/greeble.jpg-55b0e1aa001c0f3b89fdb2c9b1188b95.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/greeble.jpg"
dest_files=["res://.godot/imported/greeble.jpg-55b0e1aa001c0f3b89fdb2c9b1188b95.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/textures/wall.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://duqv3irxdi3v4"
path="res://.godot/imported/wall.jpg-8e8b3ad92a15e41a3c6423a65a989ffb.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/wall.jpg"
dest_files=["res://.godot/imported/wall.jpg-8e8b3ad92a15e41a3c6423a65a989ffb.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -7,6 +7,7 @@ class Door extends StaticBody3D:
var height := 3.0
var width := 2.4
var tint := Color(0.95, 0.5, 0.1)
var face_tex: Texture2D = null
var base_y := 0.0
var _tw: Tween
@ -18,10 +19,14 @@ class Door extends StaticBody3D:
mi.mesh = m
mi.position.y = height / 2.0
var mat := StandardMaterial3D.new()
mat.albedo_color = tint
mat.emission_enabled = true
mat.emission = tint
mat.emission_energy_multiplier = 0.3
if face_tex:
mat.albedo_color = Color.WHITE
mat.albedo_texture = face_tex
else:
mat.albedo_color = tint
mat.emission_enabled = true
mat.emission = tint
mat.emission_energy_multiplier = 0.3
mi.material_override = mat
add_child(mi)
var col := CollisionShape3D.new()

View File

@ -110,6 +110,50 @@ func mat(col: Color) -> StandardMaterial3D:
return _mats[col]
var _texs := {}
func seamless_tex(path: String) -> ImageTexture:
# mirror-fold 2x2 so any image tiles without seams
# ponytail: runtime load+fold; bake to imported textures if load time ever matters
if _texs.has(path):
return _texs[path]
var img := Image.load_from_file(ProjectSettings.globalize_path(path))
img.convert(Image.FORMAT_RGB8)
var w := img.get_width()
var h := img.get_height()
var out := Image.create(w * 2, h * 2, false, Image.FORMAT_RGB8)
var r := Rect2i(0, 0, w, h)
out.blit_rect(img, r, Vector2i(0, 0))
var fx: Image = img.duplicate()
fx.flip_x()
out.blit_rect(fx, r, Vector2i(w, 0))
var fy: Image = img.duplicate()
fy.flip_y()
out.blit_rect(fy, r, Vector2i(0, h))
var fxy: Image = fx.duplicate()
fxy.flip_y()
out.blit_rect(fxy, r, Vector2i(w, h))
out.generate_mipmaps()
_texs[path] = ImageTexture.create_from_image(out)
return _texs[path]
func surface_mat(file: String, tile_m: float, col := Color.WHITE) -> StandardMaterial3D:
var path := "res://assets/textures/" + file
if not FileAccess.file_exists(path):
return struct_mat(Color(0.5, 0.52, 0.58)) # fallback: grid grey-box
var key := "t" + file + str(tile_m)
if not _mats.has(key):
var m := StandardMaterial3D.new()
m.albedo_color = col
m.albedo_texture = seamless_tex(path)
m.uv1_triplanar = true
m.uv1_scale = Vector3.ONE / tile_m
_mats[key] = m
return _mats[key]
func struct_mat(col: Color) -> StandardMaterial3D:
var key := "g" + str(col)
if not _mats.has(key):
@ -134,12 +178,21 @@ func glow(col: Color, energy := 0.7) -> StandardMaterial3D:
return _mats[key]
func box(pos: Vector3, size: Vector3, col: Color, parent: Node3D = null, grid := false) -> CSGBox3D:
const SURF := {"wall": ["wall.jpg", 3.0], "floor": ["floor.jpg", 2.0],
"bulk": ["bulkhead.jpg", 3.0], "greeble": ["greeble.jpg", 2.2]}
func box(pos: Vector3, size: Vector3, col: Color, parent: Node3D = null, grid := false, surf := "") -> CSGBox3D:
var b := CSGBox3D.new()
b.size = size
b.position = pos
b.use_collision = true
b.material = struct_mat(col) if grid else mat(col)
if surf:
b.material = surface_mat(SURF[surf][0], SURF[surf][1])
elif grid:
b.material = struct_mat(col)
else:
b.material = mat(col)
(parent if parent else self).add_child(b)
return b
@ -157,13 +210,24 @@ func sign3d(text: String, pos: Vector3, rot_y := PI, size := 30, col := Color(0.
func divider(z: float) -> void:
var c := Color(0.45, 0.46, 0.52)
box(Vector3(-2.6, 2, z), Vector3(2.8, 4, 0.3), c, null, true)
box(Vector3(2.6, 2, z), Vector3(2.8, 4, 0.3), c, null, true)
box(Vector3(0, 3.5, z), Vector3(2.4, 1, 0.3), c, null, true)
box(Vector3(-2.6, 2, z), Vector3(2.8, 4, 0.3), c, null, true, "bulk")
box(Vector3(2.6, 2, z), Vector3(2.8, 4, 0.3), c, null, true, "bulk")
box(Vector3(0, 3.5, z), Vector3(2.4, 1, 0.3), c, null, true, "bulk")
func raw_tex(path: String) -> ImageTexture:
if _texs.has(path):
return _texs[path]
var img := Image.load_from_file(ProjectSettings.globalize_path(path))
img.generate_mipmaps()
_texs[path] = ImageTexture.create_from_image(img)
return _texs[path]
func make_door(z: float) -> Node3D:
var d := I.Door.new()
if FileAccess.file_exists("res://assets/textures/door_face.jpg"):
d.face_tex = raw_tex("res://assets/textures/door_face.jpg")
d.position = Vector3(0, 0, z)
add_child(d)
return d
@ -199,7 +263,7 @@ func _build_env() -> void:
var o := OmniLight3D.new()
o.position = Vector3(0, 3.4, z)
o.omni_range = 10.0
o.light_energy = 1.1
o.light_energy = 1.35
o.light_color = Color(1.0, 0.96, 0.88)
add_child(o)
for p in [Vector3(8, 6.5, 22), Vector3(6.4, 3.2, 25.8), Vector3(-5.0, 3.0, 24)]:
@ -213,31 +277,31 @@ func _build_env() -> void:
func _build_geometry() -> void:
var wall := Color(0.5, 0.52, 0.58)
# floor
box(Vector3(3, -0.15, 22.5), Vector3(18.6, 0.3, 46), Color(0.62, 0.62, 0.65), null, true)
box(Vector3(3, -0.15, 22.5), Vector3(18.6, 0.3, 46), Color(0.62, 0.62, 0.65), null, true, "floor")
# left wall x=-4.15, niche gap z 23.4..24.6
box(Vector3(-4.15, 2, 11.45), Vector3(0.3, 4, 23.9), wall, null, true)
box(Vector3(-4.15, 2, 33.55), Vector3(0.3, 4, 17.9), wall, null, true)
box(Vector3(-4.15, 2, 11.45), Vector3(0.3, 4, 23.9), wall, null, true, "wall")
box(Vector3(-4.15, 2, 33.55), Vector3(0.3, 4, 17.9), wall, null, true, "wall")
# relay niche (behind left wall) + bars
box(Vector3(-5.8, 2, 24), Vector3(0.3, 4, 1.6), wall, null, true)
box(Vector3(-5.0, 2, 23.4), Vector3(1.6, 4, 0.3), wall, null, true)
box(Vector3(-5.0, 2, 24.6), Vector3(1.6, 4, 0.3), wall, null, true)
box(Vector3(-5.8, 2, 24), Vector3(0.3, 4, 1.6), wall, null, true, "greeble")
box(Vector3(-5.0, 2, 23.4), Vector3(1.6, 4, 0.3), wall, null, true, "wall")
box(Vector3(-5.0, 2, 24.6), Vector3(1.6, 4, 0.3), wall, null, true, "wall")
box(Vector3(-4.15, 1, 23.8), Vector3(0.1, 2, 0.1), Color(0.15, 0.15, 0.15))
box(Vector3(-4.15, 1, 24.2), Vector3(0.1, 2, 0.1), Color(0.15, 0.15, 0.15))
# right wall x=4.15: gaps for drum bay (z 20..24) and bonus bay (z 25..26.6)
box(Vector3(4.15, 2, 9.75), Vector3(0.3, 4, 20.5), wall, null, true)
box(Vector3(4.15, 2, 24.5), Vector3(0.3, 4, 1.0), wall, null, true)
box(Vector3(4.15, 2, 34.55), Vector3(0.3, 4, 15.9), wall, null, true)
box(Vector3(4.15, 2, 9.75), Vector3(0.3, 4, 20.5), wall, null, true, "wall")
box(Vector3(4.15, 2, 24.5), Vector3(0.3, 4, 1.0), wall, null, true, "wall")
box(Vector3(4.15, 2, 34.55), Vector3(0.3, 4, 15.9), wall, null, true, "wall")
# bonus bay shell
box(Vector3(8.65, 2, 25.8), Vector3(0.3, 4, 3.0), wall, null, true)
box(Vector3(6.4, 2, 24.9), Vector3(4.4, 4, 0.3), wall, null, true)
box(Vector3(6.4, 2, 26.7), Vector3(4.4, 4, 0.3), wall, null, true)
box(Vector3(8.65, 2, 25.8), Vector3(0.3, 4, 3.0), wall, null, true, "greeble")
box(Vector3(6.4, 2, 24.9), Vector3(4.4, 4, 0.3), wall, null, true, "wall")
box(Vector3(6.4, 2, 26.7), Vector3(4.4, 4, 0.3), wall, null, true, "wall")
# drum bay outer shell
box(Vector3(12.15, 3.5, 22), Vector3(0.3, 8, 6), wall, null, true)
box(Vector3(8, 3.5, 19.3), Vector3(8, 8, 0.3), wall, null, true)
box(Vector3(8, 3.5, 24.7), Vector3(8, 8, 0.3), wall, null, true)
box(Vector3(12.15, 3.5, 22), Vector3(0.3, 8, 6), wall, null, true, "greeble")
box(Vector3(8, 3.5, 19.3), Vector3(8, 8, 0.3), wall, null, true, "greeble")
box(Vector3(8, 3.5, 24.7), Vector3(8, 8, 0.3), wall, null, true, "greeble")
# end walls
box(Vector3(0, 2, -0.5), Vector3(8.6, 4, 0.3), wall, null, true)
box(Vector3(0, 2, 44.5), Vector3(8.6, 4, 0.3), wall, null, true)
box(Vector3(0, 2, -0.5), Vector3(8.6, 4, 0.3), wall, null, true, "bulk")
box(Vector3(0, 2, 44.5), Vector3(8.6, 4, 0.3), wall, null, true, "bulk")
# dividers + doors
for z in [8.0, 16.0, 26.0, 34.0, 40.0]:
divider(z)
@ -298,11 +362,11 @@ func _build_objects() -> void:
drum.position = Vector3(8, 3.5, 22)
add_child(drum)
var dc := Color(0.42, 0.38, 0.3)
box(Vector3(0, -3.2, 0), Vector3(6.8, 0.4, 4.4), dc, drum)
box(Vector3(3.2, 0, 0), Vector3(0.4, 6.8, 4.4), dc, drum)
box(Vector3(-3.2, 0, 0), Vector3(0.4, 6.8, 4.4), dc, drum)
box(Vector3(0, 0, 2.2), Vector3(6.8, 6.8, 0.4), dc, drum)
box(Vector3(0, 0, -2.2), Vector3(6.8, 6.8, 0.4), dc, drum)
box(Vector3(0, -3.2, 0), Vector3(6.8, 0.4, 4.4), dc, drum, false, "bulk")
box(Vector3(3.2, 0, 0), Vector3(0.4, 6.8, 4.4), dc, drum, false, "bulk")
box(Vector3(-3.2, 0, 0), Vector3(0.4, 6.8, 4.4), dc, drum, false, "bulk")
box(Vector3(0, 0, 2.2), Vector3(6.8, 6.8, 0.4), dc, drum, false, "bulk")
box(Vector3(0, 0, -2.2), Vector3(6.8, 6.8, 0.4), dc, drum, false, "bulk")
box(Vector3(-2.6, 2.3, 0), Vector3(1.0, 0.15, 1.0), dc, drum) # shelf
var crate := I.Pickup.new()
crate.kind = "crate"

1
test/integration.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://dplolvpcgpla2