Skip to content

Instantly share code, notes, and snippets.

@gabrieleara
Last active August 9, 2018 11:07
Show Gist options
  • Select an option

  • Save gabrieleara/1315eed1a966e1a0e5ca38b60d4bcbe1 to your computer and use it in GitHub Desktop.

Select an option

Save gabrieleara/1315eed1a966e1a0e5ca38b60d4bcbe1 to your computer and use it in GitHub Desktop.
XVR Documentation and Style Guide

XVR S3D Language Documentation

A non-standard standardization attempt

This file is written to ease the developing of a project using XVR Studio 2.0, written in S3D language.

For the style guide, check this other document.

Note: This document is for the most part a direct copy of the content of the wiki documentation of XVR and S3D language, available inside VRMedia Wiki website.

Table of Contents

Basic Language Overview

Identifiers

Identifiers are names which are used to declare variables, functions and classes. The rules to follow to build a valid identifier are the same of the C language, i.e. they can be constituted by:

  • alphanumeric characters
  • underscore characters '_'

The first character must not be a number; besides, it is not possible to use reserved names used by the compiler. The reserved name are the followings:

aadd case for modulus static vector
abs char function null str void
acos class goto range switch while
adel continue if reset tan
array cos int return trace
asc default left right true
asin do len set upper
asize else ln sin val
atan extern log space valtype
break false lower sqrt var

The basic statements of the language are bolded.

Preprocessor

Preprocessor directives, such as #define and #ifdef, are typically used to make source programs easy to change and easy to compile in different execution environments. Directives in the source file tell the preprocessor to perform specific actions. For example, the preprocessor can replace tokens in the text, insert the contents of other files into the source file, or suppress compilation of part of the file by removing sections of text. Preprocessor lines are recognized and carried out before macro expansion. Therefore, if a macro expands into something that looks like a preprocessor command, that command is not recognized by the preprocessor. The preprocessor recognizes the following directives:

define endif ifdef include import
else if ifndef pragma

The #import directive

In order to facilitate the exchange of software modules and to preserve the IP of developers, XVR was added a new pre-processor directive, #import, allowing to import a pre-compiled XVR script under the shape of a .s3d.bin file and to enable using the functions defined and implemented in the external imported module. Functions must be explicitely declared as extern in order to be called by the host XVR application. This is a snippet showing how to use the #import directive:

#import <ExternLib.s3d.bin>    // name of the precompiled bin file
extern function fibonacci;    // name of the functions defined in
extern function factorial;    // the imported .bin file 

...

for(var i=0; i<20; i++)
{
outputLn("factorial(", i, ") = ", factorial(i) );
outputLn("fibonacci(", i, ") = ", fibonacci(i) );
}

...

Variables

XVR variables have dynamic typing; the type of a variable can change through the variable's life.

NOTE: while this is true, it is not a good practice to change multiple times the purpose of a variable.

Variables declaration

A variable is declared using the following syntax:

var <var1>[= <value1>] [,<var2>[ = <value2>]...] ;

It is possible to declare more than one variable with a single instruction and to assign an initial value.

Scope

  • Local variables — Variables declared inside a function body are considered local, therefore their value is destroyed when exiting the function.

  • Global variables — Variables declared outside function bodies are considered global, therefore they are visible everywhere from there on and their value persists during the whole execution.

Static variables

The static keyword specifies that the variable has static duration (it is allocated when the program begins and deallocated when the program ends) and initializes it to 0 unless another value is specified.

static var <var1>[= <value1>] [,<var2>[ = <value2>]...] ;

If the static variable is explicitely initialized when it is declared, it is possible in every moment to reset its value to the initialization value:

static var a = 10;
a = 7;
reset a;    // a = 10

Classes

A class is declared using the followin syntax

class <classname>[:<classbase1>][,<classbase2>][,...]   // declaration of class name (and base classes)
{    
    // class declaration body
    var <var1>[= <value1>] [,<var2>[ = <value2>]...] ;      // declaration of member variables
    ...
     <funcname> ( [<var1>] [,[<var2>] ... ] );                   // declaration of methods
    ...
};

Once the class is declared, its default constructor and destructor are automatically created. Optionally it is possible to declare a user-defined version of constructor and destructor, using this syntax:

function <classname>::<classname> ( [<c_var1>] [,[<c_var2>] ... ] )
{
    [<constructor body>]
}
 
function <classname>::~<classname> ( [<d_var1>] [,[<d_var2>] ... ] )
{
    [<destructor body>]
}

Note: it is not necessary to declare the constructor and the destructor inside the class declaration body.

Methods are declared using the following syntax:

function <classname>::<funcname> ( [<m_var1>] [,[<m_var2>] ... ] )
{
    [<method body>]
}

Note: it is necessary to declare the method prototype inside the class declaration body, while its full declaration must be defined outside.

To instance an object of the class:

var <obj> = <classname> ( [<c_param1>] [,[<c_param2>] ... ] )

To call a method of an instanced object:

<obj>.<funcname>( [<m_param1>] [,[<m_param2>] ... ] )

Functions

A function is declared using the following syntax:

function <func> ( [<var1>] [,[<var2>] ... ] )
{
    <Function Body>
}

Inside the function body it is possible to declare new local variables, call other functions and methods (both user defined or predefined), use language instruction etc.

The return [<value>] instruction allows to specify the return value of the function. If the value is not specified, or return is not used, the function will return a null value. If a function is used before its declaration, it is necessary to declare a prototype of the function before its first usage:

function <func> ( [<var1>] [,[<var2>] ... ] );

With this prototype declaration, the compiler is instructed about the function existance and about the number of parameters. In the real declaration it is possible to specify different names for the formal parameters, but the number has to be the same.

To use a function the following syntax is used:

<func> ( <param1>, <param2>, ...);

The number of actual parameters (APN) can be different from the number of formal parameters (FPN).

  • If APN < FPN, the missing parameters will be passed with null values.
  • If APN > FPN, the exceeding parameters will be ignored.

Variables can be passed to the function by reference or by value. If by reference, the actual parameters undergo the possible modifications operated by the function, otherwise they cannot be modified.

To pass a variable by reference, the & symbol must be put before the identifier in function declaration:

<func> ( &<param1>, <param2>, ...);

Note: Since Arrays or Objects are reference variables, even if they are passed by value they are actually passed by reference, like in Java language.

Data Types

Constants and variables assume values which may own to predefined data types, any predefined data type.

Therefore the following situation is legal:

var a;
a = true;
a = 2.1;
a = 182;

IMPORTANT: Often it is not necessary to specify the type at declaration time, as it is automatically assumed at the first instancing.

Therefore, although the following data types exist, they are not specified with the corresponding keyword, except from vector and array (for instance, int is not a data type but a predefined function).

NOTE: In official documentation, references to the ANYTYPE data type may be found. ANYTYPE corresponds to any valid XVR data type, with the exception of objects.

  • bool — value is either true or false.
  • float — floating point numbers, which can range between ±1e-45 and ±1e38. The '.' separator or the exponential one 'e' are sufficient to define a floating point number.
  • int — regular integer valyes, between ±2147483647.
  • object — in addition to user-defined classes, the S3D language offers lots of predefined classes. It is possible to declare objects of these classes (except from classes which allow just one object automatically created) and to access methods and members with the operator '.', as already shown.
  • string — they are sequences of character with no limits on length. As for the arrays, it is possible to access an element (a character) using an index and the operator [ ].
  • vector — they are array of floats. They can have any dimension. In the XVR reference, when not differently specified, vectors are assumed to be three-component.
  • matrices — although not directly supported, often XVR functions return, or accept as input, 16-dim vectors representing matrices. In this case the matrix is stored in column-major order, like in OpenGL. Thus, the vector representation of a 4x4 matrix named MAT will be:
MAT[0]   MAT[4]   MAT[8]   MAT[12]
MAT[1]   MAT[5]   MAT[9]   MAT[13]
MAT[2]   MAT[6]   MAT[10]  MAT[14]
MAT[3]   MAT[7]   MAT[11]  MAT[15]
  • array — it is a set of elements which can be accessed through an index. The array size is not fixed, but it may be changed with particular functions. The S3D array is much more flexible than in other languages, as its elements may be of differeny type. To specify the index in order to access the desired element, the operator [ ] is used. Arrays are zero-indexed.

An overview of the various data dypes is the following one:

v0  = false;                // bool
v1  = 123.2;                // float
v2  = -20.0;                // float
v3  = 3e-2;                 // float
v4  = 123;                  // int
v5  = CVmLight();           // object
v5.setPosition(10, 10, 20);

v6  = "Hello!!!";           // string
v7  = v6[1];                // string with only one character "e"

v8  = [0.2, 2, 3.33, 4];    // 4-elements vector (of floats)
v9  = vector(16);           // 16-elements vector (of floats)
v10 = v9[0];                // float

