Skip to content

Creating an Environment with Boo

In this Tutorial, we'll go over how environments are set-up, and how to create a custom one as a plugin.

Environment Rendering

Environments are represented as a series of layers that are drawn in the background of your world. These layers are divided into two groups, and each group can has custom render modes that describe how to draw the layers.

Both orbital and sky layers have a `Layer` flag, that tells them in which order they should be rendered.

Orbitals

Orbitals represent something discrete in the sky, that is, well, orbiting. These are are usually for things like Stars(Suns), and moons or planets, but aren't limited to that. They have two render modes: Billboard, and Sphere. Billboard draws a flat image in the sky, while sphere uses a texture and draws a sphere.

Sky Layer

Sky layers is something that engulfs the whole sky. There are several modes that can be implemented with these types of layers. This is, for example, starts, atmosphere colors, clouds, etc.

Creating your first Environment

Let's create a simple environment using some preset orbitals and layers from essentials.

using System;
using pluginbase.Objects.World.Environment;
using pluginbase.Attributes;
using essentials.Environment.Orbital;
using pluginbase.Helpers.Data;
using essentials.Environment.Sky;
using essentials.Environment.Fog;
using essentials.Environment.Atmosphere;
using pluginbase.Dependencies;

namespace essentials.Environment
{
    [Environment("Simple Environment", Author = "Pontoon City", Description = "Basic environment with sun and moon")]
    public class SimpleEnvironment : EnvironmentBase
    {
        private readonly IOrbital[] _orbitals;
        private readonly IFog _fog;

        public BasicEnvironment()
        {
            _orbitals = new IOrbital[]
            {
                new Sun(3000f),
                new Moon(3400f)
            };
            _fog = new ColorFog(new Rgba(0.5f, 0.5f, 0.55f), 0.007f)
        }

        public override IOrbital[] CreateOrbitals()
        {
            return _orbitals;
        }

        public override ISkyLayer[] CreateLayers()
        {
            return new ISkyLayer[]
            {
                new StarLayer(),
                new CloudLayer(),
                new AtmosphereLayer(_orbitals),
                new FogSkirt(_fog)
            };
        }

        public override IFog CreateFog()
        {
            return _fog;
        }
    }
}
#include "macros"
import pluginbase.Attributes
import pluginbase.Objects.World.Environment
import pluginbase.Helpers.Data
import essentials.Environment.Sky
import essentials.Environment.Fog
import essentials.Environment.Precipitation
import essentials.Environment.Filters
import essentials.Environment.Orbital

[Environment("SimpleEnvironment", Author: "Pontoon City", Description: "Simple test environment")]
class SimpleEnvironment(EnvironmentBase):
    @Declare SkyColor Rgba(0f, 0.5f, 1f, 1f)
    @Declare WorldAmbient Rgba(0.2f, 0.2f, 0.2f)

    final _orbitals = null
    final _fog = null

    def constructor():
        # We create the orbitals and fog here, because they are needed in other parts
        _orbitals = (Sun(), )
        _fog = ColorFog(Rgba(0.5f, 0.5f, 0.55f), 0.007f)

    def CreateOrbitals():
        # Return a list of orbitals
        return _orbitals

    def CreateLayers():
        # Return a list of layers to be drawn in the sky
        return (StarLayer(), CloudLayer(), AtmosphereLayer( _orbitals ), FogSkirt(_fog, 0.05f, 0.2f) )

    def CreateFog():
        # Return fog
        return _fog

Precipitation

Precipitation is a class that implements a combination of a particle system and sound effects to simulate some sort of precipitation. This can range from rain, to snow, to dust storms, and you can have more than one system going at a single time. To see a simple system, add this to your environment:

public override IPrecipitation[] CreatePrecipitationSystems()
{
    return new IPrecipitation[]{
        new DustStorm(),
    };
}
    def CreatePrecipitation():
        return (DustStorm(), )

Filters

Filters are something that overlay the camera on render-time. This can be a simple color filter, or dust building up on the user's visor.

To see a simple Vignette filter, add this to your environment:

public override IFilter[] CreateFilters()
{
    return new IFilter[]
    {
        new VignetteFilter()
    };
}
    def CreateFilters():
        return (VignetteFilter(), )

Creating an orbital

There are a few convenient base-classes provided to implement orbitals. We'll be using `OrbitalRevolutionBase`, which helps us make an orbital that revolves around the planet.

Here's an example of a custom implemented Sun.

using System;
using pluginbase.Objects.World.Environment;
using pluginbase.Helpers.Data;
using pluginbase.Helpers.Coords;
using pluginbase.Objects.World.Environment.Orbitals;
using pluginbase.Dependencies;

