Special Objects

Boxshot has some other objects besides the scene nodes. Click the links below to fast–forward to the topic you need:


 

Scene

Boxshot supports a single scene object per opened document. You can’t create or open other scene from the script, just work with the one that is currently loaded. Some examples of scene usage are given here. The more structured list of properties and methods is below.

Scene nodes

Each scene has one and only one root node. You can get its children nodes, but can’t edit it, as it is read–only. However, it is OK to add, clone or remove its children. Use the following code to get the root node:

var root = scene.root;

However, you’ll get error message if you try any of the following calls:

scene.root.name = "my name";
scene.root.visible = false;
scene.root.translation = vec3(1, 1, 1);

You can get the children nodes exactly as for any other node in the scene:

var children = scene.root.children;
print("the root node has %1 children", children.length);

Saving and exporting

Boxshot saves scenes to an internal format, it also allows you to export to some well–known 3D formats, like Collada or 3D PDF. There is no big difference from the scripting point of view which format to use. All the saving is done this way:

scene.save(filename); // everything by-default version
scene.save(filename, params); // version with parameters

The params variable is a dictionary with some settings for saving plugin and Boxshot itself. Boxshot tries to guess the saving format using the file name extension you provide. All the saving plugins report Boxshot the extensions they support, so it can find the appropriate plugin easily. If you need to save a file with non-standard extension, you may pass a mime-type as a parameter:

var params = {"mime": "model/dae"};
scene.save("collada-file.txt", params);

To save the current scene as a Boxshot project, use this code:

scene.save("file.boxshot5");

Importing

You can import another Boxshot project into the scene this way:

const target = scene.root;
scene.importProject(target, "/full/path/to/other/project.boxshot5");

Rendering

You can render scene from the script as well, as from the user interface. Boxshot allows you to configure the rendering engine the way you like, so you can automate the rendering. Here is an example of the rendering from script:

var params = {"width": 400, "height": 300, "passes": 200};
scene.render("renderer.raytracer3", "/path/to/file.png", params);

Note that the rendering may take a while and cannot be cancelled if started from the script, so make sure you saved your job first. This method is designed for using in batch scripting, so you’d better use UI rendering, if possible.

The passes parameter specifies the number of passes to do. You can also use minutes instead and specify the rendering time.

The render() method has 3 parameters. The first defines the rendering engine, currently only “renderer.raytracer3” is supported. The second parameter defines the file name to save image to. Make sure you have provided the full path to the file. The last parameter is a dictionary of properties that are passed to the rendering engine, you can define a resolution here. If you provide just width or height, Boxshot will compute the other dimension using the camera aspect ratio.

Modifying scene properties

You can access all the scene properties using scripts. Here’s how:

// load background image and set magenta tint color
scene.backgroundMode = 0; // 0 - plain background, 1 - transparent background, 2 - environment background
scene.backgroundTint = color("#FF00FF");
scene.backgroundImage = "filename.jpg";

// setup scene environment
scene.environmentImage = "environment.exr"; // 
scene.environmentIntensity = 1.0; // means 100%
scene.environmentOffsetH = 0; // 
scene.environmentOffsetV = 0; // 
scene.environmentTint = color("#FFFFFF"); // white tint

// setup sunlight
scene.sunEnabled = true; // enable sunlight
scene.sunHeight = 45; // 
scene.sunRotation = 30; // 
scene.sunIntensity = 0.5; // 50%
scene.sunAngularSize = 2.0; // make the shadow smooth
scene.sunTint = color("#FFFFF0"); // make the sunlight slightly yellow

// configure the floor
scene.showFloor = true; // show the floor
scene.floorReflectionEnabled = true; // enable floor reflection
scene.floorReflectionLevel = 0.75; // set the reflection level to 75%
scene.floorReflectionDecay = 0; // no decay
scene.floorRoughnessLevel = 0; // specular floor reflection

// advanced scene settings
scene.realisticLighting = true; // enable slower, but more realistic lighting
scene.diffuseIntensity = 1.0;
scene.floorDiffuseLevel = 1.0;
scene.floorTotalShadowLevel = 1.0;
scene.floorEnvShadowLevel = 1.0;
scene.innerReflectionsReflectEnvironment = true;


 

Camera

You can access all the camera parameters from scripts. Use the following code to get the camera object:

var c = scene.camera;
print(c);

Camera object allows the script to change angles, fov, distance, aspect and the central point of rotation. Here’s how to get and set angles:

var c = scene.camera;