v11 = {1, true, {12, 3} };  // array made of different types
v12 = array(12);            // array of 12 elements
v13 = v11[0];               // v13 = 1
v14 = v11[2][0];            // v14 = 12
v11[1] = false;             // now v11[1] is false

Operators

Operators are basically the same as in C language, with the addition of some cross-operations between integers, strings or vectors, which are allowrd in S3D language. For a complete specification refer to this document.

Swizzles

They are a convenient and meaningful way to access single components of a vector using keywords such as xyzw and rgba. A shortcut to generate a new vector containing the selected components in a specific order, including repetition The swizzle operator syntax is similar to object members access:

<vector_name>.<swizzle>

but the swizzle must not exceed 4 characters.

Allowed swizzle characters are:

x or r first component
y or g second component
z or b third component
w or a fourth component
_ or 0 ignore component

Some examples follow:

v1 = [ 1, 100, -10, -100 ];
// v1.zwx           corresponds to  [-10, -100, 1]
// v1.xxxy          corresponds to  [1, 1, 1, 100]
// v1.x_z or v1.x0z correspond to   [1, 0, -10]

col = [0.8, 0.4, 0.9, 1.0];
// col.rgb0         corresponds to  [0.8, 0.4, 0.9, 0.0]
// col.r            corresponds to  0.8

Swizzles can be used also in assignments to overwrite selected components:

v2 = [-2, 20, 200, -20] 

v2.x    = 5;            // one component change
                        // --> v2 = [ 5, 20, 200, -20]
v2.wy   = [3, 4];       // multiple components change
                        // --> v2 = [ -2, 4, 200, 3 ]
v2.x_z  = [3, 4, 5];    // multiple components change with
                        // no changes on some of them
                        // --> v2 = [ 3, 20, 5, -20 ]

Statements

Statements are basically the same as in C language. For a complete specification refer to this document.

The set statement

It is used to set the value of some system constants before the program actually starts. The allowed constants are:

  • SCENE_FOV — field of view of the camera.
  • SCENE_NEAR — near clipping plane distance (everything farther than this plane is not drawn).
  • SCENE_FAR — far clipping plane distance (everything closer than this plane is not drawn).
  • AUDIO_MODE — 3D Audio Setup mode:
    • 0 force software 3d capabilities;
    • 1 force hardware 3d capabilities;
    • 2 automatically get capabilities.

Example:

set SCENE_FOV = 50.0;

Basic Functions

It is out of the scope of this document to provide a documentation for most common basic functions. For a complete specification refer to this document.

XVR S3D Language Style Guide

A non-standard standardization attempt

This file is written to ease the developing of a project using XVR Studio 2.0, written in S3D language.

For the language documentation, check this other document.

Table of Contents

Identifiers Specification

XVR S3D language is case-insensitive, which means that no rule is enforced by language design to define identifiers for variables, functions or classes. However, since it cannot be morally acceptable to have function names as tHiSisAFUNcTioNNAmE() we decided that we will adopt the convention of writing code in CamelCase, like many modern language developers recommend (e.g. Pascal, Java, Microsoft .NET).

While it is out of the scope of this document to illustrate rules that CamelCased identifiers should follow, we will list a few ones:

  • variable and function identifiers (even methods) shall generally start with a lowerletter;
  • class identifiers shall generally start with an uppercase letter;
  • constant identifiers should be uppercase, with words separated by underscore characters;

A specialized rule for acronyms in identifiers not associated to constants is needed:

  • if the acronym is at the beginning of a variable or function identifier, then the whole acronym shall be lowercase (followed by a capitalized word if the identifer is not complete with the acronym alone);
  • otherwise, the first letter of the acronym shall be uppercase, the others should be lowercase, unless the acronym is only two characters long, in such case both letters should be either lowercase or uppercase.

The choice of writing (when possible) lowercase acronyms is to avoid problems when when multiple acronyms occur together (e.g. "parse DBM XML" would become "parseDBMXML") or when the standard requires a lowercase character (e.g. "SQL server" would become "sQLServer").

Following examples can clarify better most use cases:

// Following identifiers are valid for both
// variables and functions
var name, myName, myFullName, myHtmlName, 
    htmlName, ioName, myIOName;

// Following ones are valid class identifiers
class ClassName : MyHtmlClassName, MyIOClassName;

// Following ones are valid (standard)
// constants identifiers
set SCENE_FOV  = 60;
set SCENE_NEAR = 0.5;
set SCENE_FAR  = 1000;

Public and Private Identifiers

