Managing Geometric Objects

Creating Geometric Objects

In the previous tutorial we learned to create various user interface objects, such as windows and buttons. Creating geometric objects is equally easy. For example, in order to create sphere objects, you first need to include the header file defining properties for the sphere class. This can be done by calling:

    include("real/objects/r3sphere.js");

[Note] Note
You can find all built-in geometric objects from the 'scripts/js/real/objects' folder. Plug-in libraries may define geometric objects too, in which case their header files can be found from 'scripts/js/plugins' folder.

Once you have loaded the header file, you can create any number of sphere instances. For example, the following program creates two sphere objects.

    sphere1 = new r3Sphere(0);
    sphere1.SetRadius(0.1);

    sphere2 = new r3Sphere(0);
    sphere2.SetRadius(0.01);
    sphere2.SetCenter(new r3Vect(0.1, 0.1, 0));

The above code should be self explanatory.

Inserting into the Current Project

Once you have created geometric objects, you also need to decide what to do with them. The most common operation is to insert them into the current project.

To do this, you need to find the JavaScript object managing geometric objects in the current project. This can be done by calling:

    primLayer = GetJS("CurrentProject.Geometrics");

[Note] Note
The GetJS() function corresponds to the Get() function. However, the difference is that Get() returns you a Realsoft 3D object, whereas the GetJS() function creates and returns JavaScript wrapper object.

Then you can insert the two spheres into the current project by calling:

    primLayer.LOCKEXCLUSIVE();
    primLayer.INSERT(0, 0, sphere1);
    primLayer.INSERT(0, 0, sphere2);
    primLayer.RELEASE();

When modifying the contents of a layer, you must always lock the layer by calling either LOCKEXCLUSIVE() or LOCKSHARED() methods. The reason for this is that Realsoft 3D is multi threaded application and two or more threads can access the contents of a layer simultaneously.

If you are going to change the contents of a layer, a mutually exclusive 'write' lock must be obtained by calling the LOCKEXCLUSIVE() method. For example, when you insert a new object into a layer, mutually exclusively lock must be used.

In case you only want to read the contents of a layer, shared read lock is sufficient. This can be accomplished by calling the LOCKSHARED() method.

Both type of locks can be released by calling the RELEASE() method.

Creating Levels

To create a level object:

    include("real/objects/r3level.js");

    level = new r3Level(0);

Let's imagine we want to create couple of sub objects for the newly created level object, say two cubes.

    include("real/objects/r3cube.js");
    
    cube1 = new r3Cube(0);
    cube1.SetParent(level);
    cube1.SetP0(new r3Vect(0.0, 0.0, 0.0);
    cube1.SetP1(new r3Vect(0.2, 0.0, 0.0);
    cube1.SetP2(new r3Vect(0.0, 0.2, 0.0);
    cube1.SetP3(new r3Vect(0.0, 0.0, 0.2);

    cube2 = new r3Cube(0);
    cube2.SetParent(level);
    cube2.SetP0(new r3Vect(0.1, 0.1, 0.0);
    cube2.SetP1(new r3Vect(0.25, 0.0, 0.0);
    cube2.SetP2(new r3Vect(0.0, 0.25, 0.0);
    cube2.SetP3(new r3Vect(0.0, 0.0, 0.25);

Note that analytic cubes are defined by four points: one corner point plus three associated 'edge' points. The remaining points for the cube are defined internally, so you don't have to (actually, can't) define them.

A level object also defines the 'Boolean' attribute for boolean operations. Let's turn the level object to a boolean operation, so that the two sub cubes gets boolean operated with each other.

    level.SetBoolean(R3BOOL_AND);

Our hierarchical level object is now ready so we can insert it into the current project by calling:

    primLayer.LOCKEXCLUSIVE();
    primLayer.INSERT(0, 0, level);
    primLayer.RELEASE();

Transforming Objects

All geometric objects define an attribute called 'Matrix', which defines so called local object space for the object. For example, an object can be moved by constructing the desired transformation matrix and concantenating it to the object's matrix.

The Matrix class defines four methods for setting up transformation matrices: translate(), scale(), rotate() and skew().

Let's imagine we need to translate the selected objects by 0.1 meters on the world 'x' axis. To construct a transformation matrix which translates in world x-axis, call:

    m = new r3Matrix();
    m.translate(0.1, 0, 0);

Then you can apply the transformation matrix to the selected objects by calling:

    primLayer.LOCKEXCLUSIVE();
    primLayer.TRANSFORM(m);
    primLayer.RELEASE();

You may concatenate any number of matrix operations into a single matrix. In this case, the last concatenated operation is executed first. For example, if you want to move and scale the selected objects, call:

    m = new r3Matrix();
    m.scale(0.5 0.5, 0.5);
    m.translate(0.1, 0, 0);

in which case the selected objects are first translated and then scaled.

Any transformation effect, such as scaling, rotation or skew, or any combination of them can be constructed and applied this way.

Single Point Editing

The TRANSFORM() method allows you to apply any linear transformation to an object. However, it does not allow you to deform the geometry of an object.

The geometry base class ('scripts/js/real/objects/r3prim.js) defines the SETPOINT() method, allowing you to set any of the geometric attributes defined by the object in question.

Let's create a nurbs curve consisting of four control points.

    include("real/objects/r3nurbs.js");
    
    nurbs = new r3Nurbs(0);
    nurbs.SetOrder(4);
    nurbs.SetCount(4);
    nurbs.SetKnots([0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]);
    for(i = 0; i < 4; i++) 
        nurbs.SETPOINT(i, new r3Vect(i * 0.1, 0, 0));

Here we used SETPOINT() method to define initial geometry for the nurbs curve.

Another useful method is GETPOINT(), which allows you to fetch the value of any desired geometry point. By using GETPOINT and SETPOINT methods, you can apply deformation effects to geometric objects. First you call GETPOINT() to fetch the current value, then you modify the value using some non linear function and finally you assign the modified value back to the object by calling SETPOINT().

For example, the following uses fractal noise to deform all geometric points of the given object.

    function applyNoise(obj)
    {
        // create new vector object
        p = new r3Vect();

        // fetch the number of geometry points in the given object
        pcount = obj.GetPointCount();

        for(i = 0; i < pcount; i++) {
            // fetch the current value of the point 'i' 
            obj.GETPOINT(p, i);

            // displace the point using fractal noise 
            p2 = p.noise(3, 4);
            p2.fmul(0.1);
            p.fadd(p2);
            
            // set displaced point back to the object
            obj.SETPOINT(i, p);
        }
    }

Enumerating Selected Objects

Now that we managed to implement a somewhat interesting deformation effect, let's see how it could be plugged in as a new deformation tool.

The layer base class defines a method which is very useful when implementing custom tools. The method is called ENUMSELECTLIST(). This method scans through the currently selected objects and passes them to the given hook function, such as the deformation function we wrote above.

There is only one complication. Because the JavaScript objects are 'wrapper' objects attached to actual Realsoft 3D objects, our hook function will be called with a Realsoft 3D object rather than a JavaScript object. The low level Realsoft 3D functionality does not know if the enumeration request was generated by JavaScript wrapper object. Therefore, we must create a JavaScript interface to the given Realsoft 3D object before we can access its properties through JavaScript methods.

Here is the code:

    function deformByNoiseCallBack(r3obj)
    {
        // create JavaScript interface to 'r3obj' 
        jsobj = R3ToJS(r3obj);

        // call the applyNoise() function
        if(obj)
            return applyNoise(jsobj);
        return 1;
    }

Now you can deform the selected objects by calling:

    primLayer.LOCKEXCLUSIVE();
    primLayer.ENUMSELECTLIST([R3RA_Hook, mycallback]);
    primLayer.RELEASE();

The 'R3RA_Hook' parameter is used for specifying the callback function to be called.

You can find this example from the 'scripts/js/myclasses/toolbars/mydeform.js'. To test it, create couple of SDS or NURBS objects, select them and apply 'Scripts/Toolbars/mydeform.js' pull-down menu.