namespace essentials.Environment.Orbital
{
    public class Sun : OrbitalRevolutionBase, IOrbitalGlow
    {
        [Dependency]
        protected IResourceResolver ResourceResolver{get;set;}

        public Sun()
            :base(300f, 0f, (float)Math.PI, (float)Math.PI/2f)
        {
            this.InjectDependencies();
        }

        public override OrbitalRenderMode RenderMode => OrbitalRenderMode.Billboard;
        public override string Resource => this.ResourceResolver.Resolve("essentials", "textures/sun.png")

        public override bool HasEmittance => true;
        public override Rgba EmittanceDiffuse => new Rgba(1.2f, 1.2f, 1.2f);
        public override Rgba OrbitalEmittance => new Rgba(1f, 1f, 1f);
        public override Rgba EmittanceAmbient => new Rgba(0.3f, 0.3f, 0.3f);
        public override Rgba EmittanceSpecular => new Rgba(1f, 0.8f, 0.7f);
        public override float Size => 1f;

        public float GlowStrength => 1f;
        public virtual float GlowRadius => 1f;
        public virtual Rgba GlowColor => new Rgba(0.5f, 0.2f, 0.3f);
    }
}
#include "macros"
import System
import pluginbase.Dependencies
import pluginbase.Objects.World.Environment
import pluginbase.Objects.World.Environment.Orbitals
import pluginbase.Helpers.Data

import essentials.Environment

class MySun(OrbitalRevolutionBase, IOrbitalGlow):
    @Dependency IResourceResolver _resolver

    def constructor():
        # This passes custom parameters to the OrbitalRevolutionBase that
        # describes how fast and in what position the Sun is in the sky
        super(300f, 0f, Math.PI, Math.PI / 2f)
        self.InjectDependencies()

    @Declare RenderMode OrbitalRenderMode.Billboard
    @Declare Resource _resolver.Resolve("essentials", "textures/sun.png")

    @Declare HasEmittance true
    @Declare EmittanceDiffuse Rgba(1f, 1f, 1f)
    @Declare OrbitalEmittance Rgba(1f, 1f, 1f)
    @Declare EmittanceAmbient Rgba(0.3f, 0.3f, 0.3f)
    @Declare EmittanceSpecular Rgba(1f, 0.8f, 0.7f)
    @Declare Size 1f

    @Declare GlowStrength 1f
    @Declare GlowRadius 1f
    @Declare GlowColor Rgba(0.5f, 0.2f, 0.3f)

Creating a sky layer

For the sky layer, we're going to create a simple pattern in the sky. You can download the texture I used on the right. Make sure to save this to the same directory as the rest of your environment files.

First, we will make a new descriptor class to define the sky layer.

using System;
using pluginbase.Objects.World.Environment.Sky;
using pluginbase.Helpers.Data;
using pluginbase.Dependencies;
using pluginbase.Helpers.Computative;

namespace essentials.Environment.Sky
{
    public class CloudLayer : SkyLayerBase
    {
        [Dependency]
        protected IResourceResolver ResourceResolver{get;set;}

        public CloudLayer()
        {
            this.InjectDependencies();
        }

        public override SkyLayerRenderMode RenderMode => SkyLayerRenderMode.SphereTexture;
        public override string Resource => this.ResourceResolver.Resolve("textures/skyfire.png");
        public override int Layer => 9;
    }
}
#include "macros"
import pluginbase.Objects.World.Environment.Sky

class SkyFire(SkyLayerBase):
    @Dependency IResourceResolver _resolver
    @DefaultConstructor

    @Declare RenderMode SkyLayerRenderMode.SphereTexture
    @Declare Resource _resolver.Resolve("environment/skyfire.png")
    @Declare Layer 9

Next, we will add `SkyFire` to your Environment by adding it to the sky layers. Note, that order doesn't matter here, as they are drawn by the Layer ranking, not the ordering.

public override ISkyLayer[] CreateLayers()
{
    return new ISkyLayer[]
    {
        new StarLayer(),
        new SkyFire(), // Add skyfire
        new CloudLayer(),
        new AtmosphereLayer(_orbitals),
        new FogSkirt(_fog)
    };
}
    def CreateLayers():
        # Return a list of layers to be drawn in the sky
        return (StarLayer(), SkyFire(), CloudLayer(), AtmosphereLayer( _orbitals ), FogSkirt(_fog, 0.05f, 0.2f) )

If all goes well, you should get something that looks like: