# Hiber Engine: Code-Kit Express

_Code-Kit_ is the scripting interface for the _Hiber Engine API_, and _Code-Kit \_Express_ is an additional layer built on top of it.

_Code-Kit Express_ includes a set of functions called helpers, which provides a simplified interface and hide the complexity of working directly with the underlying layer.

Please see [Code-Kit](https://www.npmjs.com/package/@hiberworld/code-kit) for more information.

Hiber Engine is a engine specifically designed for the Metaverse. It is built using standard web technology, including C++ converted to Web Assembly, and adds features such as multiplayer, interconnections between virtual worlds, interoperability, and support for web3 technologies. As it is built using standard web technology, there is no need to download any additional software. Hiber Engine creates a layer for the Metaverse on top of the existing internet.

Hiber Engine is the engine used on the web site [www.hiberworld.com](www.hiberworld.com).

## Setup

### Dependencies

- [Yarn](https://yarnpkg.com/)
- [Node.js](https://nodejs.org/)
- [Git](https://git-scm.com/)

### installation

Run `yarn` to install dependencies

### Local development

Run `yarn dev` to start a local development server

## Keyboard shortcuts

V - Toggle fly mode

L - Open list of assets and materials

M - Open game menu

## IDE

What code editor to use is up to you but we recommend [VS Code](https://code.visualstudio.com/) from MicroSoft, it's free and widely used.
CodeKit Express is developed in _TypeScript_ also from MicroSoft. Most properties are typed and have auto completion. In most IDE´s you access it with `Ctrl+Space`.

![Auto Completion](https://user-images.githubusercontent.com/1092077/210209360-68711787-40f9-4e91-8d4f-37b0499a9e8f.gif)

## Getting started

To start working with CodeKit Express create a new project in the project folder [src/projects](src/projects). The easiest way to do this is to duplicate the project [empty.ts](src/projects/empty.ts). Make sure to change name from _empty_ in the file to the name of your project, for example `testProject`.

You should now have a project file that looks like this:

```ts
import { addTo, draw, getGround, getRoot } from '../helpers';

export const testProject = () => {
  // Create the root object
  const root = getRoot();

  // Add something to stand on
  const ground = getGround({ hilly: 0.1 });
  addTo(root, ground);

  // Draw the scene
  return draw(root);
};
```

To run this project you must add it to [src/index.ts](src/index.ts) like this.

```ts
import { CodeKit } from '@hiberworld/code-kit';
import { addKeyboardShortcuts } from './helpers';
import { onHotReload } from './helpers/onHotReload';
import { testProject } from './projects/testProject';

if (import.meta.hot) {
  onHotReload();
  import.meta.hot.accept();
}

const scene = testProject(); // <-- Add project here

const hs = new CodeKit();
addKeyboardShortcuts(hs);
hs.renderScene('canvas', scene);
```

The scene should look like this:

![empty scene](https://user-images.githubusercontent.com/1092077/210246517-430fbd14-3aa6-47c5-8bbf-5163633fe1c6.png)

## Naming

CodeKit Express consists of a set of functions called helpers. Most of these helpers create and return something or adds something to an existing object.

### get-helpers

Helpers that create and returns something starts with _get-_, like [getPrefab](docs/modules/getPrefab.md). An object returned from a get-helper will only be visible in the scene if it's added to another parent object. This is done using the helper [addTo](docs/modules/addTo.md).

```ts
const cube = getPrefab({ p: [0, 0, 5] });
addTo(root, cube);
```

### add-helpers

Helpers that adds something starts with _add-_, like [addPointLight](docs/modules/addPointLight.md). An add-helper needs a target object to work as it should. This target helper is also returned.

```ts
const lightSource = getGroup({ p: [0, 2, 5] });
addPointLight(lightSource);
addTo(root, lightSource);
```

## Adding a 3D object to the scene

3D objects in HiberEngine are called Prefabs. A prefab can contain a number of 3D objects, animations. particles etc. The most simple way to add a prefab to the scene is by using the helper [getPrefab](docs/modules/getPrefab.md):

```ts
const cube = getPrefab();
addTo(root, cube);
```

or shorter

```ts
addTo(root, getPrefab());
```

Add this to your project file and a cube will appear on the scene.

```ts
import { addTo, draw, getGround, getPrefab, getRoot } from '../helpers';

export const testProject = () => {
  // Create the root object
  const root = getRoot();

  // Add something to stand on
  const ground = getGround({ hilly: 0.1 });
  addTo(root, ground);

  const cube = getPrefab(); // <-- your code here
  addTo(root, cube);

  // Draw the scene
  return draw(root);
};
```

![Box on scene](https://user-images.githubusercontent.com/1092077/210246597-ae6da39b-5364-4ce9-8f85-ce633e1ae54a.png)

This code places a cube at the location of the avatar in the center of the scene. However, you may want to customize the cube further. Let's explore how to modify the code to add additional properties to the cube.

```ts
const cube = getPrefab({
  id: 'cube_01', // What prefab to use
  p: [0, 0, 5], // Position, default is 0, 0, 0
  r: [0, 0, 0], // Rotation in Euler/degrees, default is 0, 0, 0
  s: [1, 1, 1], // Scale, default is 1, 1, 1
});
```

In the code above we define what prefab to use and sets its _position_, _rotation_ and _scale_.

![cube positioned](https://user-images.githubusercontent.com/1092077/210246655-9d651ddc-d1d6-44e5-bd2c-434a7ce70fdb.png)

If we would like to add anything else but a cube we change the line:

    id: 'cube_01',

To something else, like this:

    id: 'wooden_wheel_01',

This will add a wooden wheel the the scene. Lets rotate and scale it to see how it works.

```ts
const wheel = getPrefab({
  id: 'wooden_wheel_01',
  p: [0, 0, 5],
  r: [0, -30, 0],
  s: [4, 4, 4],
});
```

![wooden_wheel](https://user-images.githubusercontent.com/1092077/210246712-66ace91f-b502-4992-adb4-7ea673fbd601.png)

## Materials

Changing material on a prefab is easy. Just add the `material` property. To see available materials hit _Ctrl+Space_ after the colon in `material: `.

```ts
const wheel = getPrefab({
  id: 'wooden_wheel_01',
  p: [0, 0, 5],
  r: [0, -30, 0],
  s: [4, 4, 4],
  material: 't_rainbow_02',
});
```

![material_wheel](https://user-images.githubusercontent.com/1092077/210246767-c1e3ea21-dd10-4328-a9a8-0d4222c085d4.png)

## Assets and materials

To see what assets and materials press `L` to open the Assets browser. It will open a new tab in your browser.

- Click on the thumbnails to copy the assets id
- Double click to copy is as prefab code
- Click the _Change to Material_ button to the right top to see available materials

![](https://user-images.githubusercontent.com/1092077/209970510-7d5cb336-7c4c-4b4a-8a8b-1f69b4131d2f.png)

## Ground

Unless you want to create a space or water world a ground can be handy.
_There's a helper for that_ called [getGround](docs/modules/getGround.md). You have seen it being used in the examples above.

In it's most basic form it looks like this:

```ts
addTo(root, getGround());
```

![flat_ground](https://user-images.githubusercontent.com/1092077/210246844-3eb438b0-f05b-4fd8-b33d-f6131d06dd10.png)

This will add a flat grass plane. You can define how hilly it should be with the property `hilly`.

```ts
const ground = getGround({ hilly: 10 });
addTo(root, ground);
```

![hilly](https://user-images.githubusercontent.com/1092077/210315895-a0a8cef0-d580-43d0-bc8d-73211582077d.png)

### Material

To change the material of the ground, similar to how it is done with a prefab, you can use the `material` property.

```ts
const ground = getGround({ hilly: 0, material: 't_stone_floor_01' });
```

### Size

The size of the ground can be changes in two ways. The easiest way is to just change the scale.

```ts
const ground = getGround({ hilly: 1, s: pop3(3) });
```

**Tips:** Use [pop3](docs/modules/pop3.md) to populate a vector3 with the same number.

Another way is to change the number of ground blocks being used by changing `repeatX` and `repeatZ` from it's default value 4. You can of course also use both `repeatX` , `repeatZ` and `scale`.

```ts
const ground = getGround({ hilly: 1, repeatX: 1, repeatZ: 10 });
```

A cave can easily be created using two grounds.

```ts
addTo(root, getGround({ hilly: 1, material: 't_sand_01' }));
addTo(root, getGround({ hilly: 10, material: 't_rock_03', r: [180, 0, 0], s: [1, 4, 1] }));
```

![cave](https://user-images.githubusercontent.com/1092077/210246895-5c4063a3-2db1-4e89-a68e-96449147314d.gif)

## Images

You add an image to an object like this:

```ts
const cube = getPrefab({ p: [0, 0, 6] });
addImage(cube, 'https://cdn.hibervr.com/demo/img/Mona.jpg');
addTo(root, cube);
```

![addImage](https://user-images.githubusercontent.com/1092077/210246966-579d75c2-adbf-4d50-bb00-b751eecb4ff5.png)

Images for testing can be found here:
[src/assets/artData.ts](src/assets/artData.ts)

### Poster

If you want to add an image like a flat surface, like a poster, you can use the helper [getImage](docs/modules/getImage.md):

```ts
const img = getImage({ src: 'https://cdn.hibervr.com/demo/img/Mona.jpg', ratio: 671 / 1000 });
addTo(root, img);
```

![getImage](https://user-images.githubusercontent.com/1092077/210247023-3ae20697-a310-42f7-816f-846bc301f546.png)

### Framed image

You can also add an image with a frame by using [getFramedImage](docs/modules/getFramedImage.md).

`frame` property equals:
0 = Golden frame
1 = Stone frame
2 = Modern frame
-1 = random

```ts
const img = getFramedImage({
  src: 'https://cdn.hibervr.com/demo/img/Mona.jpg',
  ratio: 671 / 1000,
  frame: 0,
  p: [0, 1, 0],
  s: pop3(1),
});
addTo(root, img);
```

![getFramedImage](https://user-images.githubusercontent.com/1092077/210247095-2d431f52-6289-4a84-8039-f5ef7da47850.png)

## Video

You add a video to an object using [addVideo](docs/modules/addVideo.md).

```ts
const cube = getPrefab({ p: [0, 0, 6] });
addVideo(cube, { src: 'https://cdn.hibervr.com/video/bike.mp4' });
addTo(root, cube);
```

![addVideo](https://user-images.githubusercontent.com/1092077/210247132-dd5c6aa1-f02a-47b7-b046-eb35c6f5d887.gif)

Here are some videos to try out:

- bike
- brand
- dj
- Cubes_Waves
- Kaleidoscope
- Tunnel_Loop
- Cubes_Loop

### VideoPlayer

You can also add a video using the helper [getVideoPlayer](docs/modules/getVideoPlayer.md). This adds an video to a flat surface.

```ts
addTo(root, getVideoPlayer({ src: 'https://cdn.hibervr.com/video/bike.mp4', p: [0, 0, 5] }));
```

## Interactive Media Asset

Interactive Media Asset (IMA) could be described as links to other sites.
Say you have an image of an product and would like to make it possible to open up a wab page with more information about the product. Then you should use an **IMA**.

Any object, not group or node, could be turned into a IMA.
You add it using [addInteractiveMediaAsset](docs/modules/addInteractiveMediaAsset.md).

```ts
const img = getImage({ src: 'https://cdn.hibervr.com/demo/img/Mona.jpg', ratio: 671 / 1000 });
addInteractiveMediaAsset(img, {
  header: 'Mona Lisa',
  body: 'Also called Portrait of Lisa Hibertini',
  url: 'https://hiberworld.com/',
});
addTo(root, img);
```

![IMA](https://user-images.githubusercontent.com/1092077/210247195-c68549bf-7834-4dbc-b299-2e514a0182d2.png)

## Portal

_PORTAL NEEDS MORE LOVE TO WORK 100%_

If you instead want to link to another world on www.HiberWorld.com you use portals.
To add a portal you use the helper [getPortal](docs/modules/getPortal.md).

Find `LevelID` in the url of a world.
Ex: https://hiber.hiberworld.com/?Launch.InitLevelID=`1237022115291292`

```ts
const prt = getPortal({ LevelID: 1237022115291292, p: [0, 0, 10] });
addTo(root, prt);
```

## Clone

If you want another prefab with the same properties you can use the clone function, [getClone](docs/modules/getClone.md).

```ts
const wheel2 = getClone(wheel, { p: [-2, 3, 8], r: [-22, 33, 44] });
```

This code will create a clone of the prefab. If you provide new values for the properties of the prefab as arguments, they will override the corresponding values in the cloned prefab.

![getClone](https://user-images.githubusercontent.com/1092077/210247252-14076359-2114-4af2-a85e-725f51ad80e1.png)

### Symmetrical clone

Say you want to build something symmetrical like a spaceship or a vehicle. Then the [getMirrorX](docs/modules/getMirrorX.md), [getMirrorY](docs/modules/getMirrorY.md) and [getMirrorZ](docs/modules/getMirrorZ.md) is handy. These functions are a special set of the cone function. They clone an item and then flip the rotation and position to make it look like a mirrored object.

Say you hav wedge placed next to a scaled box, and you want to create a mirror of it on the other side.

```ts
const hull = getPrefab({ p: [0, 0, 5], s: [1, 1, 3] });
addTo(root, hull);
let wing = getPrefab({ p: [1, 1, 0], s: [1, 1, 1], id: 'wedge_45', r: [0, 0, -90] });
addTo(hull, wing);
```

![hull](https://user-images.githubusercontent.com/1092077/210247313-dc6d82bd-76eb-4283-9ada-7f3840949ef0.png)

It's done like this using getMirrorX:

```ts
const hull = getPrefab({ p: [0, 0, 5], s: [1, 1, 3] });
addTo(root, hull);
let wing = getPrefab({ p: [1, 1, 0], s: [1, 1, 1], id: 'wedge_45', r: [0, 0, -90] });
addTo(hull, [wing, getMirrorX(wing)]);
```

![getMirrorX](https://user-images.githubusercontent.com/1092077/210247347-f657173d-c99e-46f4-8f43-333622c7d510.png)

**Note:** It's not perfect since there's no distortion happening, just rotation and repositioning.

## Groups

You can also group prefabs together. This is powerful if you want to rotate, scale, move or clone and entire group of objects.

Create a group using [getGroup](docs/modules/getGroup.md):

    let grp = getGroup({p: [0, 0, 5]});

This will place a group 5 meters (1 meter = 3.28 feet) in front of you.

```ts
let wheelGrp = getGroup({ p: [0, 0, 5] });
addTo(root, wheelGrp);

const wheel = getPrefab({
  name: 'wheel',
  id: 'wooden_wheel_01',
  p: [0, 0, 0], // <-- Change to 0, 0, 0 to place it in the center of the group
  r: [0, -30, 0],
  s: [4, 4, 4],
  material: 't_rainbow_02',
});
addTo(wheelGrp, wheel);
```

#### The tentacle

Not just groups can contain other objects, in fact any object can have children. With a few lines of code you can get this:

![the tentacle](https://user-images.githubusercontent.com/1092077/210247397-7733665c-3f3f-4bf1-a5d0-1071efc15e58.png)

```ts
let parent = addTo(root, getPrefab({ id: 'color_cube_02_04', p: [0, 0, 20], r: [0, 0, -20] }));
for (let i = 0; i < 20; i++) parent = addTo(parent, getClone(parent, { p: [0, 2.2, 0], r: [0, 15, 15], s: pop3(0.9) }));
```

## Animations

CodeKit Express has a number of ways to animate/tween objects.

### Tween using key frames

Tween an object using key frames using the helper [getTween](docs/modules/getTween.md).

**Note:** `p`, `r`, and `s` defined in the key frames is relative values to the objects `position`, `rotation` and `scale`.

```ts
// Create key frames, ts = timeStamp 0 - 1
const kf: KeyFrameOptions[] = [
  { ts: 0.25, p: [0, 2, 0], easing: 'LINEAR' }, // <-- default easing is EASE_IN_OUT_QUAD
  { ts: 0.5, p: [2, 2, 0] },
  { ts: 0.5, r: [0, 90, 180] },
  { ts: 0.75, p: [2, 0, 0] },
  { ts: 0.8, r: [0, 90, 0] },
  { ts: 1, p: [0, 0, 0] },
];

// Create a wrapper for the tweens, use this to place, rotate and scale the animated object.
const tweenWrapper = getGroup({ p: [0, 1, 5] });
addTo(root, tweenWrapper);

// Create object to tween and apply the tween
const spr = getPrefab({ id: 'sphere_01' });
addTo(tweenWrapper, getTween(spr, kf, { loopBehaviour: 'RESTART' }));
```

![kf-tween](https://user-images.githubusercontent.com/1092077/210247496-e9e01324-50c9-4aa3-9dcb-d7ebd523f654.gif)

**Tween options**

- `loopBehaviour` - RESTART | REVERSE, default: REVERSE
- `duration` - The length of the tween, default: 2
- `progress` - Where in the tween should it start. Ex 0.5 starts 50% in, default: 0
- `speedMultiplier` - Change the speed, default: 1

### Spin

If you want a more basic animation like making an object spin. Heres the basic code for that.

```ts
// First create an spinning group
const spinner = getSpinningGroup({ p: [0, 0, 10], axis: 'y', r: [10, 20, 30] });
addTo(root, spinner);
// Create what should be spinning and add it to the group.
const cube = getPrefab();
addTo(spinner, cube);
```

You can get the same result by using the helper [getSpinning](docs/modules/getSpinning.md).

```ts
const cube = getPrefab();
addTo(root, getSpinning(cube, { p: [0, 0, 10], axis: 'y', r: [10, 20, 30] }));
```

**Note:** The cube is wrapped in an [Animated Node](node_modules/@hiberworld/code-kit/dist/utils/createAnimatedNode.d.ts). You must add the object returned from getSpinning to the scene to get the cube spinning.

![getSpinning](https://user-images.githubusercontent.com/1092077/210247603-72137b86-1d40-4efd-90ed-ef57a9db4daf.gif)

### Move

The get an object to move you can use key frames like this.

```ts
const kf: KeyFrameOptions[] = [
  { ts: 0, p: [0, 0, 0] },
  { ts: 1, p: [0, 4, 0] },
];

const tweenWrapper = getGroup({ p: [0, 0, 10], r: [0, 30, 20] });
addTo(root, tweenWrapper);

const spr = getPrefab();
addTo(tweenWrapper, getTween(spr, kf, { loopBehaviour: 'REVERSE' }));
```

You can get the same result by using the helper [getMoving](docs/modules/getMoving.md).

```ts
const cube = getPrefab();
addTo(root, getMoving(cube, { to: [0, 4, 0], p: [0, 0, 10], r: [0, 30, 20] }));
```

**Note:** `to` is where it should move to _relative_ to tho objects position `p`.

![getMoving](https://user-images.githubusercontent.com/1092077/210247669-f44bc0bd-42dc-4d62-859f-302922c96f0c.gif)

## Environment

Change environment by adding an [environment id](src/generated/environments.ts) to the [draw](docs/modules/draw.md) function.
To see available environment hit _Ctrl+Space_ after `return draw(root, '`.

```ts
return draw(root, 'starry_night_01');
```

![environment](https://user-images.githubusercontent.com/1092077/210247748-fa3a9a9d-dd56-43ec-9d5e-2d7b7403af43.png)

## Lights

HiberEngine has two kind of lights. A light source is added to an object or group.

**Note**: Lights are heavy to render, don't add to many and **don't add many close to each other**.

### PointLight

A point light can be added using [addPointLight](docs/modules/addPointLight.md).

```ts
const pl = addPointLight(getGroup({ p: [0, 0, 6] }));
addTo(root, pl);
```

Or use the helper [getPointLight](docs/modules/getPointLight.md)

```ts
addTo(root, getPointLight({ p: [0, 1, 5] }));
```

![getPointLight](https://user-images.githubusercontent.com/1092077/210247793-41dd6bca-4409-41ff-bd4c-ba8f158b4c5e.png)

### Spotlight

Spotlight can be added in a similar way using [addSpotlight](docs/modules/addSpotlight.md).

```ts
const sl = addSpotlight(getGroup({ p: [0, 2, 6] }));
addTo(root, sl);
```

Or use the helper [getSpotlight](docs/modules/getSpotlight.md)

```ts
addTo(root, getSpotlight({ p: [0, 2, 6] }));
```

![getSpotlight](https://user-images.githubusercontent.com/1092077/210247839-dc0b1cdc-ee6d-4242-a439-ba78e62a1e81.png)

**Tips:** To get a random color use the helper [getRandomColor](docs/modules/getRandomColor.md)

```ts
addTo(root, getSpotlight({ p: [0, 2, 6], color: getRandomColor() }));
```

## Sound

You can add a sound source to an object using [addAudio](docs/modules/addAudio.md).

```ts
const cube = getPrefab({ p: [0, 0, 5] });
addAudio(cube, { id: 'a_m_iconic_bullet_01' });
addTo(root, cube);
```

_id_ is the id of the sound file in the Hiber assets database. As for now you can only use sounds from there.

There are various [properties](docs/modules/addAudio.md) that can be used to make the sound behave differently. To make things easier, there are three helpers available instead of trying out the properties individually.

### Point Sound

Add a point sound to a object using [addPointSound](docs/modules/addPointSound.md).
A point sound is a helper for addAudio where radius is use to define the size of the audio source.
The audio will get stronger the closer you get to the source. Good for example birds singing in a tree.

`radius` in meters, approximately.

```ts
const cube = getPrefab({ p: [0, 0, 5] });
addPointSound(cube, 'a_am_birds_and_insects_01', 10);
addTo(root, cube);
```

### Omnipresent Sound

Add a omnipresent sound source using [addOmnipresentSound](docs/modules/addOmnipresentSound.md).
This adds a point of sound with the same volume in the entire world.

```ts
addOmnipresentSound(root, 'a_am_rainforest_01');
```

### Sound Cell

Add a sound cell using [addSoundCell](docs/modules/addSoundCell.md).
The sound will play with the same volume within the radius.
A sound cell is useful in situations where you want sound to be played within a certain area, such as a room, but not outside of it.

`radius` in meters, approximately.

```ts
const soundCenter = getGroup({ p: [0, 1, 0] });
addSoundCell(soundCenter, 'a_mu_bass_meant_jazz_kevin_macleod_01', 10);
addTo(root, soundCenter);
```

## Dummies

You can place dummy avatars in a scene using [getDummy](docs/modules/getDummy.md).

```ts
addTo(root, getDummy());
```

![getDummy](https://user-images.githubusercontent.com/1092077/210247892-b3d59f8a-5903-4b53-a4df-0450ab3a6986.png)

You change avatar and it's emot like this:

```ts
addTo(root, getDummy({ avatar: 'red_robot', emot: 'an_default_emote_shuffledance' }));
```

You can also change properties like `scale`, `speed` and `material`.

```ts
addTo(
  root,
  getDummy({
    avatar: 'red_robot',
    emot: 'an_default_emote_shuffledance',
    speed: 0.2,
    s: pop3(3),
    material: 't_lava_01',
  })
);
```

![lava robot](https://user-images.githubusercontent.com/1092077/210247939-38e97531-84b8-4d1e-9c72-8bc69628b846.gif)

Or why not add a video?

```ts
let dancer = getDummy({
  avatar: 'red_robot',
  emot: 'an_default_emote_shuffledance',
  speed: 0.5,
  s: pop3(7),
  p: [0, -6, 0],
});
addVideo(dancer, { src: 'https://cdn.hibervr.com/video/Tunnel_Loop.mp4', emissiveStrength: 2 });
addTo(root, dancer);
```

![video robot](https://user-images.githubusercontent.com/1092077/210247969-66ccb087-d23f-4743-a7f8-2726b71d3485.gif)

### Ready Player Me

You can also add customized avatars from [Ready Player Me](https://readyplayer.me). For the URL to work it must be an **HiberWorld** avatar created on https://readyplayer.me/hub/avatars.

The URL is found in the `three-dots-menu -> Copy .glb URL`.

Add a Ready player me dummy using [getExternalAvatar](docs/modules/getExternalAvatar.md).

```ts
const rpmAvatar = getExternalAvatar({
  url: 'https://api.readyplayer.me/v1/avatars/6345138aabc8b2f2ee9c21b1.glb',
  skeleton: 'SKG_RPM_MALE',
  p: [0, 0, 5],
  r: [0, 180, 0],
  emot: 'an_default_backflip',
});
addTo(root, rpmAvatar);
```

![RPM](https://user-images.githubusercontent.com/1092077/210248032-c4bbec39-5a88-4ab6-8f37-b654ffde6b1e.gif)

## Particles

HiberEngine has a highly capable particle system. To add particles to an object use [addParticles](docs/modules/docs/modules/addParticles.md.md)

```ts
const ball = getPrefab({ id: 'sphere_01', p: [0, 1, 3], s: pop3(0.3) });
addParticles(ball);
addTo(root, ball);
```

![basic particles](https://user-images.githubusercontent.com/1092077/210248190-8624bdd1-f61c-45c3-af65-a5a688efd81e.gif)

You can use particles om animated objects. Important to remember to add the particle to the object to be animated and not to the Animated Node returned from `getMoving`.

```ts
const ball = getPrefab({ id: 'sphere_01', p: [0, 1, 2], s: pop3(0.3) });
addParticles(ball); // <-- add particles here
addTo(root, getMoving(ball));
```

Add properties for the particle system like this:

```ts
const ball = getPrefab({ id: 'sphere_01', p: [0, 0, 2], s: pop3(0.3) });
addParticles(ball, {
  particleTexture: 'particle_star_01_albedo',
  sizeStart: 0.25,
  sizeEnd: 0.1,
  forceGravity: -10,
  randomVelocity: [2, 5, 2],
  gradientTexture: 'particle_gradient1_albedo',
  colorEnd: [1.0, 1.0, 1.0, 1.0],
  collisionMode: 'BOUNCE',
});
addTo(root, getMoving(ball));
```

![sparks](https://user-images.githubusercontent.com/1092077/210248250-30d06b95-f325-4277-b3b6-6d7ae78f36c1.gif)

All settings found here [addParticles.ts](src/helpers/addParticles.ts).

You can also add particles using the particle presets found here [PARTICLE_PRESETS.ts](src/helpers/PARTICLE_PRESETS.ts).

```ts
const cube = getPrefab({ p: [0, 0, 5] });
addParticles(cube, PARTICLE_PRESETS.starDust);
addTo(root, getMoving(cube));
```

## Get a room

Create a room by using [getRoom](docs/modules/getRoom.md).
Returns a room with walls defined in the [wallTypes](docs/enums/getWall.WALL_TYPES.md) property.
Walls are created using [getSlice3](docs/modules/getSlice3.md) and [getWall](docs/modules/getWall.md)

```ts
const room = getRoom({
  p: [0, 0, 5],
  wallTypes: [WALL_TYPES.DOOR, WALL_TYPES.WINDOW, WALL_TYPES.DOOR, WALL_TYPES.PLAIN],
});
addTo(root, room);
```

![room](https://user-images.githubusercontent.com/1092077/210248317-1bcf4841-a40b-4e00-82df-a854d0fd8546.png)

You can also create a house complex using [getComplex](docs/modules/getComplex.md).

```ts
const floor = getComplex({
  p: [0, 0.1, 10],
  dim: 5,
  height: 5,
  rooms: [
    { coords: [0, 0, 0], wallTypes: ['PLAIN', 'PLAIN', 'DOOR', 'NONE'] }, // Define wall types
    { coords: [1, 0, 0], roomType: 'CONNECTED' }, // Or randomize them using roomType
    { coords: [1, 0, 1], roomType: 'OPEN' },
    { coords: [1, 0, 2], roomType: 'CONNECTED' },
    { coords: [2, 0, 2], roomType: 'WINDOWS' },
    { coords: [2, 0, 0], roomType: 'CONNECTED' },
  ],
});
addTo(root, floor);
```

## Random vales

Since we want a scene to look the same every time we load it, we can't use the regular `Math.random()` function. Instead we use a random seed function.

A random seed function returns a sequence of numbers that appears to be random, but will be the same every time it is run, based on a value called a seed that is provided to the function.

You get random seed values using [getRandomSeed](docs/modules/randomSeed.md).

```ts
getRandomSeed().getValue();
```

Or use if the shorthand alias:

```ts
rsd().getValue();
```

This will return a value between 0 and 1.
Since in this case no seed is sent to getRandomSeed(_seen_) the globalSeed value will be used found here [globalSeed](src/helpers/global.ts).

If you are not satisfied with the sequence of random numbers generated by the function, you can specify your own seed number by providing any integer value greater than or equal to 1.

```ts
getRandomSeed(42).getValue();
```

This feature can be useful if, for example, you have a helper that is placing trees randomly on the scene and another helper that is creating an art gallery with a random layout. Both helpers are using the global random seed by default. If you are not satisfied with the way the trees are being placed, one way to change this is to use a different seed value.
You can also change the global seed value but it will effect all values using it.

You can get a random _between two values_ using:

```ts
getRandomSeed().range(0, 100); // float
Math.round(getRandomSeed().range(0, 100)); // int
```

And _random item from an array_.

```ts
getRandomSeed().randomFromArray(myArray);
```

## Scape

Scape is a helper that is used to distribute objects in a specific area, such as a landscape, cityscape etc. It is an efficient way to add a large number of objects to a scene in a seemingly random placement.

With a few lines of code using `getScape` you can get create something like this.

```ts
addTo(root, getScape(SCAPE_PRESETS.foliagesBig));
addTo(root, getScape(SCAPE_PRESETS.rocks));
addTo(root, getScape(SCAPE_PRESETS.palmTree));
```

![scape-running](https://user-images.githubusercontent.com/1092077/210348643-80f49343-c9e2-48e1-ad18-7224672ff403.gif)

The properties for scape is this:

`areaSize`
The size of the scape area. Default is 200.

`spaceSize` and `spaceSizeDiff`
The smallest and biggest space to place and item inside.
"spaceSize" is between spaceSize and spaceSize + spaceSizeDiff.
Default 10 and 20.

`gapSize` and `gapSizeDiff`
Along with this spaces you can also insert gaps with no items inside them.

`gapFreq`
How frequent should the gaps be? For example if you set gapSize and spaceMax to the same number and gapFreq to zero you will ge ta grid.

`boundaryMin`
If you want a glade in the middle of your scape area you define it with boundaryMin. The number is between 0 - 1.5.

`boundaryMax`
Similar to boundaryMin but defined the outer limit of the forest sp to speak.

`itemScale`
The start scale of the items placed in the space.

`itemScaleDiff`
Items placed in spaces will have a scale between itemScale and itemScale + itemScaleDiff.

`prefabId`
Which prefabs to use. Can be a string or an array of prefabsIds.

`material`
Which material to use. Can be a string or an array of materialIds.

`tiltiness`
How mush should the items tilt. Good for creating a wild forest. Value between 0 and 1.

`randomRotation`
Will rotate the item randomly between 0 and 360 on the Y axis.

Over this you can also `p`, `r`, `s` and `seed`.

Lets have a closer look.
Calling getScape with it's default values and you will get this:

```ts
addTo(root, getScape());
```

![scape vanilla](https://user-images.githubusercontent.com/1092077/210248405-d0e3001d-305e-407f-a976-fdc72f38302c.png)

As mentioned above in the section about random seed, you can change the layout of the rocks by adding a custom seed value.

```ts
addTo(root, getScape({ seed: 42 }));
```

If we want to make the number of rocks denser we can change `spaceSize` and `spaceSizeDiff` values.

```ts
addTo(root, getScape({ spaceSize: 5, spaceSizeDiff: 5 }));
```

![scape alter](https://user-images.githubusercontent.com/1092077/210248461-b16a7996-4f20-4484-a686-6604bc695df3.png)

If we would like to get _tilted candy assets in different sizes_ we could type this:

```ts
addTo(
  root,
  getScape({
    spaceSize: 5,
    spaceSizeDiff: 5,
    prefabId: ['lollipop_03', 'lollipop_01', 'lollipop_02', 'marshmallow_pillar_01'],
    tiltiness: 0.1,
    itemScale: 2,
    itemScaleDiff: 5,
    boundaryMin: 0.05,
  })
);
```

![candy forest](https://user-images.githubusercontent.com/1092077/210330198-5750d96b-17f1-4bab-9ac9-6e0bd8b686ab.png)

As seen above you can also use a preset. To alter a preset you can use this:

```ts
addTo(root, getScape({ ...SCAPE_PRESETS.foliages, ...{ r: [180, 0, 0], p: [0, 10, 0] } }));
```

### onEach

Scape also has a property called `onEach` which allows you to specify a function to be called for each space that is created, rather than automatically inserting a prefab. This allows you to have more control over what happens in each space.

Using that we can do something like this:

```ts
const lightCluster = getScape({
  p: [0, 4, 0],
  spaceSize: 10,
  spaceSizeDiff: 20,
  itemScaleDiff: 3,
  onEach: (props: any) => {
    const cyl = addSpotlight(
      getPrefab({ ...props, ...{ id: rsd().randomFromArray(['tron_e3_orange', 'tron_e3_green']) } }),
      {
        color: getRandomColor(),
      }
    );
    return cyl; // <-- Important to return what's been added.
  },
});
addTo(root, getSpinning(lightCluster, { p: [0, 0, 10], axis: 'y', r: [6, 0, 0], duration: 16 }));
```

![scape neon](https://user-images.githubusercontent.com/1092077/210248542-6905b945-fdbc-476b-9494-6c56354795c5.gif)

**Note:** It's important to return what's been added from the `onEach` function

## Documentation

Check the [docs](docs/README.md) for mor in-depth information.
