# Components de Forma

Cenas tridimensionais em Decentraland são baseadas no [Entity-Component](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system) modelo, onde tudo em uma cena é uma *entity*, e cada entity pode incluir *components* que moldam suas características e funcionalidade.

A forma renderizada de uma entity é determinada por qual component ela usa.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-417a02dcf0fd40f5032551a0c6996becac7e7166%2Fecs-simple-components-new.png?alt=media)

## Use o Scene Editor em Creator Hub

A maneira mais fácil de dar forma a uma entity é usar o Scene Editor. Você pode adicionar um **Mesh Renderer** component para fornecer uma forma primitiva, ou um **GLTF** component para referenciar um modelo 3D a partir de um arquivo. Veja [Add Components](https://github.com/decentraland/docs/blob/main/creator/sdk7/scene-editor/build/components.md#add-components).

## Formas primitivas

Várias formas básicas, frequentemente chamadas de *primitives*, podem ser adicionadas a uma entity dando à entity um `MeshRenderer` component.

As seguintes formas estão disponíveis. Várias formas incluem campos opcionais adicionais, específicos para essa forma.

* **box**:

  Use `MeshRenderer.setBox()`, passando a entity. Passe `uvs` como um campo adicional opcional, para mapear o alinhamento da textura. Veja [materials](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/materials.md) para mais detalhes.
* **plane**:

  Use `MeshRenderer.setPlane()`, passando a entity. Passe `uvs` como um campo adicional opcional, para mapear o alinhamento da textura. Veja [materials](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/materials.md) para mais detalhes.
* **sphere**:

  Use `MeshRenderer.setSphere()`, passando a entity.
* **cylinder**:

  Use `MeshRenderer.setCylinder()`, passando a entity. Passe `radiusTop` e `radiusBottom` como campos opcionais adicionais, para modificar o cilindro.

  DICA: Defina ou `radiusTop` ou `radiusBottom` para 0 para criar um cone.

O exemplo a seguir cria um cubo:

```ts
const myCube = engine.addEntity()

Transform.create(myCube, {
	position: Vector3.create(8, 1, 8),
})

MeshRenderer.setBox(myCube)
```

O exemplo a seguir cria um cilindro com um `radiusTop` de 0, o que produz um cone:

```ts
const myCone = engine.addEntity()

Transform.create(myCone, {
	position: Vector3.create(8, 1, 8),
})

MeshRenderer.setCylinder(myCone, 0, 1)
```

Formas primitivas não incluem materials. Para dar cor ou textura, você deve atribuir um [material component](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/materials.md) à mesma entity.

Para tornar uma primitiva clicável, ou para impedir que jogadores caminhem através dela, você deve dar à entity um *collider* via um [MeshCollider](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/colliders.md) component.

Para alterar a forma de uma entity que já tem um `MeshRenderer` component, execute `MeshRenderer.setBox()` ou qualquer uma das outras funções auxiliares e isso sobrescreverá a forma original. Não há necessidade de remover o original `MeshRenderer` ou de usar a sintaxe avançada.

```ts
const myCube = engine.addEntity()

Transform.create(myCube, {
	position: Vector3.create(8, 1, 8),
})

MeshRenderer.setBox(myCube)

// overwrite shape
MeshRenderer.setSphere(myCube)
```

{% hint style="warning" %}
**📔 Nota**: O `MeshRenderer` component deve ser importado via

> `import { MeshRenderer } from "@dcl/sdk/ecs"`

Veja [Imports](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/getting-started/coding-scenes.md#imports) para como lidar com isso facilmente.
{% endhint %}

## Modelos 3D

Para formas mais complexas, você pode construir um modelo 3D em uma ferramenta externa como Blender e então importá-los em *.glTF* ou *.glb* (binário *.glTF*). [glTF](https://www.khronos.org/gltf) (GL Transmission Format) é um projeto aberto do Khronos que fornece um formato comum e extensível para ativos 3D que é eficiente e altamente interoperável com tecnologias web modernas.

Para adicionar um modelo externo a uma cena, adicione um `GltfContainer` component a uma entity e defina seu `src` para o caminho do arquivo glTF contendo o modelo.

```ts
const houseEntity = engine.addEntity()

GltfContainer.create(houseEntity, {
	src: 'models/House.gltf',
})
```

O `src` campo é obrigatório, você deve dar-lhe um valor ao construir o component. No exemplo acima, o modelo está localizado em uma `models` pasta no nível raiz da pasta do projeto da cena.

{% hint style="info" %}
**💡 Dica**: Recomendamos manter seus modelos separados na `assets/scene/models` pasta dentro da sua cena.
{% endhint %}

Modelos glTF podem incluir suas próprias textures, materials, colliders e animations embutidos. Veja [Modelos 3D](https://github.com/decentraland/docs-creator/blob/main/creator/3d-modeling/3d-models/README.md) para mais informações sobre isso. Para sobrescrever os materials de um modelo, use o [GltfNodeModifiers](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/materials.md#modify-gltf-materials) component. Veja [Modify glTF materials](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/materials.md#modify-gltf-materials) para mais detalhes.

Para impedir que jogadores atravessem um modelo 3D, ou para tornar um modelo clicável, você deve ter um [collider](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/colliders.md), que pode estar embutido no modelo ou fornecido via um `MeshCollider` component.

Tenha em mente que todos os modelos, seus shaders e suas textures devem estar dentro dos parâmetros das [scene limitations](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/optimizing/scene-limitations.md).

{% hint style="warning" %}
**📔 Nota**: O `GltfContainer` component deve ser importado via

`import { GltfContainer } from "@dcl/sdk/ecs"`

Veja [Imports](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/getting-started/coding-scenes.md#imports) para como lidar com isso facilmente.
{% endhint %}

### Pré-carregando um modelo 3D

Em alguns casos, um modelo 3D personalizado é adicionado à cena mas não é usado imediatamente. Por exemplo, um modelo de cadeira personalizada pode ser carregado apenas quando o jogador interage com outro objeto. Nesse cenário, a primeira interação pode levar algum tempo até que o download e o carregamento do modelo terminem.

Para evitar isso, use o `AssetLoad.create` método para garantir que o asset seja baixado antes de ser necessário.

```ts
import { AssetLoad } from "@dcl/sdk/ecs"

AssetLoad.create(engine.RootEntity, {
  assets: [
    "assets/scene/bundle1/explosion.glb",
  ],
})
```

Para mais informações, consulte a documentação [Pre Load Resources](https://github.com/decentraland/docs/blob/main/creator/sdk7/optimizing/pre-load-resources.md) .

### Bibliotecas gratuitas de modelos 3D

Em vez de construir seus próprios modelos 3D, você também pode baixá-los de várias bibliotecas gratuitas ou pagas.

Para ajudá-lo a começar, abaixo está uma lista de bibliotecas que têm conteúdo gratuito ou relativamente barato:

* [IWB Catalog](https://dcl-iwb.co/)
* [Asset Ovi](https://assetovi.com/)
* [Assets from the Builder](https://github.com/decentraland/builder-assets/tree/master/assets)
* [SketchFab](https://sketchfab.com/)
* [Clara.io](https://clara.io/)
* [Archive3D](https://archive3d.net/)
* [SketchUp 3D Warehouse](https://3dwarehouse.sketchup.com/)
* [Thingiverse](https://www.thingiverse.com/) (modelos 3D feitos principalmente para impressão 3D, mas adaptáveis a Virtual Worlds)
* [ShareCG](https://www.sharecg.com/)
* [CGTrader](https://www.cgtrader.com/)

{% hint style="warning" %}
**📔 Nota**: Preste atenção às restrições de licença que o conteúdo que você baixar possui.
{% endhint %}

Observe que em vários desses sites, você pode escolher em qual formato baixar o modelo. Sempre escolha *.glTF* format se disponível. Se não estiver disponível, você deve convertê-los para *glTF* antes de poder usá-los em uma cena. Para isso, recomendamos importá-los para o Blender e exportar como *.glTF* a partir daí.

### Otimizar modelos 3D

Para garantir que modelos 3D em sua cena carreguem mais rápido e ocupem menos memória, siga estas melhores práticas:

* Salve seus modelos em *.glb* format, que é uma versão mais leve de *.gltf*.
* Se você tiver vários modelos que compartilham as mesmas textures, exporte seus modelos com textures em um arquivo separado. Dessa forma vários modelos podem referir-se a um único arquivo de texture que só precisa ser carregado uma vez.
* Se sua cena tem entities que aparecem e desaparecem, pode ser uma boa ideia agrupar essas entities e mantê-las no subsolo, ou com escala 0. Isso ajudará elas a aparecerem mais rápido, a troca-off é que elas ocuparão memória quando não estiverem em uso. Veja [entities and components](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/architecture/entities-components.md#pooling-entities-and-components)

## Esticar uma forma

Formas primitivas e modelos 3D têm dimensões padrão que você pode alterar mudando a escala no `Transform` component.

```ts
const primitiveEntity = engine.addEntity()

MeshRenderer.setBox(primitiveEntity)

Transform.create(primitiveEntity, {
	position: { x: 8, y: 1, z: 8 },
	scale: { x: 4, y: 0.5, z: 4 },
})
```

## Tornar invisível

Você pode tornar uma entity invisível dando a uma entity um `VisibilityComponent`, com sua `visible` propriedade definida como *false*.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 0, 4),
})
MeshRenderer.setBox(myEntity)

VisibilityComponent.create(myEntity, { visible: false })
```

O `VisibilityComponent` funciona da mesma forma para entities com formas primitivas e com `GLTFContainer` components.

Se uma entity estiver invisível, seu collider pode bloquear o caminho do jogador e/ou prevenir cliques em entities que estão atrás dela, dependendo das camadas de colisão atribuídas ao collider.

### Propagar visibilidade

Você pode usar o campo `propagateToChildren` no `VisibilityComponent` para aplicar uma configuração a cada child na árvore de children da entity. Se `propagateToChildren` estiver definido como *true*, essas configurações afetam todos os children em todos os níveis abaixo. Isso pode poupar muito trabalho tedioso marcando cada child entity como invisível ou visível também.

```ts
// Parent entity (explicitamente invisible)
const parentEntity = engine.addEntity()
Transform.create(parentEntity, {
	position: Vector3.create(4, 0, 4),
})
MeshRenderer.setBox(parentEntity)
VisibilityComponent.create(parentEntity, { visible: false, propagateToChildren: true })

// Child entity (implicitly invisible because of parent)
const child = engine.addEntity()
Transform.create(child, {
	position: Vector3.create(0, 1, 0),
	parent: parentEntity
})
MeshRenderer.setBox(child)
```

{% hint style="warning" %}
**📔 Nota**: Se uma entity tem seu próprio `VisibilityComponent`, isso substitui qualquer configuração dos pais.

Se uma entity não tem seu próprio `VisibilityComponent`, então sua visibilidade é determinada pelo seu parente mais próximo com um `VisibilityComponent` e `propagateToChildren` definido como *true*.
{% endhint %}

## Estado de carregamento

Se um modelo 3D for bastante grande, pode levar algum tempo perceptível para ser renderizado, esse tempo pode variar dependendo do hardware do jogador e muitos outros fatores. Às vezes você precisa garantir que um modelo terminou de carregar antes de executar outra ação. Por exemplo, se você quiser teleportar o jogador para uma plataforma no céu, você precisa primeiro garantir que a plataforma esteja totalmente renderizada antes de mover o jogador para lá, caso contrário o jogador pode cair através da plataforma.

Para verificar se um modelo 3D terminou de ser renderizado, verifique o `GltfContainerLoadingState` component da entity. Este component é destinado a ser apenas leitura, e existe em qualquer entity que também tenha um `GltfContainer`component.

Este component tem uma única propriedade chamada `currentState`, contendo um valor do `LoadingState` enum.

O exemplo a seguir usa um system para verificar periodicamente o estado de carregamento do modelo 3D de uma entity. Se o estado for `LoadingState.FINISHED`, você pode querer executar lógica customizada ali e encerrar a execução do system.

```ts
export function main() {
	const meshEntity = engine.addEntity()
	GltfContainer.create(meshEntity, { src: 'models/Monster.glb' })
	engine.addSystem((deltaTime) => {
		const loadingState = GltfContainerLoadingState.getOrNull(meshEntity)
		if (!loadingState) return
		switch (loadingState.currentState) {
			case LoadingState.LOADING:
				console.log('mesh is LOADING')
				break
			case LoadingState.FINISHED:
				console.log('mesh is FINISHED')
				// Perform custom logic
				break
			case LoadingState.FINISHED_WITH_ERROR:
				console.log('mesh is FINISHED BUT MAY HAVE PROBLEMS')
				break
			case LoadingState.UNKNOWN:
				console.log('mesh is in an UNKNOWN STATE')
				break
		}
	})
}
```

## Sintaxe avançada

A sintaxe completa para criar um `MeshRenderer` component, sem quaisquer auxiliares para simplificá-la, é assim:

```ts
MeshRenderer.setBox(myBox, {
	mesh: {
		$case: 'box',
		box: { uvs: [] },
	},
})

MeshRenderer.create(myPlane, {
	mesh: {
		$case: 'plane',
		plane: { uvs: [] },
	},
})

MeshRenderer.create(myShpere, {
	mesh: {
		$case: 'sphere',
		sphere: {},
	},
})

MeshRenderer.create(myCylinder, {
	mesh: {
		$case: 'cylinder',
		cylinder: {},
	},
})
```

É assim que o protocolo base interpreta components MeshRenderer. As funções auxiliares abstraem isso e expõem uma sintaxe mais amigável, mas nos bastidores elas produzem essa sintaxe.

O `$case` o campo permite especificar um dos tipos permitidos. Cada tipo suporta um conjunto diferente de parâmetros. No exemplo acima, o `box` type suporta um campo `uvs` .

Os valores suportados para `$case` são os seguintes:

* `box`
* `plane`
* `sphere`
* `cylinder`

Dependendo do valor de `$case`, é válido definir o objeto para a forma correspondente, passando quaisquer propriedades relevantes.

Para adicionar um `MeshRenderer` component a uma entity que potencialmente já tem uma instância desse component, use `MeshRenderer.createOrReplace()`. As funções auxiliares como `MeshRenderer.setBox()` lidam com a sobrescrição de instâncias existentes do component, mas executar `MeshRenderer.create()` em uma entity que já tem esse component retorna um erro.
