JavaScript API
Koru lets you do simple scene manipulation using JavaScript. It provides API for nodes, snapshots and camera — just enough to build custom scene tree, snapshots buttons and even simple animations or interactive pages.
Koru JavaScript Object
Each Koru scene on the page has its own JavaScript object of type Koru. You can get it using the code below:
var el = document.getElementById("my-object");
var my_object_koru = el.koru;
Where my-object is the id of the koru-viewport <div>. This way you can have many Koru scenes on the same page and control them independently.
Here are the list of Koru object properties you can use:
Property | Type | Description |
---|---|---|
root | Koru.Node | the root node of the scene |
snapshots | [Koru.Snapshot] | the array of snapshots objects |
materials | {Koru.Material} | the dictionary of scene materials, names are the keys |
camera | Koru.Camera | the camera object of the scene |
createSnapshotButtons | boolean | flag which is true by default, but you can set it to false if you want to create snapshots buttons yourself |
showProgressBar | boolean | flag which is true by default, but can be set to false if you don’t want to see the default loading progress bar |
Here is the list of methods that Koru object has:
- draw(frames : integer) — forces the immediate drawing of frames samples, waits until the drawing ends;
- invalidate(frames : integer) — tells Koru that frames samples need to be drawing, do not wait until it ends;
- merge(koruDatFile : string, targetNode : Koru.Node) — allows you to merge another Koru scene into this one.
The example below shows scene merging in action:
function koruInit(koru) {
koru.addEventListener('loadend', onKoruLoaded);
}
function onKoruLoaded(event) {
const koru = event.koru;
setTimeout(() => {
koru.merge('scene2.koruDat', koru.root);
}, 2000);
}
Koru object also provides some events you can add listeners to:
- loadstart — notifies that scene loading has started. Use this to display your own custom loading progress indicator;
- progress — notifies about loading progress;
- loadend — lets you know that the loading is over. Use to hide custom loading progress indicator and access snapshots;
- update — this one is sent every frame and you can use it to animate scene;
- visibilitychange — notifies about node visibility changes.
Each event gets a dictionary as a parameter. The dictionary contains important information regarding the event. Here are the list of keys of the dictionary:
Key | Type | Description |
---|---|---|
koru | Koru | contains Koru object |
progress | number | a floating point value from 0 to 1, defining the current loading progress (only in progress event) |
deltaTime | number | time in seconds from the previous scene update (only in update event) |
idleTime | number | time in seconds that the scene is left idle (only in update event) |
node | Koru.Node | contains node that has changed (only in visibilitychange event) |
Here’s how you can be notified about end of loading scene:
function koruInit(koru) {
koru.addEventListener('loadend', onKoruLoaded);
}
function onKoruLoaded(event) {
var which_koru = event.koru;
// do something
}
Function koruInit() is called by the Koru core when the scene is loaded. You can setup flags and subscribe to events here. The second function is called when the scene is loaded. You get Koru object from the event’s parameters and use it to access scene API.
Snapshots
The best way to see what you can do with snapshots API is to have a look at the built-in Snapshots export template code. Basically, there are two things you can do:
- Replace snapshots buttons with your own;
- List and activate snapshots when needed.
Here’s how to override snapshots buttons creation:
function koruInit(koru) {
koru.createSnapshotButtons = false;
koru.addEventListener('loadend', onKoruLoaded);
}
function onKoruLoaded(event) {
var k = event.koru;
for (var i in k.snapshots) {
var snapshot = k.snapshots[i];
if (!snapshot.visible) continue;
console.log(snapshot.name);
}
}
Here we tell Koru that we’ll handle snapshots button creation ourselves, then start listening for loadend event. Once we get the event, we run through the scene snapshots and log the names of the visible ones.
Here’s what you can do with snapshots in Koru API:
- Enumerate them using snapshots property** — this is a simple array of snapshot objects;
- Check their visibility using visible property of snapshot;
- Get their names using name property of snapshot;
- Activate them using apply() method.
Once again, the best way to learn snapshots API is to have a look at snapshots export template which features everything discussed above.
Nodes
Each scene node in Koru JavaScript API has following properties:
Property | Type | Description |
---|---|---|
name | string | lets you get the name of the node |
metadata | object | the metadata dictionary of the node |
userData | object | the user defined data of the node |
visible | boolean | lets you show and hide nodes by settings true or false values |
position | Koru.Vec3 | the position of the node |
rotation | Koru.Vec3 | the Euler rotation angles of the node |
scale | Koru.Vec3 | the scale of the node |
matrix | Koru.Mat4 | the local transformation matrix of the node, read-only |
matrixWorld | Koru.Mat4 | the world transformation matrix of the node, read-only |
boundingBox | Koru.Box3 | the oriented bounding box of the node’s meshes, read-only |
highlightColor | Koru.Color | the highlight color of the node |
parent | Koru.Node | the parent node (or null if the node is root), read-only |
children | [Koru.Node] | returns the array of the children nodes of this one |
meshes | [Koru.Mesh] | return the array of meshes that this node contains |
Here is the list of node methods:
- traverse(callback : function) : any — executes the callback function on this node and its descendants, returns the first positive callback result or undefined;
- traverseVisible(callback : function) : any — like traverse, but the callback will only be executed for visible nodes, returns the first positive callback result or undefined;
- traverseAncestors(callback : function) : any — executes the callback function on node descendants, returns the first positive callback result or undefined;
- getNodesByName(name : string) : [Koru.Node] — finds child nodes with the specified name;
- updateMatrix() — updates the local transformation matrix;
- intersect(ray : Koru.Ray) : boolean — intersects the ray with the node meshes;
For examples of using node API, have a look at scene-tree export template which uses most of the properties mentioned above. Also try clock snippet in Script Editor (Scene → Script Editor) which shows how to make animated clock model by rotating clock hands using node rotation API.
Meshes
Here is the list of mesh properties:
Property | Type | Description |
---|---|---|
material | Koru.Material | the material of the mesh, read only |
highlightColor | Koru.Color | the highlight color of the mesh |
boundingBox | Koru.Box3 | the oriented bounding box of the mesh, read only |
userData | object | the user defined data of the mesh |
Each mesh in Koru JavaScript API has material parameter that contains a Material object described below. Here’s how you can access meshes of a node:
var n = koru.root.getNodesByName("node")[0]
var meshes = n.meshes;
for (var i = 0; i < meshes.length; i++)
console.log(meshes[i].material);
Mesh objects also have the intersect(ray : Koru.Ray) : boolean method that checks the intersection of a ray with the mesh.
Materials
You can get the list of materials using koru.materials property or by enumerating all the nodes, meshes in them and getting their materials. Each Material object has the following properties and methods:
Name | Type | Description |
---|---|---|
layers | [Koru.MaterialLayer] | the array of Layer objects (see below) |
transparent | boolean | true or false, depending on the materials parameters |
doubleSided | boolean | true or false, depending on the materials parameters |
userData | object | the user defined data of the material |
update() | ** — | updates internal parameters of the material** — call this after changing the material |
You can get all the layers from the array returned by the layers property. Each layer has the following properties and methods:
Name | Type | Description |
---|---|---|
diffuseEnabled | boolean | returns if the diffuse block is enabled |
diffuseColor | Koru.Color | gets or sets the diffuse tint color. Use either unsigned int or Koru.Color object |
diffuseMap | Koru.Texture | gets or sets the diffuse texture. The layer needs to have this texture in the original scene, as otherwise Koru ignores this property |
specularEnabled | boolean | returns if the specular block is enabled |
specularColor | Koru.Color | same as diffuseColor, but for the specular one |
specularMap | Koru.Texture | same as diffuseMap, but for the specular map |
emissiveEnabled | boolean | returns if the emissive block is enabled |
emissiveColor | Koru.Color | same as diffuseColor, but for emissive color |
emissiveMap | Koru.Texture | same as diffuseMap, but for emissive map |
mask | Koru.Texture | controls layer’s mask |
opacity | number | opacity level of this layer |
update() | ** — | call this after changing layer’s parameters, except for the maps |
All the maps you want to update using API need to have a texture when the scene is exported. Otherwise Koru exports a simpler shader and assigning texture will be ignored.
The maps are of the Koru.Texture type and can be either assigned directly (using the standard Image object), or using the following methods:
- setFromImage(image : Image/Canvas) — updates texture with HTML Image object or with HTML Canvas object;
- = — you can also assign image, canvas or url directly to the property and Koru will handle that, as well.
A sample code that modifies the diffuse map of the very first layer of the material of the very first mesh of the first node:
var layer = koru.root.children[0].meshes[0].material.layers[0];
layer.diffuseColor.setRGBA(1, 0.5, 0, 0.5);
layer.diffuseMap.set(koru.canvas);
layer.update();
The first line is long, but simple: it gets a “very first” layer. The second line modifies the diffuse tint, the third line assigns the current preview as a texture and the last line updates the layer.
Texture
Koru Texture object has just one method setFromImage(image : Image/Canvas) : this, which updates texture with HTML Image object or with HTML Canvas object.
Camera
You can also manipulate the scene camera with JavaScript. Here’s how to get the camera object:
function koruInit(koru) {
koru.addEventListener('loadend', onKoruLoaded);
}
function onKoruLoaded(event) {
var k = event.koru;
var cam = k.camera;
// access camera properties here
}
Here is the list of camera properties:
Property | Type | Description |
---|---|---|
target | Koru.Vec3 | the rotation center of the camera |
position | Koru.Vec3 | the camera position. If camera position is changed, it automatically rotates to see its target |
rotation | Koru.Vec3 | the Euler angles of camera rotation: z** — yaw, y** — pitch and x** — roll |
distance | number | the camera distance from its center of rotation |
matrixWorld | Koru.Mat4 | the camera world matrix, read only |
matrixView | Koru.Mat4 | the camera view matrix, read only |
matrixProj | Koru.Mat4 | the camera projection matrix, read only |
matrixViewProj | Koru.Mat4 | the camera view projection matrix, read only |
You can create complex animations by combining node and camera transformations.
Camera object also has the following methods:
- lookAt(targetPosition : Koru.Vec3) — set the camera target to the specified position;
- getRay(x : number, y : number) : Koru.Ray — make a ray through the screen, where x is in the range [-1, 1] from left to right, and y is in the range [-1, 1] from bottom to top.
Ray
Ray object is used for tracing through the scene and compute hit points. This is useful for custom scene objects selection, for instance to display metadata and so on.
Here’s how to construct a Ray object:
var ray = new Koru.Ray(origin : Koru.Vec3, direction : Koru.Vec3, tfar : number)
Ray objects have the following properties:
Property | Type | Description |
---|---|---|
origin | Koru.Vec3 | the origin of the ray |
direction | Koru.Vec3 | the direction of the ray |
tfar | number | the maximum distance from the ray origin |
Ray objects also have these methods:
- getPoint(distance : number) : Koru.Vec3 — returns a point along the ray at the distance from the beginning;
- hitPoint() : Koru.Vec3 — returns the hit point of the ray, same as above, but uses tfar as distance.
Vec3
Vec3 object holds three floating point numbers and used for position, rotation and scale parameters. Here’s how to make one:
var v1 = new Koru.Vec3();
var v2 = new Koru.Vec3(x : number, y : number, z : number);
Vec3 objects have three properties:
Property | Type | Description |
---|---|---|
x | number | x component |
y | number | y component |
z | number | z component |
They also have quite a lot of methods:
- copy(vector : Koru.Vec3) : this — copies another vector to this one;
- clone() : Koru.Vec3 — clones this vector and returns a copy;
- setXYZ(x : number, y : number, z : number) : this — inits this vector with three numbers;
- setScalar(value : number) : this — sets all the components of this vector with the same scalar value;
- setFromArray(array : [ number ]) : this — inits this vector from array of numbers;
- toArray() : [ number ] — converts this vector to array of numbers;
- add(vector : Koru.Vec3) : this — adds another vector to this one;
- sub(vector : Koru.Vec3) : this — subtracts another vector from this one;
- mul(vector : Koru.Vec3) : this — multiplies this vector to another one (per-component);
- mul(value : number) : this — multiplies this vector to a scalar number;
- div(vector : Koru.Vec3) : this — divides this vector to another one (per-component);
- div(value : number) : this — divides this vector to a scalar number;
- lerp(vector : Koru.Vec3, f : number) : this — finds an intermediate vector between this one and another using a parameter;
- dot(vector : Koru.Vec3) : number — computes dot product of this vector and another;
- cross(vector : Koru.Vec3) : this — computes cross product of this vector and another;
- length() : number — computes the length of this vector;
- normalize() : this — normalizes this vector;
- transform(matrix : Koru.Mat4) : this — transforms this vector as a position vector using a matrix (moves it);
- transformNormal(matrix : Koru.Mat4) : this — transforms this vector as a normal vector using a matrix (doesn’t move it);
- equals(vector : Koru.Vec3) : boolean — compares this vector with another.
Quaternion
Here’s how to make a Quaternion object in Koru:
var q1 = new Koru.Quaternion();
var q2 = new Koru.Quaternion(x : number, y : number, z : number, w : number);
Quaternion objects have these properties:
Property | Type | Description |
---|---|---|
x | number | x component |
y | number | y component |
z | number | z component |
w | number | w component |
They also have the following methods:
- copy(vector : Koru.Quaternion) : this — copies other Quaternion to this one;
- clone() : Koru.Quaternion — clones this quaternion and returns a copy;
- setFromMatrix(matrix : Koru.Matrix) : this — inits this quaternion from a matrix;
- setFromArray(array : [ number ]) : this — inits this quaternion from an array of four numbers;
- toArray() : [ number ] — converts this quaternion to array of numbers;
- slerp(quaternion : Koru.Quaternion, f : number) : this — finds an intermediate quaternion between this one and another using a parameter;
- equals(quaternion : Koru.Quaternion) : boolean — compares this quaternion with another.
Mat4
Mat4 object represents a 4x4 matrix and is used for local transformations. Here’s how to make one:
var m1 = new Koru.Mat4();
var m2 = new Koru.Mat4(elements : [ number ])
These object have just one property: m : [number] — an array of 16 numbers, representing the matrix. There are also methods:
- copy(matrix : Koru.Mat4) : this — copies another matrix to this one;
- clone() : Koru.Mat4 — clones this matrix and returns a copy;
- setIdentity() : this — sets this matrix to the identity one;
- setFromQuaternion(quaterion : Koru.Quaternion) : this — inits this matrix from a Quaternion object;
- setPosition(position : Koru.Vec3) : this — inits this matrix as a translation one;
- setRotation(rotation : Koru.Vec3) : this — inits this matrix as a rotation one;
- setTransformation(position : Koru.Vec3, rotation : Koru.Vec3, scale : Koru.Vec3) : this — inits this matrix with offset, rotation and scale parameters;
- setFrustum(left : number, right : number, bottom : number, top : number, near : number, far : number) : this — inits this matrix as a frustum one;
- setFromArray(array : [ number ]) : this — inits this matrix from an array of 16 numbers;
- toArray() : [ number ] — converts this matrix to array of numbers;
- mulMatrices(matrixA : Koru.Mat4, matrixB : Koru.Mat4) : this — multiply two matrices;
- mul(matrix : Koru.Mat4) : this — multiply this matrix to another one;
- invert(matrix : Koru.Mat4) : this — invert this matrix.
Box3
Box3 object represents a bounding box — two vectors, defining minimum and maximum points in the space. Here’s how to create one:
var bb = new Koru.Box(min : Koru.Vec3, max : Koru.Vec3)
Box3 objects have two properties:
Property | Type | Description |
---|---|---|
min | Koru.Vec3 | the “minimum” point of the box |
max | Koru.Vec3 | the “maximum” point of the box |
They also have the following methods:
- copy(box : Koru.Box3) : this — copies another Box3 object to this one;
- clone() : Koru.Box3 — clones this bounding box and returns a copy;
- center() : Koru.Vec3 — returns the center of this bounding box;
- size() : Koru.Vec3 — returns the size of this bounding box (max** — min);
- intersect(ray : Koru.Ray) : boolean — checks if a ray intersects this bounding box and modifies the ray’s tfar if it does;
- occluded(ray : Koru.Ray) : boolean — tests if the ray intersects the box, but don’t modify any of them.
Color
Color object represents a RGBA color, defined by 4 floating point numbers. Here’s how to create a Color object:
var c1 = new Koru.Color();
var c2 = new Koru.Color(red : number, green : number, blue : number, alpha : number);
Here are the properties of Color object:
Property | Type | Description |
---|---|---|
r | number | the red component of the color |
g | number | the green component of the color |
b | number | the blue component of the color |
a | number | the alpha component of the color |
Color objects also have the following methods:
- copy(vector : Koru.Color) : this — copies another color to this object;
- clone() : Koru.Color — clones this object and returns a copy;
- setRGBA(red : number, green : number, blue : number, alpha : number) : this — inits this color with the provided parameters;
- setHex(hex : number) : this — inits this color with an integer number in the form of 0xAARRGGBB;
- setScalar(value : number) : this — sets red, green and blue components of the color to the provided parameter, sets alpha to 1;
- setFromArray(array : [ number ]) : this — inits the color from array of numbers;
- toArray() : [ number ] — returns this color as array of numbers;
- lerp(color : Koru.Color, f : number) : this — finds an intermediate Color between this one and another using a parameter.
Progress Bar
For overriding progress bar you need to set showProgressBar property to false once the window is loaded and Koru object is created, then handle loadstart, progress and loadend events yourself. Then you can create/show your own progress indicator, update it and hide when the scene is loaded.
See progress export template for a complete example of overriding loading progress bar.