extends Node var _cache: Dictionary = {} func _make_image(w: int, h: int, fill: Color) -> Image: var img: Image = Image.create(w, h, false, Image.FORMAT_RGB8) img.fill(fill) return img func _to_texture(img: Image) -> ImageTexture: return ImageTexture.create_from_image(img) func _get_or_create(key: String, maker: Callable) -> ImageTexture: if _cache.has(key): return _cache[key] var tex: ImageTexture = maker.call() _cache[key] = tex return tex # Бежевые обои с растительным узором (мелкие точки) func wallpaper() -> ImageTexture: return _get_or_create("wallpaper", func() -> ImageTexture: var base: Color = Color(0.83, 0.72, 0.55) var dot: Color = Color(0.72, 0.6, 0.42) var darker: Color = Color(0.78, 0.68, 0.5) var img: Image = _make_image(64, 64, base) # мелкие крестики-цветы for y in range(0, 64, 8): for x in range(0, 64, 8): var ox: int = 4 if (y / 8) % 2 == 0 else 0 var px: int = (x + ox) % 64 img.set_pixel(px, y, dot) if px + 1 < 64: img.set_pixel(px + 1, y, darker) if y + 1 < 64: img.set_pixel(px, y + 1, darker) return _to_texture(img) ) # Линолеум жёлто-коричневый с горизонтальными полосами под паркет func linoleum() -> ImageTexture: return _get_or_create("linoleum", func() -> ImageTexture: var a: Color = Color(0.72, 0.6, 0.41) var b: Color = Color(0.65, 0.53, 0.36) var c: Color = Color(0.58, 0.46, 0.3) var img: Image = _make_image(64, 64, a) # полосы паркета for y in range(64): var stripe: int = y / 8 var col: Color = a if stripe % 2 == 0 else b for x in range(64): img.set_pixel(x, y, col) # швы между досками for y in range(0, 64, 8): for x in range(64): img.set_pixel(x, y, c) # вертикальные швы пэчворк for y in range(64): var stripe: int = y / 8 var seam_x: int = ((stripe * 23) % 32) + 16 img.set_pixel(seam_x, y, c) return _to_texture(img) ) # Потолок: светлая побелка с лёгким шумом func ceiling() -> ImageTexture: return _get_or_create("ceiling", func() -> ImageTexture: var base: Color = Color(0.92, 0.9, 0.85) var spot: Color = Color(0.88, 0.86, 0.82) var img: Image = _make_image(32, 32, base) var rng: RandomNumberGenerator = RandomNumberGenerator.new() rng.seed = 4242 for i in range(20): var x: int = rng.randi_range(0, 31) var y: int = rng.randi_range(0, 31) img.set_pixel(x, y, spot) return _to_texture(img) ) # Бордовый ковёр с персидским ромбовидным узором func wall_carpet() -> ImageTexture: return _get_or_create("wall_carpet", func() -> ImageTexture: var base: Color = Color(0.36, 0.12, 0.13) var pattern: Color = Color(0.78, 0.62, 0.2) var dark: Color = Color(0.22, 0.07, 0.08) var img: Image = _make_image(64, 64, base) # ромбы for cy in [16, 48]: for cx in [16, 48]: for r in range(6): var px1: int = cx - r var px2: int = cx + r var py1: int = cy - (6 - r) var py2: int = cy + (6 - r) if px1 >= 0 and py1 >= 0 and px1 < 64 and py1 < 64: img.set_pixel(px1, py1, pattern) if px2 < 64 and py1 >= 0: img.set_pixel(px2, py1, pattern) if px1 >= 0 and py2 < 64: img.set_pixel(px1, py2, pattern) if px2 < 64 and py2 < 64: img.set_pixel(px2, py2, pattern) # рамка for i in range(64): img.set_pixel(i, 0, dark) img.set_pixel(i, 63, dark) img.set_pixel(0, i, dark) img.set_pixel(63, i, dark) return _to_texture(img) ) # Тёмно-синее одеяло «звёзды» func blanket() -> ImageTexture: return _get_or_create("blanket", func() -> ImageTexture: var base: Color = Color(0.13, 0.16, 0.32) var star: Color = Color(0.85, 0.85, 0.75) var img: Image = _make_image(32, 32, base) var stars: Array = [[5,5],[14,3],[24,7],[8,12],[20,16],[3,21],[27,22],[15,26],[10,28]] for s in stars: img.set_pixel(s[0], s[1], star) if s[0] + 1 < 32: img.set_pixel(s[0] + 1, s[1], star) if s[0] - 1 >= 0: img.set_pixel(s[0] - 1, s[1], star) if s[1] + 1 < 32: img.set_pixel(s[0], s[1] + 1, star) if s[1] - 1 >= 0: img.set_pixel(s[0], s[1] - 1, star) return _to_texture(img) ) # CS:GO постер (жёлтый с силуэтом AWP) func poster_csgo() -> ImageTexture: return _get_or_create("poster_csgo", func() -> ImageTexture: var base: Color = Color(0.94, 0.71, 0.1) var dark: Color = Color(0.18, 0.13, 0.08) var img: Image = _make_image(32, 40, base) # рамка for i in range(32): img.set_pixel(i, 0, dark) img.set_pixel(i, 39, dark) for i in range(40): img.set_pixel(0, i, dark) img.set_pixel(31, i, dark) # буквы CS внизу (примитивно) for x in range(8, 12): img.set_pixel(x, 32, dark) img.set_pixel(x, 35, dark) img.set_pixel(8, 33, dark) img.set_pixel(8, 34, dark) for x in range(14, 18): img.set_pixel(x, 32, dark) img.set_pixel(x, 35, dark) img.set_pixel(14, 33, dark) img.set_pixel(17, 34, dark) # силуэт AWP (горизонталь сверху) for x in range(6, 26): img.set_pixel(x, 18, dark) img.set_pixel(x, 19, dark) for x in range(20, 26): img.set_pixel(x, 17, dark) img.set_pixel(7, 20, dark) img.set_pixel(8, 20, dark) return _to_texture(img) ) # Моргенштерн постер (тёмно-красный с белым контуром головы) func poster_morgen() -> ImageTexture: return _get_or_create("poster_morgen", func() -> ImageTexture: var base: Color = Color(0.7, 0.13, 0.13) var light: Color = Color(0.9, 0.85, 0.8) var dark: Color = Color(0.2, 0.05, 0.05) var img: Image = _make_image(32, 40, base) # рамка for i in range(32): img.set_pixel(i, 0, dark) img.set_pixel(i, 39, dark) for i in range(40): img.set_pixel(0, i, dark) img.set_pixel(31, i, dark) # силуэт головы по центру var cx: int = 16 var cy: int = 18 for r in range(7): var w: int = int(sqrt(49 - r * r)) for dx in range(-w, w + 1): if cy - r >= 0 and cy - r < 40 and cx + dx >= 0 and cx + dx < 32: img.set_pixel(cx + dx, cy - r, light) if cy + r < 40 and cx + dx >= 0 and cx + dx < 32: img.set_pixel(cx + dx, cy + r, light) return _to_texture(img) ) # Двери: деревянная текстура с волокном func door() -> ImageTexture: return _get_or_create("door", func() -> ImageTexture: var a: Color = Color(0.36, 0.23, 0.12) var b: Color = Color(0.45, 0.31, 0.18) var c: Color = Color(0.28, 0.18, 0.09) var img: Image = _make_image(32, 64, a) for y in range(64): for x in range(32): var v: float = sin(float(x) * 0.5 + float(y) * 0.1) * 0.5 + 0.5 if v > 0.6: img.set_pixel(x, y, b) elif v < 0.3: img.set_pixel(x, y, c) return _to_texture(img) ) # Стол MICKE: белый с малозаметной шумной текстурой func desk_white() -> ImageTexture: return _get_or_create("desk_white", func() -> ImageTexture: var base: Color = Color(0.96, 0.95, 0.93) var nick: Color = Color(0.88, 0.86, 0.82) var img: Image = _make_image(32, 32, base) var rng: RandomNumberGenerator = RandomNumberGenerator.new() rng.seed = 1337 for i in range(8): var x: int = rng.randi_range(0, 31) var y: int = rng.randi_range(0, 31) img.set_pixel(x, y, nick) return _to_texture(img) ) # Книжный корешок (несколько цветов) func book_color(color_idx: int) -> ImageTexture: var key: String = "book_%d" % color_idx return _get_or_create(key, func() -> ImageTexture: var palette: Array[Color] = [ Color(0.36, 0.18, 0.15), Color(0.2, 0.35, 0.5), Color(0.13, 0.13, 0.15), Color(0.55, 0.45, 0.25), Color(0.3, 0.5, 0.3), ] var base: Color = palette[color_idx % palette.size()] var trim: Color = Color(base.r * 0.6, base.g * 0.6, base.b * 0.6) var img: Image = _make_image(16, 32, base) # узкие линии-трим сверху и снизу for x in range(16): img.set_pixel(x, 0, trim) img.set_pixel(x, 31, trim) img.set_pixel(x, 5, trim) img.set_pixel(x, 26, trim) return _to_texture(img) ) # Применить текстуру к материалу с заданным uv-scale func apply_to_material(mat: BaseMaterial3D, texture: ImageTexture, scale: Vector3 = Vector3.ONE) -> void: if mat == null: return mat.albedo_texture = texture mat.uv1_scale = scale mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST # пиксель-арт