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

View File

@ -110,6 +110,50 @@ func mat(col: Color) -> StandardMaterial3D:
return _mats[col] 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: func struct_mat(col: Color) -> StandardMaterial3D:
var key := "g" + str(col) var key := "g" + str(col)
if not _mats.has(key): if not _mats.has(key):
@ -134,12 +178,21 @@ func glow(col: Color, energy := 0.7) -> StandardMaterial3D:
return _mats[key] 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() var b := CSGBox3D.new()
b.size = size b.size = size
b.position = pos b.position = pos
b.use_collision = true 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) (parent if parent else self).add_child(b)
return 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: func divider(z: float) -> void:
var c := Color(0.45, 0.46, 0.52) 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, "bulk")
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, "bulk")
box(Vector3(0, 3.5, z), Vector3(2.4, 1, 0.3), c, null, true) 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: func make_door(z: float) -> Node3D:
var d := I.Door.new() 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) d.position = Vector3(0, 0, z)
add_child(d) add_child(d)
return d return d
@ -199,7 +263,7 @@ func _build_env() -> void:
var o := OmniLight3D.new() var o := OmniLight3D.new()
o.position = Vector3(0, 3.4, z) o.position = Vector3(0, 3.4, z)
o.omni_range = 10.0 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) o.light_color = Color(1.0, 0.96, 0.88)
add_child(o) add_child(o)
for p in [Vector3(8, 6.5, 22), Vector3(6.4, 3.2, 25.8), Vector3(-5.0, 3.0, 24)]: 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: func _build_geometry() -> void:
var wall := Color(0.5, 0.52, 0.58) var wall := Color(0.5, 0.52, 0.58)
# floor # 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 # 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, 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) box(Vector3(-4.15, 2, 33.55), Vector3(0.3, 4, 17.9), wall, null, true, "wall")
# relay niche (behind left wall) + bars # relay niche (behind left wall) + bars
box(Vector3(-5.8, 2, 24), Vector3(0.3, 4, 1.6), 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) 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) 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, 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)) 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) # 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, 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) 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) box(Vector3(4.15, 2, 34.55), Vector3(0.3, 4, 15.9), wall, null, true, "wall")
# bonus bay shell # bonus bay shell
box(Vector3(8.65, 2, 25.8), Vector3(0.3, 4, 3.0), 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) 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) box(Vector3(6.4, 2, 26.7), Vector3(4.4, 4, 0.3), wall, null, true, "wall")
# drum bay outer shell # drum bay outer shell
box(Vector3(12.15, 3.5, 22), Vector3(0.3, 8, 6), 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) 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) box(Vector3(8, 3.5, 24.7), Vector3(8, 8, 0.3), wall, null, true, "greeble")
# end walls # end walls
box(Vector3(0, 2, -0.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) box(Vector3(0, 2, 44.5), Vector3(8.6, 4, 0.3), wall, null, true, "bulk")
# dividers + doors # dividers + doors
for z in [8.0, 16.0, 26.0, 34.0, 40.0]: for z in [8.0, 16.0, 26.0, 34.0, 40.0]:
divider(z) divider(z)
@ -298,11 +362,11 @@ func _build_objects() -> void:
drum.position = Vector3(8, 3.5, 22) drum.position = Vector3(8, 3.5, 22)
add_child(drum) add_child(drum)
var dc := Color(0.42, 0.38, 0.3) 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(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) 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) 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) 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) 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 box(Vector3(-2.6, 2.3, 0), Vector3(1.0, 0.15, 1.0), dc, drum) # shelf
var crate := I.Pickup.new() var crate := I.Pickup.new()
crate.kind = "crate" crate.kind = "crate"

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

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