Assets
A Turian utiliza um pipeline de importação: os arquivos que você joga na pasta assets/ de um projeto são fontes (sources). Na importação, cada fonte recebe um GUID estável (salvo em um arquivo sidecar <arquivo>.meta) e é compilado (cooked) em um artefato de runtime sob .cache/assets/. O jogo finalizado (shipped) lê esses artefatos de um arquivo .oap empacotado e nunca toca na pasta assets/ solta. As referências entre assets (as texturas de um material, a mesh de um MeshRenderer) são armazenadas como GUIDs, portanto sobrevivem a renomeações e movimentações.
Importando modelos (glTF / GLB / OBJ)
Colocar um .gltf, .glb ou .obj na pasta assets/ importa sua geometria. Para glTF/GLB, a Turian também lê os materiais e texturas do arquivo e gera assets do engine a partir deles — um único modelo é uma fonte que produz muitos assets:
- um
.materialpor material glTF, mapeando o modelo metallic-roughness do glTF no shader PBR nativo (base color, metallic, roughness, emissive + strength, normal scale, occlusion, alpha mode/cutoff, double-sided → cull); - um asset de textura por imagem referenciada.
Esses assets gerados são registrados no arquivo .meta do modelo e mantêm seus GUIDs em diferentes reimportações, para que as referências na cena permaneçam válidas. Extensões de material não suportadas geram avisos, mas nunca erros fatais.
A geometria é compilada para uma mesh binária canônica no momento da importação, para que o runtime a carregue por meio de um único caminho rápido e independente de formato — o parsing de OBJ/glTF/cgltf acontece apenas no editor, nunca no jogo finalizado.
Editando sub-assets gerados
Selecione um modelo importado no Asset Browser; o Inspector lista seus materiais/texturas gerados em Generated Assets. Clique em um material para abri-lo e ajustá-lo (por exemplo, sua cor base). As edições persistem entre as reimportações — o importador apenas (re)gera materiais ausentes. Um Reimport completo limpa o cache para regenerar tudo a partir da fonte.
Hierarquia de nós e geometria por mesh (glTF/GLB)
O grafo de cena de um arquivo glTF/GLB — nós nomeados, transformações pai/filho e qual mesh cada nó usa — também é importado, não apenas materiais e texturas. Junto com os materiais gerados, a Turian grava:
- um sub-asset de mesh por mesh glTF, com suas próprias submeshes e nome, mantido separado do artefato compilado do próprio modelo (que continua sendo o arquivo inteiro achatado em uma única mesh, inalterado, para um simples drop de um único objeto);
- um sub-asset de Hierarchy — um Prefab com um GameObject por nó glTF, nomeado e posicionado para corresponder à fonte, com cada nó portador de mesh vinculado à sua mesh e materiais gerados.
Encontre-o em Generated Assets ao lado dos materiais do modelo, e arraste-o para a Scene Viewport — ou clique com o botão direito nele e escolha Instantiate into Scene — para trazer toda a hierarquia nomeada de uma vez, exatamente como instanciar qualquer outro prefab.
Somente glTF/GLB carregam hierarquia de nós por enquanto; OBJ ainda é importado como uma única mesh achatada sem submeshes. Skinning/rigging também não é importado — os nós de junta (joint) de um arquivo rigado chegam como GameObjects comuns (sem animação).
Configurações de importação
Selecionar uma textura ou modelo mostra um painel Import Settings:
- Image — tipo de textura (default / normal map / sprite / UI / HDR), espaço de cor, mipmaps, compressão, filtro, wrap, tamanho máximo e (somente DDS) uma inversão do canal verde para normal maps na convenção DirectX.
- Model — importar materiais, importar animações, fator de escala.
- Font — tamanho padrão.
As configurações são armazenadas no .meta; clicar em Apply recompila (re-cooks) o asset.
Texturas externas vs embutidas
- Imagens externas (um glTF que aponta para arquivos
.png/.jpgirmãos) tornam-se assets de textura comuns com seu próprio.meta— assim você pode selecioná-los e substituí-los como qualquer outro asset. - Imagens embutidas (pedaços binários GLB ou URIs de dados base64) são extraídas em assets de textura somente em cache durante a importação.
De qualquer forma, o material gerado vincula cada mapa por GUID. Formatos comuns (PNG, JPEG, ...) são decodificados para RGBA8.
Formatos de textura
| Formato | Caminho |
|---|---|
| PNG / JPEG / BMP / TGA / WebP | Decodificado para RGBA8 (stb_image). |
KTX2 (.ktx2) |
Formatos de bloco de GPU carregados diretamente — passthrough de BCn, descompactação Zstandard e Basis Universal (ETC1S / UASTC) transcodificado para BC7. Os níveis de mipmap (mip levels) são preservados. |
DDS (.dds) |
FourCC legado (DXT1/DXT3/DXT5/ATI1/ATI2) e cabeçalhos estendidos DX10, copiados em blocos sem alteração — BC1/BC3/BC4/BC5/BC7, sem recodificação. Os níveis de mipmap são preservados; cubemaps/arrays não são suportados. |
O suporte a KTX2 reside em um módulo ktx2 independente e autônomo do engine (com um transcodificador Basis Universal embutido), integrado por trás de loadTexture para que os materiais e importadores permaneçam independentes de formato. O DDS é analisado e decodificado por
engine/assets/DdsLoader.zig, que reutiliza os tipos Format/Level do
módulo ktx2 para que ambos os contêineres produzam a mesma forma pronta
para a GPU.
Como o FourCC legado do DDS não possui um bit de sRGB, a configuração
Color Space do importador é fixada no momento da importação: um DDS
marcado como sRGB é reescrito para carregar um cabeçalho estendido DX10 com
o formato DXGI sRGB correspondente (atualizando o contêiner apenas quando
necessário); um normal map com Flip Green Channel habilitado tem seu
canal verde BC5 invertido sem perdas, bloco a bloco, para corresponder à
convenção Y do engine.
Fontes PNG/JPEG/etc. são sempre decodificadas para RGBA8 linear pelo
stb_image, então um Color Space sRGB é compilado (cooked) da mesma
forma: um pequeno envelope é prefixado aos bytes de origem no momento da
importação, que o loader remove antes de promover o formato de GPU da
textura decodificada. Linear não precisa de nenhuma compilação, já que já
corresponde ao padrão do decodificador.
Normal maps, metallic-roughness maps e occlusion maps referenciados por um
material glTF/FBX definem o Color Space da imagem irmã como Linear por
padrão (e, no caso do normal map, o Texture Type como Normal Map) na
primeira vez que essa imagem é importada — reimportações subsequentes nunca
sobrescrevem uma configuração que você já alterou manualmente.
Materiais PBR
Um .material é um pequeno asset JSON que referencia um shader por GUID e armazena valores para os parâmetros expostos por esse shader. O shader nativo PBR (Metallic-Roughness) expõe:
| Parâmetro | Tipo | Notas |
|---|---|---|
base_color |
color | tonalidade albedo (RGBA) |
metallic, roughness |
scalar | 0–1 |
emissive, emissive_strength |
color / scalar | |
normal_scale, occlusion_strength |
scalar | |
alpha_cutoff |
scalar | para transparência mascarada |
albedo_map, metallic_roughness_map, normal_map, emissive_map, occlusion_map |
texture | vinculado por GUID |
Como um material gerado é um asset normal, você pode editá-lo no Inspector ou substituir a mesh por um material diferente sem alterar o modelo. Quando você define a mesh de um MeshRenderer para um modelo e seu material ainda está vazio, a Turian o preenche com o material gerado principal do modelo de forma automática.
Você também pode criar materiais do zero através do Asset Browser, incluindo presets nativos (Default, Metal, Plastic, Emissive, Glass).
Meshes multi-material
Uma única mesh pode carregar muitas submeshes, cada uma marcada com um
slot de material. Um MeshRenderer mantém um material por slot — sua lista
Material no Inspector mostra uma linha por slot ("Material 0", "Material
1", …) — e cada submesh é desenhada com o material vinculado ao seu slot.
Slots, não submeshes, são a unidade: um modelo com milhares de submeshes
compartilhando um punhado de materiais precisa apenas desse punhado de
slots. Isso permite que cenas inteiras achatadas em uma única mesh (centenas
de materiais) sejam renderizadas por completo, cada superfície com seu
próprio material PBR.
Quando você define a mesh de um MeshRenderer para um modelo, a Turian
preenche automaticamente cada slot com o material gerado para ele; você pode
então substituir qualquer slot individualmente.
Assets de fonte
Colocar um .ttf ou .otf em assets/ o registra como um asset Font —
GUID, .meta, miniatura no asset browser e um painel Import Settings
(tamanho padrão) como qualquer outro asset.
Selecionar uma fonte mostra uma Preview ao vivo: o Studio registra os
bytes reais da fonte no dvui (uma vez por sessão, indexado pelo GUID) e
renderiza um texto de amostra real em alguns tamanhos, ao lado de uma linha
de comparação definida na fonte de corpo padrão do tema do dvui — para que
você possa avaliar uma tipografia antes de usá-la em qualquer lugar.
examples/basic-project/assets/fonts/ inclui três fontes licenciadas sob a
SIL OFL (Lora, Bebas Neue, Inconsolata — veja ATTRIBUTION.md ali) escolhidas
pelo contraste visual (serifada, condensada de destaque, monoespaçada) como
uma demonstração funcional.
Um nó de texto .uidoc pode referenciar um asset Font diretamente: defina
style.font para o GUID da fonte (e opcionalmente style.font_size, padrão
24) e ele prevalece sobre a busca por nome de tema de style.font_style. Os
bytes da fonte são resolvidos através do mesmo callback de origem indexado
por GUID que o renderer já usa para imagens, e então registrados no dvui uma
vez por sessão — compartilhados entre a viewport do Studio (overlay do modo
de edição, Play mode e a própria preview do editor .uidoc) e o jogo
finalizado igualmente, então nenhuma ramificação estilo #if EDITOR é
necessária em lugar algum. O título de
examples/basic-project/assets/ui.uidoc usa a Bebas Neue dessa forma como um
exemplo funcional.
Um asset Theme (o épico da issue #104) que define uma fonte para um documento inteiro de uma vez, em vez de por nó, está marcado como trabalho futuro, não bloqueante.
Exemplo
O exemplo 3d-model-materials mostra três objetos lado a lado: um cubo OBJ com o preset nativo Metal, a WaterBottle da Khronos como glTF (mapas .png externos) e a mesma garrafa como GLB (mapas embutidos extraídos na importação). É estruturado para escalar para cenas grandes como Sponza ou Bistro — coloque o modelo em assets/models/, abra o projeto e adicione-o a uma cena.
Consulte também a Referência de Componentes para o componente MeshRenderer e as Configurações do Projeto para a cena de inicialização.