Objetivo: entender qué hace el motor con un modelo (y qué no hace), escribir los model del script, poner un objeto en la mano y en el suelo, y armar una prenda que se vea bien puesta.
1. Qué hace el motor con un modelo
Esto define cómo trabajas, así que va primero (verificado en los shaders del juego):
- Una sola textura difusa por modelo. El shader de objetos solo tiene la textura, un tinte y luces direccionales: no hay normal map, specular ni AO. Todo detalle fino tiene que estar pintado u horneado en la textura.
- Las normales por vértice sí importan: la luz se calcula por normal interpolada. Suaves en lo orgánico, duplicadas (normal plana) solo en aristas duras.
- Hay culling: si las caras quedan al revés, el modelo se ve "plano" o invisible por un lado.
- La cámara está lejos y arriba: un objeto ocupa pocos píxeles. Contraste fuerte y formas grandes ganan a detalle fino.
Presupuesto (contado en los .x del juego): una taza tiene ~164 triángulos, un martillo ~300, el cuerpo entero del personaje 1.838. Un objeto de mano razonable: 150–1.500.
2. Formatos
.x(DirectX, texto): el que más usan nuestros mods; es legible y se puede generar con Python. Estructura: cabecera de plantillas, unFramecon su matriz,Mesh,MeshNormals,MeshMaterialList,MeshTextureCoords(v = 0 arriba) y, si tiene huesos,SkinWeights..fbx: lo que exporta Blender; lo usa Catimbas para la ropa con huesos.- Ambos van en
media/models_X/. En los scripts la ruta se escribe sin extensión y relativa amodels_X/.
Abre un .x de Tocomples Food (models_X/WorldItems/PapaPletoHand.x, 7.148 líneas) y localiza cada bloque. Es un buen ejercicio para dejar de verlo como caja negra.
3. El model del script
Del CompletoItaliano.txt de Tocomples Food:
model CompletoItaliano ← el modelo EN LA MANO
{
mesh = WorldItems/CompletoItalianoHand, ← models_X/WorldItems/CompletoItalianoHand.x
texture = CompletoItaliano, ← textures/CompletoItaliano.png
scale = 1.0,
attachment Bip01_Prop1 ← cómo se agarra con la mano derecha
{
offset = 0.0057 0.0051 0.0246,
rotate = 172.7064 35.0347 178.1460,
scale = 0.7666,
}
attachment Bip01_Prop2 { ... mismo ... } ← mano izquierda
}
model CompletoItalianoGround ← el modelo EN EL SUELO
{
mesh = WorldItems/CompletoItaliano,
texture = CompletoItaliano,
scale = 0.75,
}Y en el ítem, con el módulo delante:
StaticModel = CompleteItaliano.CompletoItaliano,
WorldStaticModel = CompleteItaliano.CompletoItalianoGround,
primaryAnimMask = HoldingUmbrellaRight, ← pose de la mano al sostenerlo
secondaryAnimMask = HoldingUmbrellaLeft,Claves:
- Mano y suelo son dos modelos distintos: el de suelo apoya en
y = 0; el de mano tiene el punto de agarre en el origen. Bip01_Prop1/Bip01_Prop2son los huesos de las manos del esqueleto. Los valores deoffset/rotate/scalede arriba son los que usan todos nuestros objetos de mano (salen deBeerBottle.xdel juego): úsalos de base.- Sin
primaryAnimMaskel objeto queda en la mano pero el brazo no lo "sostiene".
Ejes y escala
Metros, tamaño real, Y arriba, Z adelante. El juego carga los modelos convirtiéndolos a mano izquierda y el importador de .x invierte Z: si un modelo sale espejado o con las caras al revés, el problema casi siempre es este.
4. Modelos con huesos (skinned): ropa
Un modelo que se dobla con el cuerpo lleva pesos que lo atan a los huesos del esqueleto (Bip01_*). Dos caminos que ya funcionaron en nuestros mods:
.x: tomarMaleBody.xdel juego, reemplazar solo la malla y conservar huesos y matrices (en nuestros mods lo hace un script propio de rigging).- FBX: partir de una plantilla con el esqueleto
Bip01, cambiar la malla, copiar pesos y exportar con la misma configuración (así lo hace el generador de modelos de Catimbas).
Regla de pesos: un objeto rígido (zapato, casco) sigue un solo hueso; mezcla solo en la articulación. Si copias los pesos de la piel, se estira como goma.
Las tres piezas de una prenda
1. Ítem (script) ClothingItem = Catimbas_Nike_Dunk_GreyFog, BodyLocation = base:shoes
2. Prenda visual (XML) media/clothing/clothingItems/Catimbas_Nike_Dunk_GreyFog.xml
3. Registro media/fileGuidTable.xml → ruta del XML ↔ GUIDEl XML real de una zapatilla de Catimbas:
<clothingItem>
<m_MaleModel>media\models_X\Skinned\Clothes\Catimbas\M_Catimbas_Low.fbx</m_MaleModel>
<m_FemaleModel>media\models_X\Skinned\Clothes\Catimbas\F_Catimbas_Low.fbx</m_FemaleModel>
<m_GUID>20669123-b8ac-5c8c-a1b3-38ad26e2e14e</m_GUID>
<m_Static>false</m_Static>
<m_Masks>8</m_Masks>
<m_Masks>10</m_Masks>
<m_MasksFolder>none</m_MasksFolder> ← la malla tiene UV propio (sección 5)
<textureChoices>clothes/catimbas/nike_dunk_greyfog</textureChoices>
</clothingItem>- El GUID no cambia nunca: si cambia, las partidas guardadas pierden la prenda. Genéralo determinístico desde el nombre (
uuid5), como Catimbas. textureChoicespara prendas con modelo;m_BaseTexturessolo para ropa que se pinta sobre el cuerpo (poleras).- Una malla, muchas texturas: las 89 zapatillas de Catimbas comparten pocas mallas y cambian solo la textura.
5. El error de los hoyos (m_MasksFolder)
El juego recorta cada prenda con máscaras dibujadas para el UV del cuerpo, para que no asome lo que queda debajo. Si tu malla tiene UV propio, los recortes caen al azar: la prenda se ve perfecta en el suelo y con "láminas que no cierran" puesta.
- Malla con UV propio →
<m_MasksFolder>none</m_MasksFolder>. - Malla del juego que usa el UV del cuerpo (pantalones, chaquetas) → no lo pongas.
Antes de encontrar esto se probó invertir caras y hacer la malla de doble cara: no era eso. Es el mejor ejemplo de por qué el módulo 15 insiste en probar puesto, en el juego.
6. Herramientas
No tienes que modelar a mano para practicar: se puede trabajar con generadores y un editor. Estos son los que usamos en nuestros mods:
| Herramienta | Para qué |
|---|---|
El generador de .x de Terremoto (write_x, check_winding, to_hand) | Generar .x desde Python y revisar que las caras miren afuera |
| Un modelador en el navegador (herramienta interna) | Retocar, extruir, simplificar y guardar .x |
Un lector propio de .x con huesos | Leer .x con huesos y animaciones fuera del juego |
bpy como librería (python3.11) | Blender sin interfaz: importar, pesar, exportar FBX, hornear |
Ejercicios
Ejercicio 5.1 · Leer un .x
Con PapaPletoHand.x de Tocomples Food: cuenta vértices y triángulos, encuentra el nombre de la textura, y di dónde está el origen respecto al objeto. Escribe un script de 30 líneas que lo haga por ti.
Listo cuando: tu script da los mismos números para tres modelos distintos que contar a mano uno de ellos.
Ejercicio 5.2 · Un objeto en la mano y en el suelo
Haz un modelo simple para la Sopaipilla (un disco con borde, 100–300 triángulos): versión de mano y versión de suelo, con su textura.
Listo cuando: se ve en el suelo apoyado (no flotando ni enterrado), y en la mano el personaje la sostiene bien en las dos manos.
Ejercicio 5.3 · Romper el modelo a propósito
- Invierte el orden de los vértices de todas las caras. ¿Qué ves?
- Pon
scale = 10en el modelo de suelo. - Quita
primaryAnimMask. - Escribe el
StaticModelsin módulo delante.
Anota síntoma → causa, como en el ejercicio 2.4.
Ejercicio 5.4 · Una prenda reusando una malla del juego
Haz CursoLab.GorroCurso: un gorro que reusa el modelo de un gorro del juego y solo cambia la textura (como Jockies, uno de nuestros mods). Necesitas ítem, XML con GUID determinístico y entrada en fileGuidTable.xml.
Listo cuando: te lo pones, se ve con tu textura, y sigue ahí después de guardar y cargar la partida.
Ejercicio 5.5 · El caso de los hoyos
Con una malla de UV propio (puedes tomar un zapato de Catimbas), quita <m_MasksFolder>none</m_MasksFolder> y pruébalo con pantalón y calcetines. Saca una captura del problema y otra arreglado.
Desafío 5
Genera desde Python un modelo de mano para un ítem de CursoLab con iluminación horneada en la textura (luz cenital calculada con las normales), y compáralo en el juego contra la misma textura sin hornear.
Anterior: 04 · Texturas · Siguiente: 06 · Lua en PZ (Kahlua)