// read rotation angles
print("camera rotation angles: %1, %2", c.hangle, c.vangle);

// change rotation angles
c.setAngles(10, 20);
print("new camera rotation angles: %1, %2", c.hangle, c.vangle);

The other properties are even simpler:

var c = scene.camera;

c.distance = 30;
c.aspect = 4.0/3.0;
c.fov = 45;
c.znear = 1.0;
c.zfar = 3000.0;

// set the rotation center
c.center = vec3(0, 0, 1);

// set the depth of field
c.depthOfField = true;
c.focalDistance = 123.3;
c.apertureSize = 1.0;


 

Lights

You can create omni lights exactly as groups or shapes using script (see more information in the managing objects tutorial). You may then access all the light parameters from script:

// create a light
var l = scene.root.addLight("test light");

l.radius = 1.0;
l.intensity = 2.0;
l.color = color("#ff00ff");
l.lightVisible = true;

// move it
l.translation = vec3(10, 20, 30);

You may still rename and hide the light exactly as any other node in the scene:

l.visible = false;
l.name = "hidden light";


 

Materials

Boxshot has quite complex materials, so it would help to read the material tutorial first.

Obtaining materials

Materials can be obtained from the shape objects only. Let’s create a cylinder and get its materials:

var m = scene.root.addMesh("cylinder", "generator.simple.cylinder");
var mtls = m.materials;
print("Cylinder has %1 materials", mtls.length);
for (var i = 0; i < mtls.length; i++)
	print("%1. %2", i + 1, mtls[i].name);

If you know the name, there’s an easier way to get a material:

var m = scene.root.addMesh("sphere", "generator.simple.sphere");
var mtl = m.material("Surface");

Basic properties

Here’s how to modify the most common properties of the material:

// create a sphere and get its material
var m = scene.root.addMesh("sphere", "generator.simple.sphere");
var mtl = m.material("Surface");

mtl.metallicReflection = false;
mtl.saturatedReflection = true;
mtl.alphaIsReflectionMask = false;
mtl.reflection180Level = 0.2;
mtl.reflection180Tint = color(1, 0.5, 0);

mtl.refractionIndexN = 1.5;
mtl.refractionIndexK = 0;

mtl.opacity = 1.0;
mtl.refract = true;
mtl.refractionTransmittance = color(0.2, 0.1, 0.8);
mtl.refractionAttenuation = 100;

mtl.bumpMode = "Normal"; // use "None", "Normal", "ReliefIn" or "ReliefOut" here
mtl.normalMapLevel = 0.75;
mtl.heightMapLevel = 0;

mtl.thinFilmEnabled = true;
mtl.thinFilmMinimalThickness = 60;
mtl.thinFilmRefractionN = 2.5;
mtl.thinFilmRefractionK = 0;

The texture slots are accessed this way:

var tsDiffuse = mtl.diffuseSlot;
var tsReflectance = mtl.reflectanceSlot;
var tsRoughness = mtl.roughnessSlot;
var tsAnisotropyLevel = mtl.anisotropyLevelSlot;
var tsAnisotropyAngle = mtl.anisotropyAngleSlot;
var tsHeightmap = mtl.heightmapSlot;
var tsThinFilm = mtl.thinFilmSlot;

Each slot can be configured following the example below:

var tsDiffuse = mtl.diffuseSlot;
var tsRoughness = mtl.roughnessSlot;

tsDiffuse.tint = color(1, 0.5, 0, 1); // if the slot supports tint
tsRoughness.value = 0.2; // if the slot supports level value
tsDiffuse.filename = "texture.jpg";
tsDiffuse.repeatU = 2;
tsDiffuse.repeatV = 3;
tsDiffuse.offsetU = 0.2;
tsDiffuse.offsetV = -0.5;
tsDiffuse.wrapU = true;
tsDiffuse.wrapV = true;
tsDiffuse.linearFiltering = false;
tsDiffuse.rotation = 10; // use degrees here
tsDiffuse.crop = [0, 0.5, 1, 0.5]; // left, top, width, height. all in 0..1 range (if the slot supports cropping)

Cloning materials

Each material can return a dictionary of its parameters.

var s = scene.root.addMesh("sphere", "generator.simple.sphere");
var m = s.material("Surface");
var p = m.parameters;
for (var k in p) 
    print("%1 -> %2", k, p[k]);

You may build your own dictionary and pass it back to the material. Just use the identifiers from the real parameters’ dictionary:

var d = {"opacity": 0.5, "diffuse-tint": "#FF8000"};
m.parameters = d;

Boxshot silently skips the unknown items, so if you don’t see the change you need after applying the parameters, check the syntax of the dictionary keys.

Now you can copy one material to another, exactly as you do by dragging it to another mesh. Here’s the code:

var s1 = scene.root.addMesh("sphere 1", "generator.simple.sphere");
var m1 = s1.material("Surface");
m1.diffuseColor = color(1, 0, 0);
m1.opacity = 0.8;

var s2 = scene.root.addMesh("sphere 2", "generator.simple.sphere");
s2.translation = vec3(0, 0, 20);
var m2 = s2.material("Surface");
m2.parameters = m1.parameters;

The very last line copies all the material’s parameters from the first sphere to the second one.


 

Decals

You can read more about decals in the decals tutorial, below you will find how to create decals from scripts, access them and modify their parameters.

Creating decals

To create a decal you need a mesh object to put it on, then you simply call addDecal(from, to, image) method of that mesh object to place image as a decal:

var m = scene.root.addMesh("sphere", "generator.simple.sphere");
var d = m.addDecal(vec3(0, -100, 10), vec3(0, 0, 10), ":/textures/decals/Zombies 1.png");

Parameters from and to are in world coordinates and specify a ray used to trace against the mesh to find the decal placement point. The reason of using ray instead of a point on a mesh is that shapes may change in Boxshot and it needs to display decals as close to their original position, as possible. By having a ray, Boxshot can trace it against a new shape to find the new decal position.

Accessing Mesh Decals

To access existing decals, you again need a mesh object, then you can get decals using its decals property:

var s = scene.selection[0];
var decals = s.decals;
print(decals.length);

Here’s how to print decals’ names:

var s = scene.selection[0];
var decals = s.decals;
for (var i = 0; i < decals.length; i++)
	print(decals[i].name);

This decals property is a Javascript array of decal objects, so you can get its length with length property and iterate it as any other array.

Deleting Decals

To delete a decal simply pass its object to deleteDecal(decal) method of its mesh object:

var m = scene.root.addMesh("sphere", "generator.simple.sphere");
var d = m.addDecal(vec3(0, -100, 10), vec3(0, 0, 10), ":/textures/decals/Zombies 1.png");
m.deleteDecal(d);

The code above creates and immediately deletes a decal.

Accessing Decal Properties

The code below shows how to access and modify decal properties:

var m = scene.root.addMesh("sphere", "generator.simple.sphere");
var d = m.addDecal(vec3(0, -100, 10), vec3(0, 0, 10), ":/textures/decals/Zombies 1.png");
d.name = "My Decal";
d.width = 5; // cm
d.height = 4; // cm
d.scale = 1.2;
d.rotation = 45; // degrees
d.fitToImage(); // updates width and height
d.offset = 0.1;
d.opacity = 1.0;
d.customBack = true;

var mtls = d.materials;

Decal may have one or two (if the custom back is enabled) materials as an array in its materials property. You can edit them the same way as you edit the standard scene materials. Not all the properties are editable, so use the user interface controls as a reference of what you can edit for decals.


 

Snapshots

Here’s how to list the snapshots using scripting:

var s = scene.snapshots;
for(i = 0; i < s.all.length; i++)
	print(s.all[i]);

Use this code to take a new snapshot (“ui” is a global variable that you need to pass as a parameter in order to take a snapshot):

scene.snapshots.take("new snapshot", ui);

Finally, to activate a snapshot by index or by name use this code:

scene.snapshots.activate(0);
scene.snapshots.activateByName("new snapshot");


 

Math Types

Boxshot scripting engine supports some common math functions as well as some built-in types to simplify the scene scripting.

Colors

Boxshot actively uses colors to tint images, so the scripting engine supports them well. Here are some examples on how to create a color object:

var orange = color("#FF8000");
var red = color(1, 0, 0);
var magenta = color(255, 0, 255, 255);

Please note that if all the color components are less or equal to 1, Boxshot converts them to floating point numbers, so if you need (1, 1, 1) from the range 0..255, please use the string form instead:

var almostBlack = color("#010101");

You can access color components this way:

var c = color(10, 20, 30, 40);
print("r = %1, g = %2, b = %3, a = %4", c.r, c.g, c.b, c.a);

Please note that even if you may create color from string or bytes, internally color values are kept in floats in range 0..1, so all the components math should be done in floats. The code above doesn’t print 10, 20, 30 and 40, it prints floats instead. Here’s how to assign values:

var c = color(0, 0, 0, 0);
c.r = 1;
c.g = 0.5;
c.b = 0;
c.a = 1;
print(c);

In Javascript assignment means copying the reference, not the value, so the following code may surprise you:

var c1 = color(1, 1, 1, 1);
var c2 = c1; // c2 is now a reference to c1
c2.r = 0; // changing c2 changes c1, as well
print("c1 = %1, c2 = %2", c1, c2);

The code above prints two similar colors. To make c2 a copy of c1, not a reference, use the clone() method:

var c1 = color(1, 1, 1, 1);
var c2 = c1.clone(); // c2 is now a copy of c1, not a reference
c2.r = 0; // changing c2 doesn't change c1 now
print("c1 = %1, c2 = %2", c1, c2);

You can do some math with colors if necessary. Javascript language doesn’t allow overriding operators for custom classes, so there are some methods:

var c1 = color(0.5, 0.5, 0.5);
var c2 = c1.mul(2); // multiply all the components by 2
print("c1 = %1, c2 = %2", c1, c2);

var c3 = c2.div(2); // divide by 2
print("c3 = %1", c3);

You can add or subtract two colors:

var c1 = color(0.1, 0.1, 0.1, 0.1);
var c2 = color(0.3, 0.3, 0.3, 0.3);
var cAdd = c1.add(c2);
var cSub = c2.sub(c1);
print("add = %1, sub = %2", cAdd, cSub);

Please note that both mul(), div(), add() and sub() methods clamp resulting values to 0..1 range to prevent overflows. The same is done at the construction time, so color(300, 300, 300, 300) is the same as color(255, 255, 255, 255). Finally, here’s how to compare colors:

var c1 = color(255, 255, 255, 255);
var c2 = color(1, 1, 1, 1);
print(c1.equals(c2));

Vectors

Vectors work pretty much the same way as colors above. Here’s how to create a vector object:

var a = vec3(1, 2, 3);
var b = vec3(1); // means vec3(1, 1, 1)

Exactly as with colors you may access vector’s component directly:

var a = vec3(1, 2, 3);
print("x = %1, y = %2, z = %3", a.x, a.y, a.z);

You may also edit vector’s value by modifying its components:

var a = vec3(0);
a.x = 1;
a.y = 2;
a.z = 3;
print(a);

Similar to colors, assigning vectors means assigning references, use clone() to copy values:

var a = vec3(1, 2, 3);
var b = a;
var c = a.clone();
a.x = 10;
print("a = %1, b = %2, c = %3", a, b, c);

You can add, subtract, multiply vectors and do some other math functions, as well:

var a = vec3(1, 1, 1);
var b = vec3(3, 3, 3);
var c = a.add(b);
var d = b.sub(a);
print("add = %1, sub = %2", c, d);

Multiplication and division by number:

var a = vec3(1, 1, 1);
var b = a.mul(2);
var c = a.div(2);
print("mul = %1, div = %2", b, c);

Multiplication by vector:

var a = vec3(1, 2, 3);
var b = vec3(4, 5, 6);

var c = a.dot(b);
var d = a.cross(b);

print("dot = %1, cross = %2", c, d);

You may also get a vector’s length or normalize it this way:

var a = vec3(1, 2, 3);
var len1 = a.length;
var b = a.normalized();
var c = a.clone();
var len2 = c.normalize();

Please note the difference between normalize() and normalized() as the first modifies vector, while the second one doesn’t. The first function also returns the old vector’s length.

Math functions

Here is the list of built-in math functions you may use in scripts:

All the functions should be called with the Math prefix, like this:

var s = Math.sin(1);
var r = Math.random();

There are some useful constants like:

Please note that trigonometric functions like sin() and cos() work in radians not degrees, so you may need to convert values first:

var deg = 90;
var rad = 90 * Math.PI / 180;
var cos90 = Math.cos(rad);
print(cos90);

Custom functions

You can make your own custom functions. For example, you can simplify the degrees to radians code above this way:

function deg2rad(d)
{
	return d * Math.PI / 180;
}

print(Math.cos(deg2rad(90)));

Custom functions may process built–in objects. The function below converts color to grayscale using the simplest algorithm:

function grayscale(c)
{
	var gray = (c.r + c.g + c.b) / 3;
	return color(gray, gray, gray, c.a);
}

print(grayscale(color(1, 0, 0)));

More Scripting Tutorials