Consiste em um script em Python para visualizar recursos gráficos do cliente WYD em Full HD, ideal para modificação, conversão ou catálogo de conteúdo.
O script faz o seguinte:
Lê arquivos .wys e .wyt
Renderiza modelos 3D de arquivos .obj
Gera imagens 1920x1080 com fundo transparente
Não altera, converte ou sobrescreve os arquivos originais do jogo
Como usar:
1. Coloque os arquivos em uma pasta chamada assets
2. Rode o script render_fullhd.py
3. Os arquivos de saída serão gerados em outputs/
Requisitos:
pip install pillow numpy opencv-python trimesh pyrender imageio
import os
import struct
import numpy as np
from PIL import Image
import cv2
import trimesh
import pyrender
import imageio
# =================================================================================
# CONFIGURAÇÕES GERAIS
# =================================================================================
HEADER_BYTES_WYS = 128 # bytes a pular nos .wys/.wyt (ajuste se precisar)
PASTA_INPUT = "assets" # onde ficam os .wys/.wyt/.objbin
PASTA_OUTPUT = "outputs" # onde salvará as pré-visualizações
os.makedirs(PASTA_OUTPUT, exist_ok=True)
# =================================================================================
# 1) TEXTURAS: .wys / .wyt (wrapper de DDS/TGA)
# =================================================================================
def abrir_textura_wys(caminho: str) -> Image.Image:
"""
Lê o arquivo .wys/.wyt sem sobrescrever nada,
remove apenas o cabeçalho e devolve uma PIL.Image em RAM.
"""
with open(caminho, "rb") as f:
bruto = f.read()
dados_img = bruto[HEADER_BYTES_WYS:] # pula cabeçalho custom
# Detecta magic bytes simples (DDS = 'DDS ', TGA = 0x000002 or 0x000010)
if dados_img[:4] == b'DDS ':
# OpenCV lê DDS diretamente em BGR
img_bgr = cv2.imdecode(np.frombuffer(dados_img, np.uint8), cv2.IMREAD_UNCHANGED)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGBA)
pil_img = Image.fromarray(img_rgb)
else: # assumindo TGA
pil_img = Image.open(io.BytesIO(dados_img))
return pil_img
def exportar_fullhd_textura(img: Image.Image, nome_base: str):
"""
Faz upscale mantendo proporção e salva como PNG 1080 p.
"""
img_up = img.convert("RGBA")
# calcula escala mantendo aspecto
img_up.thumbnail((1920, 1080), Image.BICUBIC)
# opcional: coloca sobre canvas 1920×1080 centralizado
canvas = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
offset = ((1920 - img_up.width)//2, (1080 - img_up.height)//2)
canvas.paste(img_up, offset)
destino = os.path.join(PASTA_OUTPUT, f"{nome_base}_1080p.png")
canvas.save(destino)
print(f"[✓] Textura exportada → {destino}")
# =================================================================================
# 2) MODELOS 3D: “OBJ” binário
# - aqui criamos um parser mínimo de exemplo;
# você precisará ajustar offsets/formatos ao real .obj binário do WYD.
# =================================================================================
def ler_obj_bin(caminho: str) -> trimesh.Trimesh:
"""
Exemplo de leitura de .obj binário VERY simplificado:
[uint32 qtd_vertices][lista float32 xyz][uint32 qtd_faces][lista uint32 idx]
Ajuste ao layout real do seu arquivo.
"""
with open(caminho, "rb") as f:
data = f.read()
offset = 0
nv = struct.unpack_from("<I", data, offset)[0]; offset += 4
vertices = struct.unpack_from(f"<{nv*3}f", data, offset); offset += nv*12
vertices = np.array(vertices, dtype=np.float32).reshape((-1, 3))
nf = struct.unpack_from("<I", data, offset)[0]; offset += 4
faces = struct.unpack_from(f"<{nf*3}I", data, offset)
faces = np.array(faces, dtype=np.int64).reshape((-1, 3))
mesh = trimesh.Trimesh(vertices=vertices, faces=faces, process=False)
return mesh
def renderizar_modelo_fullhd(mesh: trimesh.Trimesh, nome_base: str):
"""
Renderiza o mesh em cena simples (luz + câmera) e salva snapshot 1080 p.
"""
scene = pyrender.Scene(bg_color=[0, 0, 0, 0])
m = pyrender.Mesh.from_trimesh(mesh, smooth=False)
scene.add(m)
# Luz e câmera
light = pyrender.DirectionalLight(color=np.ones(3), intensity=2.0)
scene.add(light, pose=np.eye(4))
camera = pyrender.PerspectiveCamera(yfov=np.pi/3.0)
scene.add(camera, pose=[[1,0,0,0],
[0,1,0,0],
[0,0,1,2],
[0,0,0,1]])
r = pyrender.OffscreenRenderer(1920, 1080)
color, _ = r.render(scene)
destino = os.path.join(PASTA_OUTPUT, f"{nome_base}_1080p.png")
imageio.imwrite(destino, color)
r.delete()
print(f"[✓] Modelo 3D exportado → {destino}")
# =================================================================================
# 3) LOOP PRINCIPAL – percorre a pasta e processa
# =================================================================================
def main():
for arquivo in os.listdir(PASTA_INPUT):
caminho = os.path.join(PASTA_INPUT, arquivo)
nome_base, ext = os.path.splitext(arquivo.lower())
try:
if ext in [".wys", ".wyt"]:
img = abrir_textura_wys(caminho)
exportar_fullhd_textura(img, nome_base)
elif ext in [".wyo", ".objbin"]: # ajuste às suas extensões
mesh = ler_obj_bin(caminho)
renderizar_modelo_fullhd(mesh, nome_base)
except Exception as e:
print(f"[!] Falhou em {arquivo}: {e}")
if __name__ == "__main__":
main()