Scene content

Development guide

Index icon See index
Search Icon

Scene content

Three dimensional scenes in Decentraland are based on the Entity-Component model, where everything in a scene is an entity, and each entity can include components that shape its characteristics and functionality.

Entities can be nested inside others to create a tree structure. In fact, all scenes must output a tree of nested entities. .xml scenes explicitly define this tree, .xlt scenes define typescript code that builds and updates this tree.

This document covers how to achieve common objectives by using different types of entities and components in a scene’s tree.

Create simple geometric shapes

Several basic shapes, often called primitives, can be added to a scene as predefined entity types. These already have certain components defined (like their shape) and let you set others (like their rotation and color).

The following types of entities are available:

  • <Box />
  • <Sphere />
  • <Plane />
  • <Cylinder />
  • <Cone />

Any of these can be added to your scene, they can all include basic components like position, scale or color.

<box position={vector} color="#ff00aa" scale={2} />

See Entity interfaces for more details on these types of entities.

Tip: When editing the code via a source code editor (like Visual Studio Code), you can see the list of components supported by a type of entity. Typically, this is done by placing the cursor in the entity and typing Ctrl + Space bar.

Text blocks

You can add text as an entity in a Decentraland scene. You can use these text entities as labels in your scene, display them momentarily as error messages, or whatever you wish.

<text
  value="Users will see this text floating in space in your scene."
  hAlign="left"
  position={{ x: 5, y: 1, z: 5 }}
/>

To display a value that isn’t of type string in a text entity, use the toString() function to convert its type to string.

<text value={this.state.gameScore.toString()} />

Color

Color is set in hexadecimal values. To set an entity’s color, simply set its color to the corresponding hexadecimal value.

<sphere position={{ x: 0.5, y: 1, z: 0 }} color="#EF2D5E" />

Tip: There are many online color-pickers you can use to find a specific color graphically. To name one, you can try the color picker on W 3 Schools.

Materials

Materials are defined as separate entities in a scene, this prevents material definitions from being duplicated when multiple entities use them, keeping the scene’s code lighter.

Materials can be applied to primitive entities and to planes, simply by setting the material.

<material
  id="reusable_material"
  albedoTexture="materials/wood.png"
  roughness={0.5}
/>
<sphere material="#reusable_material" />

In the example above, the image for the material is located in a materials folder, which is located at root level of the scene project folder.

When an entity uses a material, you must refer to it prepending a # to the material’s id. So if the material’s id is reusable_material, you must set the material on an entity to #reusable_material.

Materials are also implicitly imported into a scene when you import a glTF model that includes embedded materials. When that’s the case, you don’t need to declare a <material /> entity.

Not all shaders are supported by the Decentraland engine. For example, all blender render materials should be supported, in Cycles render only PBR (physically based rendering) materials are supported.

See entity interfaces for a full list of all the properties that can be configured in a material. Keep in mind that all materials and textures must be within the parameters of the scene limitations.

Texture mapping

An entity that uses a material can be configured to map specific regions of the texture to its faces. You do this by setting u and v coordinates on the 2D image of the texture to correspond to the vertices of the entity. The more vertices the entity has, the more uv coordinates need to be defined on the texture, a plane for example needs to have 8 uv points defined, 4 for each of its two faces.

async render() {
  return (
    <scene position={{ x: 5, y: 1.5, z: 5 }}>
      <basic-material
        id="sprite001"
        texture="materials/atlas.png"
        samplingMode={DCL.TextureSamplingMode.NEAREST}
      />
      <plane
        material="#sprite001"
        uvs={[
          0,
          0.75,
          0.25,
          0.75,
          0.25,
          1,
          0,
          1,
          0,
          0.75,
          0.25,
          0.75,
          0.25,
          1,
          0,
          1
        ]}
      />
    </scene>
  )
}

To create an animated sprite, use texture mapping to change the selected regions of a same texture that holds all the frames.

To work with animated sprites, you can install and use the Decentraland sprite helpers node package. Read documentation on how to use it in the provided link.

Transparent materials

To make a material transparent, you must add an alpha channel to the image you use for the texture. The material entity ignores the alpha channel of the texture image by default, so you must either:

  • Set hasAlpha to true.
  • Set an image in alphaTexture, which can be the same or a different image.
<material
  albedoTexture="materials/semiTransparentTexture.png"
  hasAlpha
/>
// or
<material
  albedoTexture="materials/semiTransparentTexture.png"
  alphaTexture="materials/semiTransparentTexture.png"
/>

Basic materials

Instead of the material entity, you can define a material through the basic-material entity. This creates materials that are shadeless and are not affected by light. This is useful for creating user interfaces that should be consistently bright, it can also be used to give your scene a more minimalist look.

<basic-material
  id="basic_material"
  texture="materials/profile_avatar.png"
/>
<sphere
  material="#basic_material"
/>

Materials are also implicitly imported into a scene when you import a glTF model that includes embedded materials. When that’s the case, the scene doesn’t need a material entity declared.

Import 3D models

For more complex shapes, you can build a 3D model in an external tool like Blender and then import them in glTF format. glTF (GL Transmission Format) is an open project by Khronos providing a common, extensible format for 3D assets that is both efficient and highly interoperable with modern web technologies.

To add an external model into a scene, add a gltf-model element and set its src to the path of the glTF file containing the model.

<gltf-model
  position={{ x: 5, y: 3, z: 5 }}
  scale={0.5}
  src="models/myModel.gltf"
/>

In the example above, the model is located in a models folder, which is located at root level of the scene project folder.

Tip: We recommend keeping your models separate in a /models folder inside your scene.