While the language does not provide direct support for public/private distinction for class attributes, we will just stick to the convention that private attributes/methods shall begin with an underscore character.

class MyClass
{    
    // public
    var length;
    var attribute;
    methodName(v1, v2);
    
    ...

    // private
    var _hidden;
    var _hiddenAttribute;
    // _hiddenMethodName(v1, v2);

    ...
};

Class declaration and definition shall follow this ordering rule:

  • public attributes
  • class constructors
  • class destructor (if defined)
  • public methods
  • private attributes
  • private methods

Always declare methods inside class declaration but define them somewhere else. The only motivation to define methods in C++ inside a class declaration is when the method shall be declared inline (all methods defined like that in C++ are implicitly inline), but S3D language does not provide inline support (it's just a scripting compiled language).

NOTE: since there is no actual support for private attributes, private methods could be defined in a way that they won't make class interface "dirty". The idea is to define some support functions in class definition file that accept the object on which they operate as first argument, like this:

function hiddenMethodName(this, arg1, arg2)
{
   ...
}

This is perfectly valid and clear, since the this keyword is not reserved in S3D language and visibility rules can enforce these method to be callable only from within public class methods or other support functions.

Notable exceptions

When building a new project using XVR Studio 2.0, some code may be auto-generated by the IDE. In such case it is probably a bad idea to rename everything to comply to the rules stated above.

The following exceptions are listed in the way they should be written:

  • variables:
    • CamPos
    • Light0
    • PosL
  • functions:
    • OnDownload()
    • OnInit()
    • OnDownload()
    • OnFrame()
    • SceneBegin()
    • SceneEnd()
    • DownloadReady()
    • OnTimer()
    • OnEvent
    • OnError
    • OnExit
  • classes:
    • any class beginning with 'cvm' characters shall be written as CVmClassName.

White Space

Code shall be easy to read and maintain (taking into consideration also performance tradeoffs when appropriate, see here). Thus, a correct white space usage will significantly improve the readability of the code.

Blank Lines

A careful use of blank lines between code “paragraphs” can greatly enhance readability by making the logical structure of a sequence of lines more obvious. This can help organize code into meaningful chunks

set LOWER = 0;
set UPPER = 300;
set STEP  = 20;

// The following function prints a
// Fahrenheit to Celsius conversion table
function fah2celsius()
{
    var fahr;

    for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
    {
        printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr - 32));
    }
}

However, overuse of blank lines can defeat the purpose of grouping and can actually reduce readability. Therefore, use a single blank line to separate parts of your program from one another.

Spacing

Appropriate spacing enhances the readability of lexical elements such as variables and operators. The following examples illustrate how to use individual spaces to improve readability and to avoid errors.

Put one space after a comma and between consecutive parenthesis to improve readability, as shown in the second and fourth example below.

average = total / count;

s3 = concat(s1, s2);

v1 = {1, true, {12, 3} };

Indentation

Use indentation to show the logical structure of your code. Research has shown that four spaces is the optimum indent for readability and maintainability. However, in highly nested code with long variable names, four-space indentation may cause the lines of code to overrun the end of the line. Use four spaces unless other circumstances make it unworkable.

function f()
{
    var c;

    c = getchar();
    while (c!= EOF)
    {
        putchar(c);
        c = getchar();
    }
}

Comments

Comments usage will significantly improve the readability of the code, thus use them whenever is needed to explain what the code is intended to do:

  • At class level, a comment can serve as class prolog;
  • At the function level, a comment can serve as a function prolog;
  • Throughout the file, where data are being declared or defined, it is helpful to add comments to explain the purpose of the variables.

Use comments to add information for the reader or to highlight sections of code. Do not paraphrase the code or repeat information that can be elementarly inferred from the code itself.

Comments come in various kinds:

  • Block comments — used as class or function prologs;
  • Short comments — used on the same line as the code or data definition they describe;
  • Inline comments — used at the same level of indentation as the code they describe;
  • Section separators.

In general, use short comments to document variable definitions and block comments to describe computation processes.

// Following one is a section separator
/******************************************************/

/*
 * This is a comment block. Write the comment text
 * here, in complete sentences.
 * Use block comments when they are used as prologs.
 */
function f()
{
    // This is an inline comment, while following ones
    // are short comments
    var ieeeReal = [];  // array of IEEE real values
    var ibmReal  = [];  // string of IBM real values
    var count;          // number of real values
    ...
}

As shown in the example, tabs shall be used to inser enough separation between code statements and comments, often indenting multiple comments on the same column.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment