Creating an Empty Scene with Server-side V8

We’ve covered server-side V8 commands before but in this post we will go into a little more detail and use some of the helper classes that are provided with RealityServer to make common tasks easier. Quite often you want to kick off an application by creating a valid, empty scene ready for adding your content. Actually, it’s something we we need to do in a lot of our posts here so to avoid repeating it each time, lets make a V8 command to do it for us.

Introduction

If you haven’t already used it, the server-side V8 JavaScript API in RealityServer is an easy way to add commands to the RealityServer JSON-RPC API with a little JavaScript. We covered some of the essentials in our Basic Canvas Operations in V8. The command in this post will be very simple but is a good example of something repetitive than can be frustrating to keep implementing on the client-side.

We’re going to make a command called create_empty_scene, can you guess what it will do? If you’re following along you can place it in your v8/commands directory and call the file create_empty_scene.js.

V8 Helper Classes

The V8 JavaScript API ships with many helpful classes for wrapping common manipulations and operations. You can read the full documentation in the RealityServer Document Center under Server APIsV8 JavaScript APIV8 APICore Modules. If you’re interested in how they are implemented the full source is provided in v8/includes.

For our command we will need five of the helper classes so let’s require them, Node.js style.

const Scene = require('Scene');
const Options = require('Options');
const Camera = require('Camera');
const Instance = require('Instance');
const Group = require('Group');

The require() method searches any configured include paths, by default just v8/includes but you can add more. By convention we put these require lines together at the start of the command file. As an interesting side note, you can also require JSON files which can be useful for configuration information.

Command Definition

All V8 commands need to define their parameters and other information. This also provides the documentation for the command. The code is pretty self explanatory.

module.exports.command = {
    name: 'create_empty_scene',
    description: 'Creates a valid but empty scene ready for adding objects and lighting.',
    groups: ['scene', 'javascript'],
    arguments: {
        scene_name: {
            description: 'The name to use when storing the created scene in the database. '
                + 'Other element names will be derived from this name as well.',
            type: 'String'
        }
    },
    returns: {
        type: 'Map',
        description: 'Map containing the database names of the options, camera, camera instance and rootgroup.'
    }
};

By this point the command will already be showing up in your list of RealityServer commands at http://host:port/?doc. However the command won’t do anything just yet as there is no execute function being exported.

Command Execution

To get the command to do anything useful we need to provide an execute function. This will look something like this.

module.exports.command = {
    ...
    execute: function({scene_name}) {
        ...
    }
};

This boilerplate shows a nice convenience you may want to use in your commands, destructuring arguments. The execute function when called receives a single argument, let’s call it args for now. Normally to access the individual arguments you need to use standard dot notation, e.g., args.scene_name. Using JavaScript destructuring in the argument list we can pull out the arguments directly into their own variables. So in the above there is no need to have lots of args dot this and args dot that, we can just use scene_name. You can of course also use the standard JavaScript arguments array.

Making Things Happen

Everything is now setup to do the work. Here is the code, with comments to explain what is happening.

function({scene_name}) {
    // Create all of the needed database elements
    const scene = new Scene(scene_name, true);
    const options = new Options(`${scene_name}_opt`, true);
    const camera = new Camera(`${scene_name}_cam`, true);
    const camera_instance = new Instance(`${scene_name}_cam_inst`, true);
    const rootgroup = new Group(`${scene_name}_root`, true);

    // Attach the camera to its instance
    camera_instance.item = camera;

    // Attach the camera instance to the rootgroup
    rootgroup.attach(camera_instance);

    // Set default filtering to Gauss with a radius of 1.5 pixels
    options.attributes.set('filter', 2, 'Sint32');
    options.attributes.set('radius', 1.5, 'Float32');

    // Set the needed elements onto the scene
    scene.options = options;
    scene.camera_instance = camera_instance;
    scene.root_group = rootgroup;

    // Return the details of our configured scene
    return {
        rootgroup: rootgroup.name,
        options: options.name,
        camera: camera.name,
        camera_instance: camera_instance.name
    };
}

If you look through the first sections of the Creating a Simple Scene Programmatically article you can get a feeling for how this sequence of operations would be implemented using the JSON-RPC API. Needless to say it is a lot more verbose and a lot less readable than the above code. Even so, a few parts warrant further explanation.

Element Constructors

Looking at the constructors for the elements you may notice something:

const scene = new Scene(scene_name, true);

The second parameter controls whether the constructor will try to locate an existing element to use or whether to create a new element. If set to true a new element will be created, otherwise the constructor will try and look up the named element in the database. This is useful for pulling existing elements into the helper objects so you can access them conveniently. Of course in this command we are creating everything from scratch.

Element Attributes

All of the helpers in our example are for Elements and derive from the Element class. This furnishes them with a property attributes which contains a special set and get function. You can see them used in these lines.

// Set default filtering to Gauss with a radius of 1.5 pixels
options.attributes.set('filter', 2, 'Sint32');
options.attributes.set('radius', 1.5, 'Float32');

Because attributes in RealityServer are typed we do not make use of the setter/getter system in JavaScript but rather need to set them through a function where we provide the name, value and type respectively.

Results

So, what does our fancy new command actually do. Well if you run the command and then call the render command on the scene it outputs, you’ll be treated to a rendering of the cover art from Smell the Glove by Spın̈al Tap. Not too shabby. Yes, I did actually render the image to the right using Iray rendering.

If you call the export_scene command and output the scene to a .mi file you can see the structure that has been created. Obviously there are no lights, environment or objects in the scene so there is nothing to render yet. While you can see from the code what the names of the elements created by the command will be, it also returns them in case you need to automate processing. In our next post we will turn on the light by adding a HDRI environment.

Empty Scene Rendering

Write Your Own–Start With Ours

This post has given you another quick introduction to some key concepts for using the server-side V8 JavaScript API. You can download the full code for the command below and it serves as a useful starting point for writing your own (it uses most of the useful boiler plate). For example, you could add parameters to allowing passing in initial options to be set as attributes on the options object or camera. As always, get in touch if you want to know more or have any questions.

create_empty_scene.js (right click to download)

Articles Tutorials