glTF models can also include their own textures, materials, colliders and animations. See 3D models considerations for more information on this.

Keep in mind that all models, their shaders and their textures must be within the parameters of the scene limitations.

Note: obj models are also supported as a legacy feature, but will likely not be supported for much longer. To add one, use an <obj-model> entity.

Animations

Files with .gltf extensions can be opened with a text editor to view their contents. There you can find the list of animations included in the model and how they’re named. Typically, an animation name is comprised of its armature name, an underscore and its animation name. For example myArmature_animation1.

See 3D models considerations for information on how to create animations for a 3D model before importing it to a Decentraland scene.

You handle animations to a gltf-model entity by adding skeletalAnimation settings to it. This setting receives an array of JSON entries, where each entry handles one of the animations in the model. For an animation to be activated, you must set the playing property of a clip to true.

The example below imports a model that includes animations and configures them.

<gltf-model
  position={{ x: 5, y: 3, z: 5 }}
  scale={0.5}
  src="models/shark_anim.gltf"
  skeletalAnimation={[
    {
      clip: "shark_bite",
      weight: 0.8,
      playing: false
    },
    {
      clip: "shark_swim",
      weight: 0.2,
      playing: true
    }
  ]}
/>

In this example, the armature is named shark and the two animations contained in it are named bite and swim.

An animation can be set to loop continuously by setting its loop property. If loop:false then the animation will be called only once when activated.

The weight property allows a single model to carry out multiple animations at once, calculating a weighted average of all the movements involved in the animation. The value of weight determines how much importance the animation will be given in the average.

The weight value of all active animations should add up to 1 at all times. If it adds up to less than 1, the weighted average will be referencing the default position of the armature for the remaining part of the calculation.

For example, in the code example above, if only shark_swim is active with a weight of 0.2, then the swimming movements are quite subtle, only 20% of what the animation says it should move. The other 80% of what’s averaged represents the default position of the armature.

The weight property can be used in interesting ways, for example the weight property of shark_swim could be set in proportion to how fast the shark is swimming, so you don’t need to create multiple animations for fast and slow swimming. You could also change the weight value gradually when starting and stopping the animation to give it a more natural transition and avoid jumps from one pose to another.

Free libraries for 3D models

Instead of building your own 3d models, you can also download them from several free or paid libraries.

To get you started, below is a list of libraries that have free or relatively inexpensive content:

Note: Pay attention to the licence restrictions that the content you download has.

Note that most of the models that you can download from these sites won’t be in glTF. If that’s the case, you must convert them to glTF before you can use them in a scene. We recommend importing them into Blender and exporting them with one of the available glTF export add-ons.

Sound

You can add sound to your scene by including a sound component in any entity.

<sphere
  position={{ x: 5, y: 3, z: 5 }}
  sound={{
    src: "sounds/carnivalRides.ogg",
    loop: true,
    playing: true,
    volume: 0.5
  }}
/>

The src property points to the location of the sound file.

In the example above, the audio file is located in a sounds folder, which is located at root level of the scene project folder.

Tip: We recommend keeping your sound files separate in a /sounds folder inside your scene.

Supported sound formats vary depending on the browser, but it’s safe to use .mp3, .accc and .ogg.

.wav files are also supported but not generally recommended as they are significantly larger.

Each entity can only play a single sound file. This limitation can easily be overcome by including multiple invisible entities, each with their own sound file.

The distanceModel property of the sound component conditions how the user’s distance to the sound’s source affects its volume. The model can be linear, exponential or inverse. When using the linear or exponential model, you can also set the rolloffFactor property to set the steepness of the curve.

Video

You can add video to your scene by including a video entity.

<video
  id="myVideo"
  position={{ x: 2, y: 3, z: 1 }}
  width={4}
  height={2.5}
  volume={currentVolume}
  src="video/myVideo.mp4"
  play={true}
/>

The video entity needs to have a video selected in src, this can either be a local file or a url pointing to a remote video for streaming.

In the example above, the video is located in a video folder, which is located at root level of the scene project folder.

Supported video formats vary depending on the browser, but it’s safe to use .mp4 and .avi.

With volume you set the volume from 0 to 100.

Entity collision

Entities that have collisions enabled occupy space and block a user’s path, objects without collisions can be walked through by a user`s avatar.

<box position={{ x: 10, y: 0, z: 0 }} scale={2} withCollisions={true} />

The example above defines a box entity that can’t be walked through.

All entities have collisions disabled by default. Depending on the type of entity, collisions are enabled as follows:

  • For most entities, including primitives (boxes, spheres, etc), planes and base entities, you enable collisions by setting the withCollisions component to true.
  • To enable collisions in glTF models, you can either:

    • Overlay an invisible entity with the withCollisions component set to true.
    • Edit the model in an external tool like Blender to include a collider object. The collider must be named x_collider, where x is the name of the model. So for a model named house, the collider must be named house_collider.

A collider is a set of planes or geometric shapes that define which parts of the model are collided with. This allows for much greater control and is a lot less demanding on the system, as the collision object is usually a lot simpler (with less vertices) than the original model.

See 3D models considerations for more details on what colliders are and how to add them.

Collision settings currently don’t affect how other entities interact with each other, entities can always overlap. Collision settings only affect how the entity interacts with the user’s avatar.

Decentraland currently doesn’t have a physics engine, so if you want entities to fall, crash or bounce, you must code this behavior into the scene.

Tip: To view the limits of all collider meshes in the scene, launch your scene preview with dcl start and then click c. This draws blue lines that delimit all colliders in place.

×
×

Subscribe

The latest tutorials sent straight to your inbox.

×

Share

Share this tutorial with your community.