Continuing our series of articles on using the server-side V8 API we will now add some objects, loaded from a model file to the scene that we setup and lit in our previous articles. This one is going to be very simple but for bonus points we’ll add multiple copies of the object in different locations.<\/p>\n\n\n\n\n\n\n\n
Before starting, if you haven’t already read our posts on Creating an Empty Scene with Server-side V8<\/a> and Environment Lighting with Server-Side V8<\/a> please head over and read those articles now as we will build on what we created there for this one.<\/p>\n\n\n\n
We’ll need some extra helper classes for this command. Scene is still needed like before as we want to add our objects to a specified scene but we will also need two more, Group and Instance.<\/p>\n\n\n
\nconst Scene = require('Scene');\nconst Group = require('Group');\nconst Instance = require('Instance');\n<\/pre><\/div>\n\n\nCommand Definition<\/h3>\n\n\n\nAs usual, let’s start with the command definition and decide what parameters we need.<\/p>\n\n\n
\nmodule.exports.command = {\n name: 'tutorial_add_objects',\n description: 'Adds an object to an existing scene.',\n groups: ['tutorial', 'javascript'],\n arguments: {\n scene_name: {\n description: 'The name of the existing scene to add the sun and sky lighting to.',\n type: 'String'\n },\n filename: {\n description: 'The filename of the object to be loaded and inserted in the scene.',\n type: 'String'\n },\n options: {\n description: 'Importer options to use when loading the file.',\n type: 'Map',\n default: {}\n },\n positions: {\n description: 'Locations at which to insert the objects in the scene.',\n type: 'Array',\n default: [ { x: 0.0, y: 0.0, z: 0.0 } ]\n }\n }\n};\n<\/pre><\/div>\n\n\nThe scene_name<\/em> is needed to retrieve the existing scene. The filename<\/em> is the name of the file on disk to load. In our test example JSON-RPC later we’ll load a .mi file but this could also point to a .obj<\/em>, .gltf<\/em>, .fbx<\/em> or any other format supported by RealityServer. We also have an options<\/em> parameter in case you want to pass import_options<\/em> into the import call (for example to suppress automatic generation of lights and daylight in the OBJ importer).<\/p>\n\n\n\n
The final parameter, positions<\/em>, is going to be an array, in this case an array of objects representing position vectors at which we want to place the objects. We’ll end up with the number of objects specified in this array. As a default we just put one copy of the object at the origin.<\/p>\n\n\n\n
Execution<\/h3>\n\n\n\nSimilar to the previous articles we will first fetch the requested scene. In this case though, instead of fetching the options from the scene, which we don’t need, we are going to fetch the root group as a Group <\/em>object.<\/p>\n\n\n
\nmodule.exports.command = {\n ...\n execute: function({scene_name, filename, options, positions}) {\n \/\/ Fetch the existing scene data and its root group to work with\n const scene = new Scene(scene_name);\n const rootgroup = scene.root_group;\n ...\n }\n};\n<\/pre><\/div>\n\n\nThe root group is needed later so that we can attach instances of our imported object to the scene. Adding an object to the scenes root group is what causes it to be rendered. Actually importing the object is easy using the static import_elements <\/em>method on the Scene<\/em> object.<\/p>\n\n\n
\nconst import_result = Scene.import_elements(filename, { import_options: options });\n<\/pre><\/div>\n\n\nWe store the result of this call since that will contain the name of the root group element in the imported data. This is how we can place the contents of the imported file without knowing anything about the names of the objects inside it. Of course if you do know the names of the elements you can work with those specifically rather than the root group. Let’s fetch the existing object root group into a Group<\/em> object.<\/p>\n\n\n
\nlet object_group = new Group(import_result.rootgroup);\n<\/pre><\/div>\n\n\nThe result of the import had a rootgroup<\/em> property which tells us the name of the root group element. We now want to take the number of positions that were specified and create an instance of the objects root group at each of these positions. For that we will use a simple for loop.<\/p>\n\n\n
\nfor (let i = 0; i < positions.length; i++) {\n let transform = new RS.Math.Matrix4x4();\n transform.set_translation(-positions[i].x, -positions[i].y, -positions[i].z);\n\n let object_inst = new Instance(`${object_group.name}_inst_${i}`, true);\n object_inst.item = object_group;\n object_inst.matrix = transform;\n\n rootgroup.attach(object_inst);\n}\n<\/pre><\/div>\n\n\nThere are quite a few new concepts introduced here. For each object we create a transform matrix which uses one of our V8 math classes RS.Math.Matrix4x4<\/em>. This has various methods to help with using matrices but in this case we will just use the set_translation<\/em> method to set the elements of the matrix needed to move the object into position. Note that we invert the components of the position since transform matrices in RealityServer are inverse affine transforms<\/a>. Of course, to be really useful this command would likely need to be extended to also rotate and potentially scale the object which are also possible.<\/p>\n\n\n\n
Take it for a Spin<\/h3>\n\n\n\nHere is a quick JSON-RPC command sequence which uses this command and those we have made in previous articles to generate a simple layout of objects and render them.<\/p>\n\n\n
\n[\n {"jsonrpc": "2.0", "method": "create_scope", "params": {\n "scope_name": "tutorial_scope"\n }, "id": 1},\n {"jsonrpc": "2.0", "method": "use_scope", "params": {\n "scope_name": "tutorial_scope"\n }, "id": 2},\n {"jsonrpc": "2.0", "method": "tutorial_create_empty_scene", "params": {\n "scene_name": "tutorial_scene"\n }, "id": 3},\n {"jsonrpc": "2.0", "method": "camera_set_resolution", "params": {\n "camera_name": "tutorial_scene_cam",\n "resolution": { "x": 440, "y": 440 }\n }, "id": 4},\n {"jsonrpc": "2.0", "method": "tutorial_add_hdri", "params": {\n "scene_name": "tutorial_scene",\n "hdri_filename": "studio_grid.exr"\n }, "id": 5},\n {"jsonrpc": "2.0", "method": "tutorial_add_objects", "params": {\n "scene_name": "tutorial_scene",\n "filename": "scenes\/crystal.mi",\n "positions": [\n { "x": 0.1, "y": -0.1, "z": -0.5 },\n { "x": -0.1, "y": -0.1, "z": -0.5 }\n ]\n }, "id": 6},\n {"jsonrpc": "2.0", "method": "render", "params": {\n "scene_name": "tutorial_scene"\n }, "id": 4},\n {"jsonrpc": "2.0", "method": "delete_scope", "params": {\n "scope_name": "tutorial_scope"\n }, "id": 5}\n]\n<\/pre><\/div>\n\n\n\n\nIf all goes well you should get something like the result on the right. This looks quite dark and has some other issues we will correct in our next post on setting up camera and environment dome parameters.<\/p>\n\n\n\n
While this command is very simple, it introduces some concepts you will find yourself using over and over. In particular creating an instance of some type of element (whether it is an object, a light or something more exotic like a section plane) with transformation information and adding that to the scene root group so that it is used by the renderer.<\/p>\n\n\n\n
You can take this a lot further by also changing materials on the instance and overriding those in the file or if you know something about the structure of the data in the files, reaching into the imported data and making further changes.<\/p>\n<\/div>\n\n\n\n
\n
Dynamically Loaded Objects<\/figcaption><\/figure>\n\n\n\n<\/p>\n<\/div>\n<\/div>\n\n\n\n
Summary<\/h3>\n\n\n\nIn this post we learned how to load up 3D data from disk and insert it into the scene using a server-side V8 command. As part of that process you’ve also seen how the basic structure of a scene works. As always, if you are having any trouble getting going please contact us<\/a>. You can download the full source for the commands used in this article below.<\/p>\n\n\n\n
tutorial_create_empty_scene.js<\/a>Download<\/a><\/div>\n\n\n\ntutorial_add_hdri.js<\/a>Download<\/a><\/div>\n\n\n\ntutorial_add_objects.js<\/a>Download<\/a><\/div>\n","protected":false},"excerpt":{"rendered":"Continuing our series of articles on using the server-side V8 API we will now add some objects, loaded from a model file to the scene that we setup and lit in our previous articles. This one is going to be very simple but for bonus points we’ll add multiple copies of the object in different […]<\/p>\n","protected":false},"author":2,"featured_media":2950,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"spay_email":"","jetpack_publicize_message":"Learn how to add objects to your scene in RealityServer with Server-side V8 JavaScript","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[15,30],"tags":[14,27,34],"jetpack_featured_media_url":"https:\/\/www.migenius.com\/migenius\/wp-content\/uploads\/2019\/09\/v8_add_objects_feature.jpg","_links":{"self":[{"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/posts\/2949"}],"collection":[{"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/comments?post=2949"}],"version-history":[{"count":6,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/posts\/2949\/revisions"}],"predecessor-version":[{"id":3645,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/posts\/2949\/revisions\/3645"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/media\/2950"}],"wp:attachment":[{"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/media?parent=2949"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/categories?post=2949"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.migenius.com\/wp-json\/wp\/v2\/tags?post=2949"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}