The ANSI C Open Community Version 0.9 Application Program Interface

Richard C. Waters and David B. Anderson

This document is available as "http://www.merl.com/opencom/opencom-c-api.html".
This version of the document was created on February 19, 1997.

Copyright 1997, MERL - A Mitsubishi Electric Research Lab. All rights reserved.


1 introduction

This document describes the Open Community Version 0.9 Application Program Interface (API) as exported to ANSI C. It documents each publicly available object class and function.

This is a machine generated document created from a database of information. It exists in both paper and HTML form. Other documents describe the API exported to other programming language environments.

The API also includes the following ordinary data structures:

Finally, the API includes the following shared object classes:

1.1 Reading This Document

This document is a reference manual rather than a tutorial. In general, each topic is only discussed once and therefore any order of reading the sections in this document will not be quite right, because every section can be best understood only after having read many other sections.

The document is organized around the various classes enumerated above. At the top level, there is a section for each class. Within these sections, there are subsections corresponding to each instance variable and method.

There are two ways to look up instance variables and functions in this document. By using the table of contents, you can look them up by the name of the class. By using the quick reference index, you can look them up alphabetically by the name of the instance variable or method.

There are several quite different kinds of classes listed above: , passive data structures, and shared objects that are the basis of communication between processes.

The passive data structures (e.g., spTransform) are pieces of data that are stored in shared objects or used in intermediate computations. In the C interface, these items are not objects, but rather just simple vectors and structures.

The shared objects consist of the class sp and its subclasses. These classes receive specialized treatment by the system. In particular, instances of the shared classes are communicated between processes. Applications can define new shared classes using a Java interface supported by a special Java preprocessor.

Most of this document consists of descriptions of the shared classes. The first line of each section for a shared class shows the declaration used in the Java interface to define the class. This is followed by a brief discussion of the class and a listing of the externally available shared instance variables and functions in the class. Subsections of the section for a class describe each of its instance variables and functions in detail.

The first line of a subsection discussing an instance variable of a shared object shows the declaration used in the Java interface to define the variable. This line includes both the type of the variable in Java (in the middle) and the type in C (in a comment at the end of the line). From the perspective of this document, only the C type is relevant. The variable declaration line is followed by a general discussion of the instance variable and the various access functions available for the variable.

The first line of a subsection discussing a function is the ANSI C signature of the function. This is followed by a summary of what the function does.

The names of C functions in the API are derived using the following naming scheme. If a class spK contains a method M, then the corresponding C function has the name spKM. Since every shared class name begins with the letters ``sp'', every C function in the API begins with the letters ``sp''.

The sections on non-shared classes have much the same format as the sections on shared classes.

At various places in this document, topics of interest are discussed at length in subsections. One reason for gathering information into a separate section is so that it can be referred to from many places in the document.

1.2 System Overview

Above all else, the system provides a convenient architecture for implementing multi-user interactive environments. This architecture is centered on a world model that mediates all interaction. Figure 1 illustrates the application programming model. It shows five applications interacting through the world model.

Applications do not communicate directly with each other, but rather only with the world model. This allows applications to be written without thinking about how communication is achieved. An application does exactly the same things when it is interacting with an application running in shared memory on the same machine as it does when interacting with an application connected via the Internet.

Figure 1: The programming model.

The world model is not a scene graph, but rather an object-oriented database that does not consider one kind of content to be any more important than another. In particular, we believe that audio information and autonomous behavior are at least as important as visual information and should not be limited by constraints inherited from visual rendering.

The world model specifies what objects exist in the virtual world, where they are, what they look like, and what sounds they are making. The world model does not contain historical information, but rather just a snapshot of what the virtual world is like at the current moment. As the virtual world changes second by second, the world model changes.

The emphasis in the design of the world model is on the term database, not object oriented. The objects have methods associated with them, but by far the dominant operations consist of reading and writing data stored in the instance variables of world model objects.

Applications observe the virtual world by retrieving data from the world model. Applications affect the virtual world by adding, removing, and modifying objects in the world model. To avoid readers/writers conflicts, each object in the world model has one process as its owner and only the owning process can modify it. However, the ownership of an object can be transferred from one process to another.

By itself, the system does not cause objects to persist over time. An object exists only so long as the process that owns it runs. To have persistent objects, an application must provide persistent processes that accept ownership of these objects. These processes could make use of a persistent file format for objects to provide efficient long term support for infrequently visited parts of a virtual world.

1.2.1 Scalability

Application programmers are encouraged to think in terms of Figure 1. However, it would not work well to use a centralized architecture when actually implementing the system. Rather, the system operates as shown in Figure 2. To provide low latency interaction with the world model, the world model is replicated so that a copy resides in each application process. Messages sent over a computer network linking the processes are used to propagate changes from one world model copy to another.

Figure 2: The communication model.

A central feature of the system is that it is designed to be scalable to a large number of users (e.g., thousands) interacting in real time. Two key features support this: providing only approximate equality of local world model copies and dividing the world model into chunks each of which is communicated only to the small group of users that are actually interested in it, rather than to all the users of the world model.

Distributed databases typically require that all local copies of the database must agree exactly on the information in the database. However, this requires object locking and handshaking that is incompatible with real-time interaction if there are more than a very small number of users. In contrast, the focus here is on real-time interaction at all costs, providing only approximate equality between world model copies.

The primary way in which world model copies are only approximately equal is that different users observe things as occurring at slightly different times. We call this a relativity model of communication and is actually not unlike the real world. When you hear sounds from distant sources, you do not hear the sound that is being made now, but rather the sound that was made seconds ago. As a result, people in different locations do not hear the same things at the same time. How great the differences are depends on how far apart the sound sources and people are.

Similarly, when an application process finds out about a world model change, it is not finding out about a change that is happening now, but rather about one that happened some time ago. How long ago depends on the network distance between the two processes. In general, this distance is not more than a couple hundred milliseconds and does not lead to world model differences that are unduly large.

Having only approximate equality of world model copies allows real-time interaction, but does not of itself prevent the computation required to maintain each local world model copy from growing in proportion to the total number of simultaneous users of a virtual world. To prevent this, the world model is broken up into many small chunks called regions and information about a given region is communicated only to the small number of users that are near enough to that region to be interested in it. Each region is associated with a separate communication channel so that processes that are not interested in a region do not have to expend any processing ignoring it. This allows the system to scale based solely on the maximum number of users that are gathered in any one region, rather than on the total number of users in the virtual world.

1.2.2 Servers

A significant feature of Figure 2 is that it does not contain a central process. To minimize latency, and prevent bottlenecks, the primary communication used by the system is peer-to-peer rather than passing through centralized processes. However, centralized server processes are used for five key services. The way these servers interact with applications is illustrated in Figure 3.

Figure 3: Servers.

In the lower right of the figure is the session manager. It handles the connection of new users to an on-going session and their eventual disconnection. Its workload is proportional only to the number of users that enter or leave the session in a given minute, not the total number of connected users.

One or more servers are included in a Spline session to act as: user servers, region servers, beacon servers, and contact points. For flexibility, there is only one kind of server process, which can act in any of the above roles, or several at once.

The purpose of a user server is to support users with slow network connections (e.g., modems) that do not allow them to operate as first class peers. When acting in this role, a server intercepts all communication to and from the user. The message traffic to the user is compressed to take maximum advantage of the bandwidth available. As part of this, audio streams are combined and localized before sending them to the user. Servers are replicated as needed so that no one server has to support more users than it can handle. Two servers acting in this capacity are shown in Figure 3.

A region server maintains a record of everything in a given region. When the locus of attention of a user process enters a new region, the appropriate region server is queried in order to obtain initial information about the state of objects in the region. After this initial download, the user process obtains further incremental information by peer-to-peer communication. Responsibility for regions is parceled out among a set of servers, which is made large enough that no one server is responsible for a larger piece of the virtual world than it can easily handle.

Because the world model copy in an application process only contains information about the objects in the regions the process is attending to, there has to be an explicit mechanism for locating far away objects in the virtual world. This is done by having the servers provide a name service that makes it possible to locate specialized objects called beacons by name no matter where they are in the virtual world. When a user process wants to locate a particular beacon object, it consults the appropriate beacon server in order to find the beacon. As with regions, responsibility for the beacon name space is parceled out among a set of servers, which is made large enough that no one server is responsible for more beacons than it can easily handle.

To simplify the communication between user processes and the various servers it has to interact with, each user process has a server assigned to it that acts as a sole contact point for the process. Every message from a user process (or user server acting on its behalf) that requests a service, is sent to the contact point. This allows the user process to always operate as if there was only one server. The various complexities that arise when servers are replicated are handled by the contact point, which decides where to route the messages it receives from user processes.

The session manager monitors the servers in a session and if it detects that one has crashed, restarts it. This is facilitate by the fact that the user processes and the session manager are the only sources of information. The various servers merely act as repositories of information obtained from elsewhere and can straightforwardly be reinitialized if they need to be restarted.

As shown in Figure 3, the system utilizes a hybrid communication model in which client/server communication is primarily point-to-point but server/server communication is peer-to-peer. In addition, users with sufficiently fast network connections (e.g., users on corporate intranets) can interact directly with each other and the servers using peer-to-peer communication.

1.2.3 Application Processes

The structure of a single application process is shown in Figure 4. The dashed box at the top of the figure shows how the application itself fits into the picture.

Figure 4: An application process.

The foundation of the system is the inter-process communication module shown at the bottom of Figure 4. It provides all the processing necessary to maintain approximate consistency between the world model copies associated with a group of communicating processes, sending messages describing changes in the world model caused by the local application and receiving messages from other processes about changes made remotely. The network interface specifies the format of these messages. Any process that obeys this interface can interoperate.

The messages sent are of three kinds, corresponding to three kinds of data in the world model: small rapidly changing objects, large slowly changing objects, and continuous streams of data. An important feature of the system is that it includes an efficient scheme for synchronizing these different kinds of data.

The most prevalent kind of object in the world model is small things that can change rapidly. For example, an object representing something in the virtual world (e.g., a chair) requires only a small description---i.e., to specify its position and orientation, whether it is contained in some other object, and which appearance should be used when displaying it. The features of small objects can be changed very rapidly.

Messages describing changes in small objects are sent using User Datagram Protocol (UDP) messages. This allows them to be communicated very rapidly. The objects must be small enough so that a message describing one will fit in one UDP message.

Graphic models, recorded sounds, and behaviors are represented using large objects. These objects are identified by Universal Resource Locators (URLs) and communicated using standard World Wide Web protocols. Standard formats are used (e.g., VRML for graphic models, WAVE for sounds, and Java for behaviors) so that standard tools can be used. There is no limitation on the size of the large objects, but several seconds can be required to communicate one. Fortunately, since these objects change infrequently, this latency can generally be masked by preloading the objects before they need to be used.

The final kind of object in the world model corresponds to continuous streams of data such as sound captured by a microphone. These streams are communicated in small chunks using UDP messages. At the moment, video streams are not supported. When they are, they will be communicated in a similar fashion, but of necessity using larger messages.

A central feature of the system is that using the various messaging approaches above, every kind of data in the world model can be communicated between processes. Therefore, applications can modify and extend every aspect of a virtual world. Furthermore, while it is often advantageous to prestore data for an application to use (i.e., by delivering it on a CD-ROM) this is not necessary. Once started, an application will fetch everything it needs that has not been prestored.

1.2.4 The API

The Application Program Interface (API) described in this document consists primarily of operations for creating/deleting objects in the world model and reading/writing instance variables in these objects. The application support module (see Figure 4) contains various facilities that make application writing easier. For the convenience of application writers, multiple APIs are provided. The principle APIs being in ANSI C and Java.

1.2.5 Rendering

Figure 5 shows the system being used to support an application that interacts with a human user. The primary feature of the figure is that three applications are used in this situation. The main application (in the dashed box) presents an interface to the user.

Figure 5: Typical configuration supporting a user.

Visual and audio rendering modules are provided; however rather than being tightly integrated with the system, they are separate applications interfaced to the system. They use the same API as other applications and do not have to be tightly coupled with the main application.

One advantage of the loose coupling of renderers is that the renderers interacting with a person can run in separate processes from the main application. This allows them to operate on separate machines in situations where maximum performance is required. However, the primary mode of operation is for them to run in the same process with the main application, sharing a single copy of the world model as shown in Figure 5.

The greatest advantage of the loose coupling between rendering and the system core is that the system is not tied to any one renderer. Rather, the API is designed to be easily interfaced to almost any renderer. Default renderers are supplied with the system, but it is expected that demanding applications will switch to renderers that are tuned to the task at hand.

Consider visual rendering as an example. In the world model, objects can have positions, orientations, and appearances. The visual renderer creates a scene graph by combining the appearances associated with the objects that are near enough to be seen and renders the scene graph from the vantage point specified by the application. The system itself does nothing with the graphic models that describe the appearances of objects. The only thing that matters is that the visual renderer being used can load them.

Audio rendering is supported in a similar fashion. The world model contains objects representing sources of sounds. These objects specify the places where the sounds are located. They can either be point sources or diffuse ambient sources. Sound to be played through these objects can be captured live from a microphone or prestored in recorded sound objects. The audio renderer creates an audio scene graph by combining the sounds associated with sound source objects that are near enough to be heard and renderers this from the vantage point specified by the application. The system itself does nothing with sound encodings. The only thing that matters is that the audio renderer being used can decode them.

1.2.6 Supporting Simulations

From the earliest days of work on the system, we paid close attention to supporting interaction with computer simulations as well as people. The way this is done is shown in Figure 6.

Figure 6: Typical configuration supporting a simulation.

A simulation operates just like any other application using the same API to interact with the world model. However, no visual or audio rendering is needed, because there is no person to see or hear it. Complex simulations, intelligent agents, and large persistent databases can all be directly connected to a virtual world. Large powerful computers without support for graphics or sound can be used to manage shared content.

It is important to note that it is a great deal easier to say that a simulation interacts with the world model just like any other application than it is to make it possible for a simulation to effectively interact with the world model. The reason for this is that applications supporting a user have a human being in the loop and simulations do not.

For example, it is typically easy for a person to look at an avatar and determine which way the avatar is facing, based on a rendered image. However, it verges on impossible for a program to tell where the face of an avatar is by looking at a list of polygons. To deal with this kind of problem, the system has been designed to make information such as which way an avatar is facing easily accessible to programs. Specifically, Spline's world model contains an object class spAvatar that is used to identify avatars as opposed to other kinds of objects and by convention the center of the coordinate system for an object is at the center of the object, the Y axis is up, and objects face done the negative Z axis

1.3 Atomic Data

Much of the data used in the API is simple atomic data of 64 bits or less in length such as boolean values, integers, and floating point numbers. This data is copied whenever it is passed to or returned from a function.

The following paragraphs briefly describe each kind of atomic data used in the API. Some of these types of data are described at greater length in other sections.

Boolean values represented using the type spBoolean. These are used to indicate True and False values.

16-bit integers represented using the type short. These are used when low range integers are adequate.

32-bit integers represented using the type long. These are used for representing most integer values.

32-bit floating point numbers represented using the type float. In keeping with standard graphics practice, these are used to represent most non-integer values. They have limited precision, but are compact.

32-bit unique names represented using the type spName. The system dynamically generates names that are unique across all the processes participating in a session. These names are used both to identify shared objects and to identify the processes that own the objects.

IP addresses/port pairs represented using the type spAddress. These are used to represent network addresses. They contain two pieces of information, a 32-bit IP host address and a 16-bit port number. In C an spAddress is a struct containing the host address, followed by the port number. followed by 16 unused bits to pad the total field out to two full words.

32-bit time durations in milliseconds represented using the type spDuration. Whenever an API function takes a time duration as an argument, this argument is in terms of milliseconds. For compactness, 32-bit integers are used. This allows a range of approximately plus or minus two weeks. (This does not impose any limitation on the lifetime of a session in the virtual world, but rather only limits the maximum difference between two times that can be represented.)

World model view masks represented using the type spMask. These bit masks are used to control the visibility of shared objects in the world model.

Audio formats represented using the type spFormat. These values are used to represent audio encodings and sample rates.

1.4 Pointer Data

In addition to atomic data, the API makes use of several different kinds of pointer data. As discussed at length in the next section, the most important kind of pointer data is pointers to shared objects. However, several other kinds of pointer data are used as well.

A key feature of the API is that pointer data is always passed into and returned from functions by reference rather than copying. This promotes efficiency, but means you must pay close attention to the following.

1- The system never alters data that is passed to it via a pointer unless this document explicitly states otherwise. This means you can depend on the value remaining the same unless you change it yourself.

2- The system never retains a pointer passed to it beyond the time the function that was given the pointer returns. (If the system needs to keep any of the data, it copies it.)

3- When a pointer is passed to you, you must never alter the data pointed to unless it is explicitly stated that you should, because the system depends on the data not being altered. In particular, you must never free something referred to by a pointer unless you yourself allocated the storage.

4- It is allowed that you retain pointers returned by an API function, but only until the next time spWMUpdate is called. The reason for this is that the system assumes that during calls on spWMUpdate, it can free any storage it has allocated. The only exception to this is certain shared objects. If you want to save data across a call on spWMUpdate, you must copy it.

The following paragraphs briefly describe each kind of pointer data used by the system. Many of these types of data are described at greater length in other sections.

A UTF8 ASCII string represented using the type char *. This is a null-terminated string. If the characters are all 7-bit ASCII characters, then this is an entirely ordinary string. If extended characters are used, then special escape sequences are present.

An immutable spFixedAscii string containing no more than 500 characters represented using the type spFixedAscii. This is the same as the above, but is limited in length so that it can fit into a single UDP message. It is used for most shared character data in shared objects. This data must be specified when the object is initially created and cannot be altered afterward.

A 17-element position, orientation, and scaling vector represented using the type spTransform. This structure is used as the primary representation of the position and orientation of objects.

A 3-element vector represented using the type spVector. Vectors containing three values are used for several different purposes.

A 4-element quaternion represented using the type spQuaternion. Quaternions are one way of representing rotations.

A 4x4 transformation matrix represented using the type spMatrix. Following standard graphics practice, the position, orientation, and scaling of objects can be represented using a 4x4 matrix.

A locally defined operation represented using the class spFn. The API makes heavy use of callback functions and the mapping of functions over objects. For convenience, a single type is used to represent functional arguments in all these situations.

An interaction window represented using the type spWindow. Exactly what type this is depends on the execution environment.

An opaque pointer represented using the type void *. In a number of places, the system records pointers that are not part of the external interface and are not intended to be manipulated by applications.

1.5 Shared Objects

The central kind of data in the API consists of shared objects. These objects are stored in a world model and shared between the participants in a session. (Other data is shared only if it is stored in a shared variable of a shared object.) The key feature of shared objects is instance variables. They can have methods associated with them as well; however, the system makes relatively little use of these methods. Rather, shared objects are essentially passive, principally just representing a database of information.

1.5.1 Accessors

All interaction with instance variables in shared objects is via access functions, rather than via direct access to the instance variables or structure fields. In the external API, the number of access functions available depends on the visibility of the instance variable in the API. The following discusses the accessors in detail for an instance variable V in a shared class spK containing data of type T.

The following accessor obtains the value of the instance variable V for an object. It reports an error if the object from which the data is being obtained is not an instance of (a subclass of) the class spK.

T spKGetV(sp Object)

The following accessor is available for setting the value of V. A set accessor is available in the external API only if the external API allows the variable to be set. The Set accessor reports an error if the object to be modified is not an instance of (a subclass of) the class spK, if the object has been removed from the world model, or if the value being stored is a shared object that has been removed. If V is an instance variable that is shared between the processes in a session, then the Set accessor reports an error if the owner of the object is not equal to the current value of spWMGetMe.

void spKSetV(sp Object, T Value)

A few instance variables in shared objects are constant in nature in that they cannot be altered after an object is initially created. For example, this is true for variables whose values have the C type spFixedAscii. If a shared instant variable is not constant in nature, then the following accessor is available for obtaining the value of V prior to a change. (Specifically, the accessor obtains the value V had at the end of the last call on spWMUpdate.) The primary intended use of these accessors is in callback tests.

The accessor reports an error if the object from which the data is being obtained is not an instance of (a subclass of) the class spK. Obtaining an old value can take substantially more time than obtaining the corresponding current value.

T spKGetOldV(sp Object)

In the interest of saving space in this document, the discussions of individual instance variables in shared objects merely list which accessors are available with reference to this section for greater details.

It should be noted that for a given class spK, the accessors above are available not just for the variables directly defined in the class, but also for the variables that are inherited. For example, if spK inherits a variable V from a superclass spJ and the accessor spJSetV is available, then the accessor spKSetV is also available. In the interest of brevity, these inherited accessors are not explicitly listed in this documentation.

1.5.2 Referring To

Instance variables that point to shared objects are handled specially in a number of ways. This is necessary due to the partial and asynchronous way that the world model is copied between processes.

A very important feature of the API is that the local world model copy in a given process does not contain everything in the entire virtual world, but rather only a subset of the objects that are of local interest. As discussed elsewhere, the determination of subsets is based primarily on regions.

Because only some of the objects in the world model are in the local world model copy at a given moment, it is entirely possible that the local world model might contain an object A that refers to an object B (e.g., A's Parent might be B) and yet the local world model might not contain the object B.

Communication is arranged so that if two objects refer to each other, then in general they are both communicated in the same region. As a result, the kind of inconsistency described above seldom exists for long. However, it is very common for this kind of inconsistency to exist briefly for a variety of reasons.

(1) When the focus of interest moves into a new region, a process inevitably hears about some objects before others. (Because there are places where circularity of references is required, nothing can avoid the fact that this can cause temporary dangling references.)

(2) When new objects are created, a process inevitably hears about some objects before others.

(3) When objects are removed, a process may hear that an object is gone before hearing that other objects have stopped referring to it.

The above situations are handled by essentially putting the burden of a sanitized version of the problem on the application. When an instance variable contains a shared object, the access function that obtains the value (e.g., spGetParent) returns the object referred to only if the object is in the local world model copy. Otherwise it returns Null.

Suppose again that there is an object A in the local world model copy. If spGetParent returns non-Null when applied to A, then the return value is the Parent of A. However, if spGetParent returns Null, then this could mean either that A has no Parent or merely that the Parent of A does not yet exist in the local world model copy. (There is no way to tell the difference between these two things without inspecting details of A that are below the level of the even the internal API.)

Suppose that A does indeed have a Parent B and suppose further that A appears in the local world model before B. When B later appears, this registers in all respects just the same as if the Parent of A changed from Null to B. In particular, if there is a callback watching for such a change, it will be triggered.

The above approach works well because it does not matter to the typical application whether the Parent of A has changed or has merely just become known. An application has to be able to deal with arbitrary changes made by other processes, and doing this properly typically leads to a solution that operates properly in the presence of evolving partial knowledge as well. However, it is important to keep clearly in mind that a process's knowledge of the world model is always partial and therefore, it is never justified to draw more than merely provisional conclusions from the absence of something in the world model, since anything can appear during a call on spWMUpdate.

1.5.3 Removal

A key issue is asynchronous changes in the world model. In particular, objects owned by other processes can appear, change, and disappear from the world model any time spWMUpdate is called. In addition, if there are multiple threads in the local process, then from the perspective of a given thread, other threads can asynchronously create, modify, and remove locally owned objects.

A particularly good example of the problems related to asynchronous changes is what must be done to properly handle the asynchronous removal of objects. To start with, it is important to understand what happens when an object is removed.

First, the IsRemoved bit is set on in the object R being removed and all connections between R and other objects are broken. Specifically, any instance variable in any other object that refers to R is set to Null. In addition, any instance variable of R that refers to any other shared object is set to Null. A result of this is that having an object's Parent removed is treated very much the same as if the object's Parent was simply changed to Null.

After the first stage of removal above, one can still consult all the instance variables of R, with the proviso that the variables that point to other objects have all become Null. (For variables that are shared with other processes, you can consult the old values of the instance variables to find out what they used to point to.)

Second, some time later (exactly how much time later is discussed in detail below) the storage corresponding to object R is freed. After that time, one can no longer access any of the instance variables of R, and it can be disastrous to try. A key purpose of the first step of removal is to ensure that no other shared object can retain a pointer to a freed object. Applications must be sure that they don't either. There is a separation in time between the first and second stages of removal so that applications have time to notice when objects have been removed.

To deal with asynchronous object removal, an application must check on a regular basis whether any objects it is interested in have been removed and respond appropriately before the objects are freed. The system has carefully designed rules about when objects can be removed and freed in order to reduce the number of times an application has to check for an object's continued existence.

Between calls on spWMUpdate, objects are never removed unless they are explicitly removed by the local process. The basic result of this is that it is sufficient for a process to check for the continued existence of an object it is keeping track of once each time after spWMUpdate returns. This checking is done using the accessor spGetIsRemoved. Once an object has been removed, the local process should drop all pointers to it immediately, because they will very soon be invalid. (A particularly good way to do the checking above is to use an spJustRemoved callback on the object in question.)

Objects are never freed except during calls on spWMUpdate. In addition, if an object is removed after the beginning of a call on spWMUpdate it is not freed until the very end of the next call on spWMUpdate. This means that for every object, there will be at least one period between spWMUpdate calls where the object has been removed and not yet freed. This gives the application an opportunity to detect this fact and do something about it. (It also guarantees that the object stays around long enough for any spJustRemoved callbacks to get triggered.)

As a result of the above, C applications need not worry much about objects being asynchronously freed as long as they check that the objects are still in the world model once after each call to spWMUpdate (e.g., with callbacks).

In the Java interface, garbage collection and finalize methods ensure that shared objects are not freed as long as any pointers to them remain. In the C interface the function spWMRegister is used to obtain a somewhat similar level of protection. This can be used to make sure that the object pointed to from a given variable will never be freed and therefore it will always be valid to check whether the object has been removed.

An important special case is worthy of note. If the local process owns an object, then it can rely on the fact that the object will not get removed unless the local process removes it. This obviates the need for a significant amount of spGetIsRemoved checking in typical applications.

If the local process has multiple threads, then several problems arise. First, the other threads in the process can remove anything and so no object access can be considered entirely safe. Second, it is likely that some of the threads will not be synchronized with calls on spWMUpdate. It is difficult for these threads to know when they should check that objects still exist.

The most robust way to avoid problems with threads is to avoid having asynchronous threads manipulate shared objects directly, but rather have them communicate in some other way with the main thread in a process. Barring this, spJustRemoved callbacks are the best way to ensure safety.

1.6 Defining Shared Classes

No matter what API language is being used, shared objects are defined using Java with a few small extensions. (The following discussion assumes a basic understanding of Java.)

The extended Java syntax is supported by a preprocessor called SPOT. In general, SPOT takes in a Java file containing a shared class definition and produces:

SPOT operates in 3 basic modes: C-only, Java-only, and mixed.

In C-only mode, SPOT is used purely to extend the C API. In C-only mode, everything in the definition of a class must be native. (What this means for instance variables is discussed below.) In C-only mode outputs (2) and (3) are unnecessary.

In Java-only mode, SPOT operates purely to extend the Java API. In Java-only mode, classes can not contain any native elements. Things are arranged so that the shared class can be used from Java without having to link anything additional into the system core. In particular, outputs (1) and (3) are unnecessary.

In mixed mode, the class can contain native and non-native elements and all four outputs are produced so that the class can be used to maximum effect in both C and Java. The shared classes described in this document are defined solely using native elements and are processed using the mixed mode of SPOT so that they are available fully in both the Java and C APIs. An appendix at the end of this documentation contains the SPOT input corresponding to the entire API described in this document.

1.6.1 Example

The following example is used throughout the explanation below. It is contrived to illustrate many different features in a small space.

public class spExample extends spThing { //* [+SendToContact]
  shared public float[] Orientation;            //* [spTransform:17]
  shared public/protected spAction Agent;
  native int Timeout;                           //* [spDuration]

  native public static int New(float [] Orientation);
   //* [sp spExampleNew(spTransform:17 Orientation)]

  native public void Initialization();
   //* [void spExampleInitialization(sp Object)]

  native void SetTimeout(int Timeout);
   //* [void spExampleSetTimeout(sp Object, spDuration Timeout)]

  native public final void Setup(int Timeout, spAction Action);
   //* [void spExampleSetup(sp ExampleObj, spDuration Timeout, sp Action)]
}

For the most part, the class definition above is standard Java. The exceptions to this are the keyword `shared', the use of the keyword `native' in an instance variable declaration, the use of the syntax keyword/keyword, and the uses of the //* comments. (spThing and spAction are shared object classes. spTransform and spDuration are types in the C API.)

1.6.2 Shared Classes

A class is a shared class if and only if it is the root shared class sp or extends another shared class. (In the case above, spExample extends the shared class spThing.) The key feature of a shared class is that the objects that are created as instances of the class are shared between the processes participating in a session.

The definition of a shared class must use the keyword `extends' and can use the keywords `public' and/or `abstract'. However, it cannot use any other keywords. In particular, it cannot use the keyword `implements'.

The open brace that begins the body of a shared class definition can be followed by a comment beginning with `//*'. The text in the comment can begin with [+/-SendToRegion +/-SendToContact]. This specifies two key facts about the class: whether it is communicated in the normal region-based way and whether it is communicated directly to the contact point for a process, with `+' signifying yes and `-' signifying no. If either the SendToRegion or the SendToContact specification is omitted, it defaults to the value inherited from the class that is being extended.

The SendToRegion and SendToContact specifications are used in a few crucial places in the definition of the standard shared classes, but it is very unlikely that an application programmer would ever need to use these flags. The reason for this is that for any class that an application programmer is likely to define, the inherited values of these flags will be correct.

1.6.3 Native Instance Variables

The keyword `shared' specifies that an instance variable is shared between processes. When a shared variable is set in one process, the change is automatically reflected in all other processes. In contrast, variables that are not marked as shared exist in each process, but the values of non-shared variables are set separately by each process, with no effect on their values in any other processes.

The keyword `native' specifies that an instance variable is represented in C so that efficient methods exist for accessing it directly from C. (Shared implies native.)

The keyword native by itself is used often when defining the standard classes in the system, and would be used by someone extending the C API. It would never be used by someone who was solely extending the Java API.

Shared and native instance variables cannot be directly referred to as instance variables. (For instance, when using an instance X of the class spExample, one cannot write X.Agent anywhere.) Rather, a shared instance variable Var in the class spK is accessed solely through the use of automatically generated access methods.

Shared and native instance variables cannot redefine any variable in the class being extended. (This is because, for efficiency, the system assumes that none of the instance variables it uses can have their definitions changed.)

If the keyword shared or native is used, one can also use the access control keywords private, protected, and public. If an access control keyword appears, then the Get and Set methods have the specified access control. It is permissible to use the form Keyword/Keyword, in which case the Get method has the access limitations specified by the keyword before the / and the Set method has the access limitations specified by the keyword after the /. (The keyword before or after the / can be omitted with the meaning that the corresponding accessor can be used anywhere in the package.) For example, the Agent instance variable of spExample can be read by everyone, but only written by the methods of the spExample class and its subclasses.

If the keyword shared is used, it is not permissible to use the keywords static or final. However, it is permissible for a native variable to be static or static final.

If a native variable is specified to be static, it is processed by SPOT in the same way as a variable that is not static except that the methods created for accessing the variable are static. The only class in the API that uses native static variables is the class spWM.

If a native variable is specified to be static final, it acts as a constant that is known both to Java and C. It is referred to directly as a variable in both APIs, rather than being accessed via methods. In C native static final variables are #defines. An example of a native static final variable in the API is spDEGREES.

If the keyword shared or native is used, then it is not permissible to have an initialization expression, except for a variable that is native static final. Rather, one uses an Initialization method.

1.6.4 Scalar Types

If the keyword shared or native is used, then the data stored in the variable must be one of only a few permitted types. These types fall into two groups: scalar types and pointer types.

A shared or native instance variable can have as its type any of Java's eight primitive scalar data types (byte, short, int, long, float, double, char, and boolean).

Since each shared and native instance variable is represented in C, it must have a C type as well as a Java type. The following table shows the default C type associated with each of the scalar Java types.

The semicolon ending a shared or native instance variable declaration can be followed by a comment beginning with `//*' that specifies a non-default type to use for the variable in the C API. This specification has the form [type] where the type is a C type (defined separately in a C file). The only restriction on the C type is that it occupy the same amount of storage as the Java type. For instance, an int in Java is represented using 32 bits. Any 32-bit C type can be used in conjunction with it. Whenever they are passed as arguments to functions or passed between C and Java, scalar values are copied.

In general, SPOT does nothing special with individual types. It merely needs to know what types to use in Java and in C and what their sizes are. However, one scalar type gets special treatment.

If the C type of a shared or native instance variable is spBoolean, then the value is stored in a single bit using special internal variables of the class sp. However, once the bits reserved in these variables have been exhausted, then a whole byte is used for an spBoolean value.

1.6.5 Pointer Types

In addition too the eight Java scalar types, a shared or native instance variable can have one of the following pointer types.

As with scalar types, each of the pointer types above must be mapped to a C type. The correspondence is shown in the table below.

In C, all shared objects are referred to by pointers of type sp and no other C type can be specified. All functional arguments are referred to by pointers of type spFn and no other C type can be specified. Note that it is not possible to have a shared or native variable whose Java type is anything other than the types specified above.

For strings and arrays, there is no default C type. Rather, the C type must be specified using a `//*' comment. When specifying a pointer type, this comment begins with [type:size] where type must be a C type that is a pointer (e.g., float*) and where size specifies how many elements there are in the string or array. For instance, in the spExample class, the C type spTransform points to a vector of 17 floats.

When pointer types are passed as arguments to functions, they are passed whenever possible by reference via a pointer. However, when pointer types are passed between C and Java, the data is typically copied so that appropriate data structures can be maintained separately in C and Java.

As with scalar types, SPOT typically does nothing special with individual pointer types. It merely needs to know what types to use in Java and in C and what the memory size in C is. (For ease of allocation and communication, strings and arrays stored in shared or native variables are always stored in-line in the C representation for an object.) However, several pointer types get special treatment.

The shared object types and the type spFn get special treatment so that appropriate objects will be available efficiently in both Java and C. In addition, Strings get special treatment.

A difference between Java and C is that Strings in Java use Unicode characters while strings in C use ASCII characters. To accommodate this, Java strings are encoded as null terminated UTF8 strings when communicated to C. (If a string only contains 7-bit ASCII characters, this encoding leaves the bits unchanged.)

The C type spFixedAscii specifies a string of variable length that can only be set at the moment when a shared object is being created and cannot be changed later. To minimize the memory used for spFixedAscii values, they are placed in a special variable-sized part of an object. (A consequence of this is that it is not possible to obtain the old value of an spFixedAscii variable.)

The type spFixedAscii can only be used for shared variables as opposed to ones that are merely native. There can be at most one spFixedAscii variable in a shared object. Unlike other array-like types, no explicit size can be specified when using the type spFixedAscii.

The total size (in C) of all the shared instance variables must fit in a single UDP message (i.e., be less than 600 bytes or so). A warning is issued if this restriction is violated.

1.6.6 Native Static Final Variables

A shared or native variable has an initializer expression if and only if it is a native static final variable. If the initialization of the value in C needs to be different from the initialization in Java (e.g., because it is not just a numeric constant) then the C initialization can be specified after an `=' sign in the //* comment that specifies the C type of the variable. For example the definition of spDEGREES could be:
native static final public float DEGREES = Math.PI/180.0; //* [float=M_PI/180.0]

1.6.7 Methods

The methods in the definition of a shared class are specified using entirely standard Java code. However, for a method to be available in C, it must be native, or redefine one of a few special methods that the system core uses.

The specification of a native method in a shared class is obligatorily followed by a //* comment containing the signature of the C function that implements the method. The signatures are used by SPOT both to create appropriate C .h files and to produce appropriate stub files linking the Java and C API's.

In order to make it possible to create stubs correctly, types in the C function signature that correspond to arrays must obligatorily contain a specification of their sizes as illustrated below. This is the same convention that is used when specifying native or shared variables that contain arrays. In general, lengths must also be included for string types. However, it can be omitted if the string is null terminated.

  native public static int New(float [] Orientation);
   //* [sp spExampleNew(spTransform:17 Orientation)]

For the generation of stubs, the names of the arguments are used to determine the correspondence between the arguments of the Java and C signatures. The Java and C type of an argument of a native method must be one of the types that are valid for native variables. The corresponding C type of the argument must be one of the C types that are permitted to correspond to the Java type. Automatic conversions are performed when passing values between Java and C.

If a method is not a static method, then the object the method is applied to is passed to the C function via the first argument whose name is not the same as the name of any argument in the Java method. Otherwise, if an argument appears only in the C signature, it is passed the value 0. If an argument appears only in the Java signature it is not be passed to the C function.

In the standard API classes, the convention is followed that if a class spK has a method M, then the name of the corresponding C function is spKM, the arguments are in the same order, and the argument to the C function that receives the object the method is applied to is the first argument.

The following methods are interpreted in special ways. (Note that for all these methods, there can be at most one method with the indicated name in a given shared class definition.)

Initialization - As noted above, you cannot have initialization expressions for native variables. However, you can define a method called Initialization that initializes variables to any values you want. (Note that unlike merely using initialization expressions, this approach allows you to change the initialization of variables that are inherited from an ancestor class.) If no Initialization method is provided, SPOT defines one that does nothing.

New - You can define a static method called New that can be called from C to create an instance of the class. This method can take various arguments and initialize various values. If no New method is provided, then SPOT creates one with no arguments that does nothing other than call spClassNewObj, which in turn calls the Initialization methods for the class and every ancestor class (calling the ancestor methods first). (Operating in Mixed or Java-only mode SPOT generates a creation method for each shared class which calls the New method.)

C - A static method called C is automatically generated by SPOT. This method returns the class object corresponding to the class being defined. You cannot define a method with this name.

ReadData, Function, Predicate, Inside - These four methods are recorded in the class descriptor object (along with the Initialization method) so that they can be called by the system core.

Accessor methods - In general, there is no explicit mention in a shared class definition of the access methods corresponding to shared and native variables because these methods are generated by SPOT. However, If you include an explicit definition of an accessor method, it will override what SPOT would have created. In the definition of spExample above, this is the case for the method SetTimeout.

1.6.8 Example Revisited

Returning to the example above.

public class spExample extends spThing { //* [+SendToContact]
  shared public float[] Orientation;            //* [spTransform:17]
  shared public/protected spAction Agent;
  native int Timeout;                           //* [spDuration]

  native public static int New(float [] Orientation);
   //* [sp spExampleNew(spTransform Orientation)]

  native public void Initialization();
   //* [void spExampleInitialization(sp Object)]

  native void SetTimeout(int Timeout);
   //* [void spExampleSetTimeout(sp Object, spDuration Timeout)]

  native public final void Setup(int Timeout, spAction Action);
   //* [void spExampleSetup(sp ExampleObj, spDuration Timeout, sp Action)]
}

SPOT expects the following C functions to be defined separately:

extern sp spExampleNew(spTransform Orientation);
extern void spExampleInitialization(sp Object);
extern void spExampleSetTimeout(sp Object, spDuration Timeout);
extern void spExampleSetup(sp ExampleObj, spDuration Timeout, sp Action);

Operating in C-only or mixed mode, SPOT generates the following functions:

extern sp spExampleC();
extern spTransform spExampleGetOrientation(sp Object);
extern spTransform spExampleGetOldOrientation(sp Object);
extern void spExampleSetOrientation(sp Object, spTransform Orientation);
extern sp spExampleGetAgent(sp Object);
extern sp spExampleGetOldAgent(sp Object);
extern void spExampleSetAgent(sp Object, sp Agent);
extern spDuration spExampleGetTimeout(sp Object);

SPOT would have generated spExampleNew, spExampleInitialization, and spExampleSetTimeout if they had not been provided by the programmer. The generated spExampleNew would just call spClassNewObj with the class spExample as its argument. The generated spExampleInitialization would do nothing. The generated spExampleSetTimeout would merely set the value of the field.

1.6.8 Non-Shared classes

A Java file input to SPOT can define non-shared classes that are meaningful to Java and will be available in Java. These classes cannot use the keyword shared. However, they can have native static variables, native static final variables, and methods. These three constructs are handled in exactly the same way as in shared classes. In particular, the native variables are accessible in both C and Java, and SPOT automatically creates stubs so that the native methods are available in Java and C.

A Java file input to SPOT can define a non-shared class that does not contain anything that is either native or shared. When that is the case, the class has no relationship whatever to the system. However, anything that is acceptable to Java can be used in the definition of such a class.

A shared class can contain instance variables that are neither shared nor native. Such a variable can be specified in any way that is acceptable to Java. However, the variable will not be accessible from C.

In addition, a shared class can define non-native methods. However, except for the methods Initialization, ReadData, Function, Predicate, and Inside, non-native methods have nothing to do with the system and will not be called by the system or available from C.

2 spWM

The central data structure in the API is the world model. There can be only one world model object at a time. It contains the various shared objects that are communicated among a group of processes. The spWM type does not extend the class sp and does not correspond to an object in the world model.

To start up a process, the first thing one does is create the world model to operate on. When the process wishes to terminate, it should remove the world model. In between, the world model is repetitively updated to reflect changes in the objects in the world model caused by other processes.

The class spWM defines the following instance variables:

The class spWM defines the following functions:

2.1 Me

native public static int Me; //* [spName]

The following access functions are available:

spName spWMGetMe()
void spWMSetMe(spName X)

Processes are identified by 32-bit session-wide unique owner ids of type spName that are assigned by the session manager. The Me variable of the spWM object contains the owner id of the activity currently in control.

When the world model is created, the Me value is set to a main owner id assigned by the session manager. It can be changed at will later. Additional owner ids can be obtained using spWMGenerateOwner.

2.2 MainOwner

native public/ static int MainOwner; //* [spName]

The following access functions are available:

spName spWMGetMainOwner()

The MainOwner variable of the spWM object contains the owner id assigned to the local process by the session manager. It is set when the world model is initially created and cannot be altered later.

2.3 Error

native public static String Error; //* [char *:500]

The following access functions are available:

char * spWMGetError()
void spWMSetError(char * X)

The Error instance variable of an spWM object records the most recent error to have occurred during the evaluation of any API function. When the world model is created the Error value is set to Null. It is changed whenever an error occurs. You can set the Error value back to Null if you want to. However, it cannot be directly set to any other value, but rather only indirectly via spWMReportError. Error strings are limited to being no more than 500 characters long.

2.4 LastError

native public/ static String LastError; //* [char *:500]

The following access functions are available:

char * spWMGetLastError()

The LastError instance variable of an spWM object is identical to the Error instance variable except that it cannot be directly modified by an application, but rather only indirectly via spWMReportError. The LastError value can always be consulted to determine what the most recent error, if any, was.

2.5 Interval

native public/ static spDuration Interval; //* [spDuration]

The following access functions are available:

spDuration spWMGetInterval()

The Interval instance variable of a world model records the time in milliseconds between the ends of the last two calls on spWMUpdate. When the world model is created, the interval is set to zero. After the second and subsequent calls on spWMUpdate, the interval value is updated to reflect the timing of events. It must not be modified by an application.

To be precise, the interval value is updated just before action processing begins and reflects the interval in time between corresponding points in spWMUpdate calls. Note particularly, that the interval is calculated after any waiting that spWMUpdate does in order to achieve the desired interval.

2.6 DesiredInterval

native public static spDuration DesiredInterval; //* [spDuration]

The following access functions are available:

spDuration spWMGetDesiredInterval()
void spWMSetDesiredInterval(spDuration X)

The DesiredInterval instance variable of a world model controls the interval between calls on spWMUpdate as follows. When spWMUpdate is just about to begin processing actions, it determines the elapsed time since the last time actions were processed. If this time is less than the desired interval, then spWMUpdate waits until the interval is reached. If the elapsed time is greater than or equal to the desired interval, spWMUpdate proceeds without waiting. There is nothing that spWMUpdate can do to make processing take less time than it is taking. However, spWMUpdate ensures that the actual interval will not be less than the desired interval. Since there is a good deal of imprecision in the system's timing mechanisms, the system cannot guarantee that the actual interval will be equal to the desired interval; however, as long as the desired interval is long enough to be achievable, the system guarantees that the average error over time will be low.

When the world model is initially created, the desired interval is set to zero. This causes spWMUpdate to run as fast as possible without ever waiting. You can change the desired interval at any time.

It is important to think carefully about how often spWMUpdate gets executed. If it executes too often, time is wasted looking at data that has not changed in any useful way. Alternatively, If spWMUpdate executes infrequently, you will be operating on very stale data much of the time. Further, if spWMUpdate runs very infrequently (e.g., less than once a second or so) some of the information about world model changes will probably be lost as queues and buffers overflow. (Things are arranged, however, so that even if you never call spWMUpdate, the system will not crash.)

2.7 Window

native public static int Window; //* [spWindow]

The following access functions are available:

spWindow spWMGetWindow()
void spWMSetWindow(spWindow X)

The Window instance variable contains the current user interaction window. The exact type of the window object and the operations that can be applied to it depend on the operating environment---In Java they are one thing and in C under Windows95 they are another.

When the world model is first created, the Window is set to Null. The surrounding environment may force a particular value to be used subsequently. For instance, this is the case when using the system as a Netscape plugin. Otherwise, the application is free to create or choose a window to use.

2.8 spWMNew

spWM spWMNew(char * Server, spTransferVector V)

Creates the world model. As part of this, establishes a connection to a session via a session server. A process can only create one world model at a time.

The first argument is a DNS string that identifies the session server to connect to (e.g., "node.myUniv.edu"). If the server address string is Null, then all phases of initialization are performed except contacting the session server. This is useful for some kinds of single-user testing.

A process must create the world model before using any shared object operation. After initialization, the world model contains nothing but definitions of the built-in shared classes and a few internal objects that are not observable by applications.

The transfer vector is used to specify key internal operations (e.g., allocating memory) that may have special definitions that are required by the surrounding environment. In simple applications, Null is an acceptable value for this argument.

2.9 spWMRemove

void spWMRemove()

Eliminates the world model and disconnects the process from the session it is in. Among other things, this causes all of the objects owned by the process to be removed from the world model and ensures that all the other processes in the session are informed of this fact. A process should always call spWMRemove before terminating. Once spWMRemove has been called, a process cannot use any shared object operation unless it first creates a new world model.

2.10 spWMUpdate

void spWMUpdate()

Causes the local world model copy to be updated to reflect changes made by other processes. The fundamental operation of an application process is based on a cycle of: updating the world model (based on information received from other processes); running the application, waiting until the desired update interval has been reached, updating the world model again, and so on.

A number of important things happen each time spWMUpdate is called.

(1) Other processes are notified of changes made in the local world model copy only when spWMUpdate is called. This gives the application control over when other processes see changes. (For instance, one can ensure that several instance variables of an object will change simultaneously).

(2) The world model is brought up to date by processing messages from other processes. It is guaranteed that the world model will not change in any way between calls to spWMUpdate, unless the application makes the changes itself.

(3) Any actions, including Callbacks, in the world model are run. (This is the only time they are run.)

(4) As part of updating the world model, some objects may be removed from the world model.

2.11 spWMGenerateOwner

spName spWMGenerateOwner()

Activities in processes are identified by 32-bit session-wide unique ids of type spName. These owner ids are used to tag the objects created by different activities. The main owner id of a process is assigned by the session manager. Additional ids can be created by using the function spWMGenerateOwner. Each time this function is called it returns an additional owner id.

There are two kinds of owner ids: system and ordinary. spWMGenerateOwner returns ordinary owner ids. System owner ids are only used by the system core; there is no function in the API for creating them.

The purpose for having multiple owner ids in a single process is as the basis for controlling the visibility of objects via world model view masks.

2.12 spWMReportError

void spWMReportError(sp Object, long Code, char * Description)

The API utilizes a uniform approach to error reporting. This centers around the error reporting strings created by spWMReportError. Whenever an error occurs, spWMReportError is called and an error reporting string is stored so that it can be easily retrieved.

An error is created based on the data passed to spWMReportError as follows. The description string becomes the heart of the error report. It should be human readable, containing as much contextual information as possible. The system does not modify the descriptive string and does not retain a pointer to it.

The code is converted to ASCII and appended to the front of the description followed by a blank. The code should be a unique identifier of the error. (The codes for the errors reported by the system are all negative and every different error has its own unique error code. Applications are advised to use positive error codes.) Programs that want to handle errors can tell which error occurred by looking at the error code portion of the report string.

The error string created is stored so that it can be retrieved as the value of spWMGetError and spWMGetLastError.

As an example of the way error reporting is used, consider that a careful program that was not completely sure that the variable X contained an spThing, might retrieve the Transform from X as follows:

  spWMSetError(NULL);
  transform = spThingGetTransform(X);
  if (spWMGetError()) ...

2.13 spWMRegister

void spWMRegister(sp * Pointer)

After a shared object has been removed, the storage associated with it is eventually freed. However, if a pointer is registered using the function spWMRegister, then the system will never free an object that is pointed to by the pointer. This means that it will be safe to save an object in this pointer indefinitely. To stop protection, thereby allowing eventual freeing of the storage, call spWMDeregister.

The system essentially supports a restricted form of garbage collection where the function spWMRegister is used to specify exactly which pointers are taken into account for garbage collection purposes. Note that several threads can each register the same pointer and the pointer will be protected until after all the threads have deregistered it. Note also that it is pointers that are being protected, not objects per se.

2.14 spWMDeregister

void spWMDeregister(sp * Pointer)

Cancels the protection of a pointer started by spWMRegister. It is important to cancel registration when a pointer no longer needs to be protected. This is particularly true if the pointer is stack allocated and the function it is declared in is about to return, rendering the address of the pointer meaningless. Calling spWMRemove cancels all pointer registration.

3 spFn

An important part of the API is functions that map functional arguments over objects. There are three basic kinds of mapping functions.

The type spFn is used for the functional arguments to all the above functions. (A single type is used in all these situations because this is considerably more convenient than having to define a separate type for each purpose.) The spFn type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spFn data is stored in shared objects and used in intermediate computation.

In C, spFn is the following functional type:

typedef spBoolean spFn(sp Object, void * State)
The State argument is used to communicate state information between calls to an spFn. It can be used to accumulate a result. The return value is used for search-like operations. If an spFn ever returns True, then the mapping activity immediately halts. It is essential that an spFn be light weight in the sense that it runs quickly, and must not call spWMUpdate.

The following spFn could be used to count.

spBoolean spCount(sp ignore, void * state) {
  int * count = (int *)state;
  *count = *count+1;
  return FALSE;
}

For instance, the following code counts the number of children of an object X.

int Counter = 0;
spExamineChildren(X, spMaskNORMAL, spCount, &Counter);
Result = Counter;
The value of the count is obtained by observing the value of the state variable Counter after the examination is over.

Note that the state value that is passed to spExamineChildren and then on to the spFn spCount, is passed directly without copying. This is essential so that it can communicate information by side-effect. However, it means that the state must be in existance for the full period of time that spExamineChildren runs. This is not a problem for spExamineChildren, but requires more thought for operations like spExamineBeacons where the operate continues asynchronously for a potentially long period of time.

3.1 spFn Predicates

Another key part of the API is the ability to install callback functions that will automatically be called when certain events occur. Events are defined by predicates that test changes in the shared variables of objects. These predicates are defined using the same type spFn as functions that are mapped over objects.

When an spFn is used as a callback predicate, the return value is interpreted as specifying whether an event has occurred. Specifically, the predicate should return True or False depending on whether it observes that the event it tests for has occurred.

Callback predicates define events in terms of changes to shared instance variables. In particular, they compare the current state of these variables with the state of these variables at the end of the last call on spWMUpdate. (Since callback predicates are evaluated during each call on spWMUpdate, this ensures that any state change will be detected.)

The following shows an example of a simple callback predicate.

spBoolean spChangedParent(sp object, void * ignore) {
  return spThingGetOldParent(object) != spThingGetParent(object);
}

There are several key things to note about callback predicates. First, since callbacks are only applied to objects whose shared variables have changed, callback predicates are not called in situations where there has been no change in the shared instance variables of an object. Therefore, events must involve some change in these variables.

Second, the event generally must focus on changes in the shared instance variables of an object. The API does not provide any way to look at old values of local instance variables.

Third, callback predicates should not modify the object passed to them.

The API includes the following predefined callback predicates. Users can define any other predicates they want.

4 spMask

A fundamental feature of the API is the notion of a view mask for the world model. The purpose of these masks is to control the visibility of objects when using functions like spClassExamine. The spMask type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spMask data is stored in shared objects and used in intermediate computation.

The class spMask defines the following instance variables:

There are several kinds of objects that an application typically should not see. First, they should not see objects that exist purely for the use of the system core and are not part of the external API. Second, they should not see objects that are created by another activity and exist in the local world model copy only because this activity happens to be running as part of the same process, rather than in a separate process. (For instance, an application does not want to see objects owned by other activities that are not communicated to other processes listening in the regions the objects are in.) Note that an application also typically does not want to see objects that have been removed. However, this is no problem because objects that have been removed are not in the local world model copy and therefore are not encountered by an application.

A world model view mask (spMask) is an integer bit mask that controls what objects are viewable. A view mask is created by adding (or or'ing together) the constants above.

Before discussing the exact meaning of these constants, it is useful to consider a simple example. The following expression applies F to every spThing object that is not the private object of some other activity.

spClassExamine(spThingC(), spMaskNORMAL, F, NULL)

In contrast, the following expression applies F to every spThing object that is owned by the current activity. It does not apply F to any objects owned by other activities.

spClassExamine(spThingC(), spMaskMINE, F, NULL)

The following pseudo-code shows the reasoning applied in functions like spExamineChildren to determine whether an object is compatible with a view mask.

boolean CompatibleWithMask(sp object, int mask) {
  if (spGetOwner(object) == spWMGetMe()) {
    return (spMaskMINE & mask)
  }
  else {
    return (spMaskOTHERS & mask) &&
           (~ SystemOwner(spGetOwner(object))) &&
           ( spClassGetSendToRegion(spGetClass(object)))
  }
}

For a view mask to make any objects viewable, at least one of the spMaskMINE and spMaskOTHERS bits must be on. The three view masks constructible using spMaskMINE and spMaskOTHERS that view any objects are all of potential use to applications. The most common single case is spMaskNORMAL which combines spMaskMINE and spMaskOTHERS and views all ordinary objects. This is the default value for spMasks in most situations.

The functions spExamineChildren, spExamineDescendants, spClassExamine, and spClassMonitor all have view mask arguments that limit the objects that are viewed. Actions and callbacks also make use of view masks.

View mask restrictions do not apply to the access functions (such as spGetParent) for obtaining the value of instance variables. This is not a problem because, in general, you cannot get from objects you should see to ones you should not see. Objects with ordinary owners should never point to objects with system owners. Objects that are communicated via regions never point to ones that are not. Note that no object ever points to an object that has been removed.

4.1 Constants

View masks are created by combining the following constants. (In C these constants are #defines.)

As illustrated above, view masks can be created by adding or or'ing these constants together.

5 spTransform

The data type spTransform is used as the fundamental representation of the position, orientation, and scaling of objects. A key advantage of an spTransform is that the various components are easy to understand. The components are also well suited to interpolation. The spTransform type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spTransform data is stored in shared objects and used in intermediate computation.

The class spTransform defines the following instance variables:

The class spTransform defines the following functions:

An spTransform contains the same information as in a VRML transform node. Specifically, an spTransform is a vector of 17 floats representing 5 logical values as follows.

The first three elements are a vector representing the translation. The next four elements are an spRotation vector representing a rotation. The next three elements specify the amount of scaling along the X, Y, and Z axes with the value 1.0 indicating no scaling. The next four elements are an spRotation that is applied before the scaling is performed. The final three elements specify a center point that specifies the center point for both rotations.

As in VRML, the parts of an spTransform act together as follows. Note that because of the ScaleOrientation, the scale value can specify shear as well as scaling. It can be shown that the composition of any two spTransforms can be represented as an spTransform and the inverse of any spTransform can be represented as an spTransform.

(translate by Translation
 (translate by Center
  (rotate by Rotation
   (rotate by ScaleOrientation
    (scale by Scale
     (rotate by -ScaleOrientation
      (translate by -Center
       ...)))))))

By convention, Spline uses a right-hand coordinate system with the Y axis up and objects facing down the negative Z axis. (No assumptions are made about the relationship of the X and Y axes to compass directions.) It should be noted that different graphics modeling languages disagree with each other about these conventions. For instance, some have the Z axis up. The way spVisualDefinition links are specified makes it easy to use any kind of graphic model, without having to modify it.

The origin of the coordinate system for an object should be in the middle of the object unless there is a compelling reason otherwise. This is needed so that the InRadius and OutRadius of spThings will be meaningful. (An example of a compelling reason why the origin of an object would not be in the middle is that the origin of a subpart of an articulated form should be at the pivot point. This makes the mathematics of moving the subpart much easier.)

Any object that has a recognizable up direction should be oriented so that this up direction is parallel to the Y axis and points toward positive Y. Similarly, any object that has a recognizable front should have the front facing toward the negative Z axis. For instance, an object corresponding to the torso of an avatar would have the origin of its coordinate system in the middle of the chest, with the Y axis pointing up toward the head and the negative Z axis pointing out through the front of the torso. These axis conventions are needed both so that it is easy to combine different objects into a single scene and so that simulations that want to interact with objects can find where the tops and fronts of the objects are.

All units are in terms of meters and radians. Radians are used because they are more natural for numerical calculations. However, most people think more easily in degrees. In recognition of this, the following constant is provided.

Using this constant you can specify PI/2 radians (90 degrees) as follows.

90.0f * spDEGREES

In C, an spTransform is the following type.

typedef float * spTransform;

In addition, the following type is available for allocating memory for an spTransform. When calling a function that operates on spTransforms, one can pass in a variable that is either of the type spTransform or spTransformData.

typedef float spTransformData[17];
There are several levels at which you can interact with an spTransform. First, you can alter the underlying vector directly, e.g., using the index constants above. This allows you to do everything that is possible, but nothing easily.

Second, you can use the functions described in the following subsections to operate on spTransform objects. These make it easier to do complex operations. However, they require a clear understanding of transform interactions in order to figure out how to obtain a desired effect. In addition, these operations are static in the sense they call for the computation of particular spTransforms, rather than sequences of transform values over time.

Several conventions are worthy of note about the functions on spTransform objects. None of the functions ever allocates memory. Rather, the control of memory is left entirely up to the application. All modifications are by side-effect. However, to make it easier to create nested expressions the modified value is used as the return value.

A key complexity is that four different representations for rotations are supported. This is necessary because different communities of people are accustomed to different representations and justified because each representation has a situation where it is particularly convenient. The primary rotation representation is spRotation vectors such as those included in an spTransform. The second rotation representation is a vector of 3 Euler angles (in an spVector). The Third rotation representation is a vector of 4 floats representing a quaternion (spQuaternion). The fourth rotation representation is the rotation part of an spMatrix.

5.1 Constants

The following constants are available for directly accessing the various elements of an spTransform. (In C, these constants are #defines.)

For example, you might write.

spTransform P;
P[spTransformX] = P[spTransformY] + 2.0;

5.2 spTransformCopy

spTransform spTransformCopy(spTransform Destination, spTransform Source)

Copies the data from a Source spTransform to another, which is returned.

5.3 spTransformFromIdent

spTransform spTransformFromIdent(spTransform Transform)

Initializes the values in an spTransform so that they specify no translation, rotation, or scaling. That is to say, the spTransform is set to (0,0,0, 0,0,1,0, 1,1,1, 0,0,1,0, 0,0,0). It is important to initialize an spTransform before using it as a source of data, because the operations on spTransforms do not test that transforms are well formed before beginning their operations. In particular, they assume that the rotations in it are well formed.

5.4 spTransformGetTranslation

spVector spTransformGetTranslation(spTransform Transform)

Returns the Translation portion of an spTransform. In C, the spVector returned shares memory with the spTransform.

5.5 spTransformSetTranslation

spTransform spTransformSetTranslation(spTransform Transform, spVector Translation)

Sets the Translation portion of an spTransform. The other parts of the spTransform are not altered.

5.6 spTransformGetRotation

spRotation spTransformGetRotation(spTransform Transform)

Returns the Rotation portion of an spTransform represented as an spRotation. In C, the spRotation returned shares memory with the spTransform.

5.7 spTransformSetRotation

spTransform spTransformSetRotation(spTransform Transform, spRotation Rotation)

Sets the Rotation portion of an spTransform. The other parts of the spTransform are not altered.

5.8 spTransformGetScale

spVector spTransformGetScale(spTransform Transform)

Returns the Scale portion of an spTransform. In C, the spVector returned shares memory with the spTransform.

The three elements of the spVector contain the amount of scale along the X, Y, and Z axes respectively. The value 1.0 indicates no change in scale.

5.9 spTransformSetScale

spTransform spTransformSetScale(spTransform Transform, spVector Vector)

Sets the Scale portion of an spTransform. The other parts of the spTransform are not altered.

5.10 spTransformGetScaleOrientation

spRotation spTransformGetScaleOrientation(spTransform Transform)

Returns the ScaleOrientation portion of an spTransform represented as an spRotation. In C, the spRotation returned shares memory with the spTransform.

5.11 spTransformSetScaleOrientation

spTransform spTransformSetScaleOrientation(spTransform Transform, spRotation R)

Sets the ScaleOrientation portion of an spTransform. The other parts of the spTransform are not altered.

5.12 spTransformGetCenter

spVector spTransformGetCenter(spTransform Transform)

Returns the Center portion of an spTransform. In C, the spVector returned shares memory with the spTransform.

5.13 spTransformSetCenter

spTransform spTransformSetCenter(spTransform Transform, spVector Center)

Sets the Center portion of an spTransform. The other parts of the spTransform are not altered.

6 spVector

The type spVector is a 3-element vector of floats. It is used to represent several distinct things. The spVector type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spVector data is stored in shared objects and used in intermediate computation.

The class spVector defines the following instance variables:

The class spVector defines the following functions:

There are three major uses of vectors. The first is as an ordinary vector in Cartesian coordinates, with the three elements being the X, Y, and Z coordinates respectively. These vectors are used to specify translations, rotation axes, and center points.

The second use of vectors is as a set of Euler angles. In this use, the first element is a rotation around the X axis in radians; the second element is a rotation around the Y axis in radians; and the third element is a rotation around the Z axis in radians. Since rotations do not commute, it is important to realize that these correspond to first rotating around Z, and then rotating around Y, and lastly rotating around X.

The third use of vectors is as a representation for scaling. In this use, the three elements represent the amount of scaling in the X, Y, and Z axes respectively. A value of 1.0 in an element specifies no scaling.

In C, an spVector is the following type.

typedef float * spVector;

In addition, the following type is available for stack allocating memory for an spVector. When calling a function that operates on spVectors, one can pass in a variable that is either of the type spVector or spVectorData.

typedef float spVectorData[3];

Several conventions are worthy of note about the functions on spVectors. None of the functions ever allocates memory. Rather, the control of memory is left entirely up to the application. All modifications are by side-effect. However, to make it easier to create nested expressions the modified vector is used as the return value.

6.1 Constants

The following three constants are provided for accessing the components of an spVector. (In C, these constants are external variables initialized to appropriate values.)

For example, you might write.

spVector V;
V[spVectorX] = P[spVectorY] + 2.0;

The following four constants contain particular vectors that are useful as arguments to various API functions. (In C, these constants are external variables initialized to appropriate values.)

For example, you might write.

spRotation R;
spRotationLookAt(R, spVectorZERO, spVectorAXISX, spVectorAXISY);

6.2 spVectorCopy

spVector spVectorCopy(spVector Destination, spVector Source)

Copies a vector into another.

6.3 spVectorSetFromScalar

spVector spVectorSetFromScalar(spVector Vector, float Scalar)

Sets all three elements of an spVector to be equal to a given Scalar.

6.4 spVectorEquals

spBoolean spVectorEquals(spVector A, spVector B)

Returns True if two vectors are component-by-component equal within a small system-defined tolerance.

6.5 spVectorEqualsDelta

spBoolean spVectorEqualsDelta(spVector A, spVector B, float Tolerance)

Returns True if two vectors are component-by-component equal within a tolerance specified by the user.

6.6 spVectorAdd

spVector spVectorAdd(spVector A, spVector B)

Adds two vectors together and stores the result in the first vector.

6.7 spVectorSubtract

spVector spVectorSubtract(spVector A, spVector B)

Subtracts a second vector from a first vector and stores the result in the first vector.

6.8 spVectorMultiplyByScalar

spVector spVectorMultiplyByScalar(spVector Vector, float Scalar)

Modifies a vector by multiplying each element by a scalar.

6.9 spVectorDivideByScalar

spVector spVectorDivideByScalar(spVector Vector, float Scalar)

Modifies a vector by dividing each element by a scalar.

6.10 spVectorCrossProduct

spVector spVectorCrossProduct(spVector A, spVector B)

Computes the cross product of two vectors and stores it in the first vector.

6.11 spVectorDotProduct

float spVectorDotProduct(spVector A, spVector B)

Computes the dot product of two vectors and stores the result in the first vector.

6.12 spVectorComposeScales

spVector spVectorComposeScales(spVector A, spVector B)

Modifies an spVector by multiplying each element by the corresponding element of another spVector.

6.13 spVectorLength

float spVectorLength(spVector Vector)

Computes the length of a vector. That is to say, the square root of the sum of the squares of the elements.

6.14 spVectorNormalize

spVector spVectorNormalize(spVector Vector)

Converts a vector into a unit length vector pointing in the same direction. That is to say, divides each element by the length of the vector.

7 spRotation

The principle way that rotations are specified is in terms of a rotation axis passing through the origin and a rotation angle about this axis in radians. Typically, the rotation angle is between plus or minus PI. A key advantage of an spRotation is that the various components are easy to understand. In addition, because the components are relatively independent, they are well suited to interpolation. The spRotation type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spRotation data is stored in shared objects and used in intermediate computation.

The class spRotation defines the following instance variables:

The class spRotation defines the following functions:

An spRotation is a vector of 4 floats consisting of two parts as follows.

The first three elements are a vector representing an axis through the origin to rotate about. The last element is an amount of rotation in radians.

In C, an spRotation is the following type.

typedef float * spRotation;

In addition, the following type is available for allocating memory for an spRotation. When calling a function that operates on spRotations, one can pass in a variable that is either of the type spRotation or spRotationData.

typedef float spRotationData[4];

Several conventions are worthy of note about the functions on spRotations. None of the functions ever allocates memory. Rather, the control of memory is left entirely up to the application. All modifications are by side-effect. However, to make it easier to create nested expressions the modified value is used as the return value.

7.1 Constants

The class spRotation includes the following constants for accessing the X, Y, Z, and Angle components of an spRotation. (In C, these constants are #defines.)

For example, you might write.

spRotation R;
R[spRotationX] = A[spRotationY] + 2.0;

7.2 spRotationCopy

spRotation spRotationCopy(spRotation Destination, spRotation Source)

Copies the data from a Source spRotation to another, which is returned.

7.3 spRotationFromIdent

spRotation spRotationFromIdent(spRotation Rotation)

Initializes the values in an spRotation so that they specify no rotation about the Z axis. That is to say, the spRotation is set to (0,0,1,0). It is important to initialize an spRotation before using it as a source of data because the operations on spRotations do not test that rotations are well formed before beginning their operations. In particular, they assume that the axis does not have zero length.

7.4 spRotationGetAxis

spVector spRotationGetAxis(spRotation Rotation)

Returns the Axis portion of an spRotation. In C, the spVector returned shares memory with the spRotation.

7.5 spRotationSetAxis

spRotation spRotationSetAxis(spRotation Rotation, spVector Axis)

Sets the Axis portion of an spTransform. The angle is not altered.

7.6 spRotationGetAngle

float spRotationGetAngle(spRotation Rotation)

Returns the Angle in an spRotation.

7.7 spRotationSetAngle

spRotation spRotationSetAngle(spRotation Rotation, float Angle)

Sets the Angle in an spTransform. The axis is not altered.

7.8 spRotationToQuat

spQuaternion spRotationToQuat(spRotation Rotation, spQuaternion Quat)

Computes the spQuaternion corresponding to an spRotation.

7.9 spRotationFromQuat

spRotation spRotationFromQuat(spRotation Rotation, spQuaternion Quat)

Computes the spRotation corresponding to an spQuaternion.

7.10 spRotationToAngles

spVector spRotationToAngles(spRotation Rotation, spVector Vector)

Computes the Euler angles corresponding to an spRotation.

7.11 spRotationFromAngles

spRotation spRotationFromAngles(spRotation Rotation, spVector Angles)

Computes the spRotation corresponding to a vector of Euler angles.

7.12 spRotationMult

spRotation spRotationMult(spRotation A, spRotation B)

Computes an spRotation that represents the composition of two spRotations. Specifically, an spRotation A is modified to represent the effect of applying a second rotation B after A. Note that this means that B is effectively multiplied on the left, not the right.

7.13 spRotationLookAt

spRotation spRotationLookAt(spRotation R, spVector From, spVector To, spVector Up)

Computes the absolute spRotation that should be used to view one position from another. In particular, an spRotation R is computed so that if the rotation is used for an object A at the From position, then the negative Z axis of A's coordinate system points to the specified To position, and A's Y axis and the specified Up vector are co-planar. Typically, the Up vector is chosen to be parallel to the Y axis of the main coordinate system. This causes objects that are upright to appear upright if the rotation is used to position an spSeeing beacon.

The rotation that is the target of spRotationLookAt is modified by filling it in with the viewing rotation and then returned.

8 spQuaternion

Several different representations for rotations are supported. One of these is spQuaternion, which is a 4-element vector of floats representing a quaternion. There are very few functions in the class spQuaternion, because spRotation is much more central to the API. The spQuaternion type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spQuaternion data is stored in shared objects and used in intermediate computation.

The class spQuaternion defines the following functions:

The four elements of an spQuaternion represent a quaternion number. (The vector is required to represent a unit quaternion, I.e., the sum of the squares of the elements of an spQuaternion is 1.0.)

q[0]*i + q[1]*j + q[2]*k + q[3]

In C, an spQuaternion is the following type.

typedef float * spQuaternion;

In addition, the following type is available for stack allocating memory for an spQuaternion. When calling a function that operates on spQuaternions, one can pass in a variable that is either of the type spQuaternion or spQuaternionData.

typedef float spQuaternionData[4];

8.1 spQuaternionCopy

spQuaternion spQuaternionCopy(spQuaternion Destination, spQuaternion Source)

Copies the data from a Source spQuaternion to another, which is returned.

8.2 spQuaternionFromIdent

spQuaternion spQuaternionFromIdent(spQuaternion Quaternion)

Initializes the values in an spQuaternion so that they specify no rotation. That is to say, the spQuaternion is set to (0,0,0,1). It is important to initialize an spQuaternion before using it as a source of data because the operations on spQuaternions do not test that the spQuaternions are well formed before beginning their operations.

8.3 spQuaternionMult

spQuaternion spQuaternionMult(spQuaternion A, spQuaternion B)

Computes an spQuaternion that represents the composition of two spQuaternions. Specifically, an spQuaternion A is modified to represent the effect of applying a second rotation B after A. Note that B is effectively multiplied on the left, not the right.

9 spMatrix

Following standard graphics practice, the positions, orientations, scaling, and shear of objects can be represented using 4x4 transformation matrices called spMatrix objects. These matrices are also capable of representing other effects such as taper, however, because spTransforms are used as the fundamental representation, these additional effects cannot be used. The various operations on spMatrix objects assume that the matrix corresponds purely to translation, rotation, scaling, and shear. The spMatrix type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spMatrix data is stored in shared objects and used in intermediate computation.

The class spMatrix defines the following functions:

An spMatrix is a matrix of the following form. The upper right hand quadrant is a 3x3 rotation matrix specifying orientation, scaling, and other effects. The last column specifies translation (i.e., position).

R R R X
R R R Y
R R R Z
0 0 0 1

An spMatrix is stored in column major order as a vector of 16 floats. In C, an spMatrix is the following type.

typedef float * spMatrix;

In addition, the following type is available for stack allocating memory for an spMatrix. When calling a function that operates on spMatrix objects, one can pass in a variable that is either of the type spMatrix or spMatrixData.

typedef float spMatrixData[16];
Several conventions are worthy of note about the functions on spMatrix objects. All of the functions assume that a spMatrix represents only rotation, translation, scalling, and shear with no other effects like taper or reflection. None of the functions ever allocates memory. Rather, the control of memory is left entirely up to the application. All modifications are by side-effect. However, to make it easier to create nested expressions the modified value is used as the return value.

9.1 spMatrixCopy

spMatrix spMatrixCopy(spMatrix Destination, spMatrix Source)

Modifies one matrix to equal another.

9.2 spMatrixFromIdent

spMatrix spMatrixFromIdent(spMatrix Matrix)

Modifies an spMatrix to be the identity matrix. The identity matrix is one where the rotation matrix is the identity matrix specifying no changes and there is no translation. That is to say, the spMatrix is set to (1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1). It is important to initialize an spMatrix before using it as a source of data because the operations on spMatrix objects do not test that the matrices are well formed before beginning their operations.

9.3 spMatrixGetTranslation

spVector spMatrixGetTranslation(spMatrix Matrix)

Returns the translation component of an spMatrix. In C, the spVector returned shares memory with the spMatrix.

9.4 spMatrixSetTranslation

spMatrix spMatrixSetTranslation(spMatrix Matrix, spVector Translation)

Sets the translation component of an spMatrix, leaving the rest of the spMatrix unmodified.

9.5 spMatrixFromTransform

spMatrix spMatrixFromTransform(spMatrix Matrix, spTransform Transform)

Modifies a matrix so that it has the translation, rotation, scaling, and shear specified by an spTransform.

9.6 spMatrixToTransform

spTransform spMatrixToTransform(spMatrix Matrix, spTransform Transform)

Computes the spTransform that corresponds to an spMatrix and stores the result in the indicated spTransform. (It should be noted that this operation is computationally quite expensive and should be avoided if possible.)

Not every spMatrix can be converted into an spTransform. For example, an spTransform that specifies taper cannot be. However, any spMatrix that can be computed by using the functions provided here can be converted into an spTransform. The only exception to this is if the individual elements of an spMatrix are set directly. This is not recommended.

9.7 spMatrixInverse

spMatrix spMatrixInverse(spMatrix spMatrix)

Modifies a matrix so that it becomes the inverse of its previous value. That is to say so that it specifies an opposite rotation about the same axis, inverse scaling, and the negation of the translation. The product of a matrix and its inverse is the identity matrix.

9.8 spMatrixMult

spMatrix spMatrixMult(spMatrix A, spMatrix B)

Computes the product of two matrix objects. This is the sum of the translations, the product of the scaling and the result of performing one rotation after the other. A matrix A is multiplied by another matrix B (with A on the left and B on the right) and A is modified to contain the result and returned.

9.9 spMatrixMultVector

spVector spMatrixMultVector(spMatrix Matrix, spVector Vector)

Multiply an spMatrix times an spVector to determine what the transformed vector is. This determines the result of the transformation specified by an spMatrix on the given vector. The result is stored in the argument vector and returned.

As an example, the following code shows how to calculate a vector Up in the local coordinate system of an spThing A that corresponds to the global up direction spVectorAXISY. The code multiplies the spMatrix relating the topmost coordinates to A's coordinates by a copy of spVectorAXISY to determine a vector in A's coordinates that corresponds to up.

  spMatrix M;
  spVector Up;
  Up = spVectorCopy(Up, spVectorAXISY);
  spMatrixMultVector(spThingRelativeMatrix(A, spTopmost(A), M), Up);

10 spFormat

The spFormat type does not extend the class sp and does not correspond to objects in the shared world model. Rather, spFormat data is stored in shared objects and used in intermediate computation. spFormats specify various sound data formats including sampling rate and encoding.

The class spFormat defines the following instance variables:

The class spFormat defines the following functions:

In C, An spFormat is the following structure. The various fields can be independently manipulated; however, it is primarily intended that applications use a small set of constant values and the small number of methods described below.

typedef struct _spFormat {
  unsigned short rate;             
  short encoding; 
  unsigned short bitsPerSample; 
  unsigned short samplesPerFrame; 
} spFormat;

The rate specifies how many times per second the sound data is sampled. Popular rates include 8000, 16000, 32000, and 44100. The encoding (an integer code) specifies what kind of data encoding is used. The bit samples specifies how many bits (e.g., 8, 16) are in each sample. The samples per frame specifies how many channels of data are represented (e.g., 1 (mono), 2 (stereo), 4 (quad)).

All sound data accessible to applications is linearly encoded with 16 bits per sample. (More compact encodings are used when communicating between processes.) Typically, sound is output through spAudioSources in mono and rendered through a user's headphones in stereo.

10.1 Constants

The following constants contain useful spFormat values. (In C these constants are #defines.)

10.2 spFormatDurationFromLength

spDuration spFormatDurationFromLength(spFormat Format, long Bytes)

Computes the duration in milliseconds (rounded up to the nearest millisecond) of the specified number of bytes of sound data stored using the indicated format.

10.3 spFormatLengthFromDuration

long spFormatLengthFromDuration(spFormat Format, spDuration Duration)

Computes the number of bytes required to represent the specified number of milliseconds of sound data using the indicated format.

11 sp

public abstract class sp //* [+SendToRegion -SendToContact]

This is the root of the shared object hierarchy. The class is abstract in the sense that applications do not create objects of the class sp. However, the functions associated with this class are applicable to every shared object.

The class sp defines the following instance variables:

The class sp defines the following functions:

It is not possible to create instances of the class sp. Rather, one can only create instances of particular subclasses of the class sp.

11.1 Constants

All angles are represented in radians. Radians are used because they are more natural for numerical calculations. However, most people think more easily in degrees. In recognition of this, the following constant is provided. (In C, this constant is a #define.)

Using this constant you can specify PI/2 radians (90 degrees) as follows.

90.0f * spDEGREES

11.2 Class

shared public/ spClass Class; //* [sp]

The following access functions are available:

sp spGetClass(sp Object)

Each shared object has a shared instance variable called its Class that contains an spClass object describing the local and shared instance variables associated with the object.

The Class of an object is set when it is initially created and cannot change after that time. Information about the Class is shared between the processes in a session.

11.3 Owner

shared public int Owner; //* [spName]

The following access functions are available:

spName spGetOwner(sp Object)
void spSetOwner(sp Object, spName X)

An important feature of the API is that every shared object has a single owner and the owning activity is the only process that can modify any of the shared data associated with the object. This avoids readers/writers conflicts when using shared objects. The only exception to the above is that a process can create an spAction object that can run in other processes and remotely modify objects that are owned by the same process as the spAction.

Each shared object has a shared instance variable called its Owner that contains the unique id of the activity that owns it. The name space of owner ids is the same as the name space of object ids. Owner ids are created using the function spWMGenerateOwner. To determine whether you are the owner of an object X perform the test:

spGetOwner(X) == spWMGetMe()

The Owner of a shared object is set to the value of spWMGetMe when the object is created. After that time, the Owner of an object is free to change the Owner to a different owner id, thereby giving up ownership of the object to another process. A process must be careful to set the Owner to a valid owner id. If not, the object will be irrecoverably lost in limbo, until it eventually times out. Information about the Owner is shared between the processes in a session.

A process can request that ownership of an object be transferred to it by creating an spOwnershipRequest object. The owner may or may not satisfy the request.

11.4 Parent

shared public sp Parent; //* [sp]

The following access functions are available:

sp spGetParent(sp Object)
void spSetParent(sp Object, sp X)

A key feature of shared objects is that they can be hierarchically arranged. For example, an articulated humanoid figure might be constructed so that the top level object corresponds to the torso. The torso might have a head, two upper arms, and two thighs attached to it. Each upper arm might have a lower arm attached to it and so on.

Hierarchical relationships between shared objects are represented by using a shared instance variable called the Parent. In the example above, the lower arms of the humanoid figure would have as Parents the corresponding upper arms which would have as their Parents the torso.

An equally important feature of the Parent of an object is that it determines what region the object is in. This in turn determines how the object is communicated, because information about an object is sent only to the region it is in. Therefore, if the Parent of an object is Null, information about it will not be sent anywhere.

There are no explicit access functions for the inverse of the Parent relationship. However, this information can be indirectly obtained using the functions spExamineChildren and spExamineDescendants.

The Parent of a shared object must be another shared object, or Null, meaning that there is no Parent. When an object is created, its Parent is initialized to Null. Information about the Parent is shared between the processes in a session.

11.5 IsRemoved

shared public/ boolean IsRemoved; //* [spBoolean]

The following access functions are available:

spBoolean spGetIsRemoved(sp Object)

The local instance variable IsRemoved has the value True if and only if the object has been removed from the local world model copy. It is still permissible to look at the values of the instance variables of an object after the object has been removed. (This is important in callbacks.) However, one has to be very careful about retaining pointers to objects that have been removed, because the system frees the storage corresponding to an object soon after it is removed.

The IsRemoved bit is set to False when an object first appears in the world model. It is set to True when the object is removed. The IsRemoved bit must never be altered in any other way. Information about the IsRemoved bit is shared between the processes in a session.

By comparing the current and old values of the IsRemoved bit, one can determine whether an object was recently removed. In particular, if the current value is True while the old value is still False, then the object was removed since the end of the most recent call on spWMUpdate to have been completed.

11.6 IsNew

native public/ boolean IsNew; //* [spBoolean]

The following access functions are available:

spBoolean spGetIsNew(sp Object)

The local instance variable IsNew has the value True if and only if the object appeared in the local world model copy since the end of the last call on spWMUpdate.

The IsNew bit is set to True when an object first appears in the world model. It is set to False at the end of the next call on spWMUpdate. Information about the IsNew bit is maintained separately in each process.

11.7 AppData

native public int AppData; //* [void *]

The following access functions are available:

void * spGetAppData(sp Object)
void spSetAppData(sp Object, void * X)

Every shared object has a local instance variable called AppData, which applications can use to store any 32-bit quantity they want to. This can be very convenient in many situations. However, note that if you store pointers to dynamically allocated structures, you must carefully monitor the removal of objects (e.g., with a callback) or risk having a memory leak due to the allocated structures not getting freed when objects are removed. (Since the system does not understand anything about what is in this variable, it cannot take any action to prevent memory leaks.) In addition, if two applications running on the same machine and sharing the same world model copy both try to use this variable, severe problems will arise unless they cooperate closely.

When an object is created, the AppData value is set to Null. Applications can do whatever they want with it after that time. Information about the AppData is maintained separately in each process.

11.8 spC

sp spC()

Given an object, one can obtain the descriptor of the object's class by using the accessor spGetClass. This is all that is needed in many situations. However, it is also often convenient to be able to obtain the description of a particular class even though you do not have any particular instance of the class. For example, you might want to use spClassExamine to find any instances that exist.

For each class spK, the system automatically generates a function spKC with no arguments that returns the class descriptor for the class. For example the class sp includes the function spC, which can be used as follows:

spClassExamine(spC(), spMaskNORMAL, F, NULL)

11.9 spNew

sp spNew()

With regard to constructing objects, shared classes fall into three categories. A couple of classes (such as sp and spLink) are abstract in the sense that they merely group together some useful operations or specify a pattern that must be specialized before it can be used. It is not possible to construct instances of these classes. (As a result, the function spNew does not actually exist. It is described here in order to present what New functions must look like for shared classes that are not abstract.)

For most classes spK, instances are created using an automatically generated zero-argument constructor named spKNew. When this is the case, no special comment is made in this documentation beyond noting the existence of the constructor. The way spKNew is implemented is shown below.

sp spKNew() {
  return spClassNewObj(spKC(), 0);
}

Some classes (for example spRegion) have constructors that take arguments. In this case, the constructor is not automatically generated. Nevertheless, it must begin by calling spClassNewObj as in the automatically generated constructor shown above.

Whenever a special New function exists, this document contains a subsection describing it. A key reason for having constructors with arguments is that a number of classes have instance variables that must be set at the moment an object is created and cannot be changed later.

11.10 spInitialization

void spInitialization(sp Object)

Initializes the values of instance variables. Whenever an instance of a shared class is created, by a New function, The values of the instance variables are all set to all bits zero. After this, the Initialization functions are called for the class and all its ancestors in order to properly initialize any variables that need to have non-zero values. The Initialization functions are called starting with spInitialization and then working down to the most specific class so that the Initialization function for a class can override the Initialization function(s) for its superclass(es).

In order to reduce the number of Initialization functions that have to be written, the API is designed so that as much as possible, all bits zero is an appropriate initial value. The only major exception to this is that spTransforms are initialized to the identity transform, which is not all zero. Wherever a non-zero initialization is needed, this fact is pointed out in this documentation.

Whenever the a new shared class is defined that requires a variable to be initialized to something other than zero, an Initialization function must be defined for the class. If no Initialization function is defined, then the system automatically generates one that does nothing.

11.11 spRemove

void spRemove(sp Object)

Removes an object from the world model, setting the IsRemoved bit to True. If an object is removed by a process that does not own it, then the object is removed from the local world model copy of that process with no other effect. However, if an object is removed by the process that owns it, then the fact that the IsRemoved bit has been set is communicated to all the other process that know about the object, causing the object to be removed in these processes as well. One must be very careful when accessing objects that may have been removed.

Removal is different from freeing the storage for an object. Freeing happens separately and not necessarily immediately. Removal of an object by its owner signals the explicit desire to remove an object from the world model and therefore from the view of other processes. It is essential that it occur immediately, not at some later time.

11.12 spExamineChildren

void spExamineChildren(sp Object, spMask Mask, spFn F, void * Data)

Applies an operation to all the direct children of an object that are compatible with the Mask. (An object C is a child of an object P if and only if P is the Parent of C.) There are no guarantees as to the order in which the children will be accessed.

There is no explicit representation of the children of an object, and no direct way to access the children of an object. However, the indirect approach provided by spExamineChildren is typically at least as convenient as some more direct approach would be.

11.13 spExamineDescendants

void spExamineDescendants(sp Object, spMask Mask, spFn F, void * Data)

Applies an operation to all the children of an object that are compatible with the Mask and then to all their children that are compatible with the Mask and so on. The order of application is undefined except that the operation will be applied to a given descendent D before it is applied to any of the descendants of D.

11.14 spTopmost

sp spTopmost(sp Object)

Computes the highest level ancestor of an object that defines the coordinate system the object is in. This value is often useful when using operations like spThingRelativeMatrix. If the object is not in any region, then Null is returned.

11.15 spPrint

void spPrint(sp Object)

Prints out the contents of all the instance variables of a shared object. This is very useful for debugging.

12 spRegion

public class spRegion extends sp //* [-SendToRegion +SendToContact]

Regions break the world model up into pieces that are handled separately. They are the basis for the scalability of the world model. They make it possible for both the size of the local world model copy and the number of incoming messages that have to be handled per second to be dependent only on the part of the virtual world that is `near' to the local process, rather than dependent on the total size of the virtual world as a whole.

The shared class spRegion inherits all the instance variables and functions of the class sp. The class spRegion defines the following instance variables:

The class spRegion defines the following functions:

The concepts underlying spRegions are a simplification of the concepts presented in Barrus J.W., Waters R.C., and Anderson D.B., ``Locales: Supporting Large Multiuser Virtual Environments'', IEEE Computer Graphics and Applications, 16(6):50--57, November 1996.

12.1 How Regions Work

The key feature of regions is that every ordinary shared object is in exactly one region. The region is determined by following the object's Parent link. In particular, every ordinary object other than an spRegion needs to have a Parent. If the Parent of an object is a region, then the object is in that region. Otherwise, the object is in whatever region its Parent is in. The Parent of a region must be Null.

Like any other object, a region has an owner that is the only process that can change it. The region will go away, turning all the objects in it into orphans, if the owner of a region leaves the session.

Each region is associated with a particular set of communication addresses. Information about objects in a region is communicated only via those addresses. An orphaned object that is not in any region because it has no Parent is not communicated to anybody.

An important feature of a region is its boundary. The boundary specifies the three dimensional extent of the region. The boundaries of an spRegion must be specified when the region is first created and cannot be changed later.

It must be stressed that what region an object is in for communication purposes is determined by its Parent as discussed above, not any geometric considerations. However, in general, an object should be placed in a region only if its position is within the boundary of the region. (A point is within a boundary only if there is both a point below it and a point above it that are on the boundary.)

In general, transferring an object form one region to another involves nothing more complicated than changing its Parent. However, it can be somewhat complicated to figure out what region an object should be in. Determining this is facilitated by the function spThingLocalize. If an object whose Parent is an spRegion is not within the boundary of the region, and there is a nearby region whose boundary does contain the object, then spThingLocalize transfers the object to the appropriate nearby region.

A key concept above is what regions are nearby a given region. This is an induced relation based on the boundaries of regions. Specifically, a region R is near another region S if and only if the axis aligned bounding boxes of the boundaries of R and S touch---i.e. if there is any 3D point that is in (or on) both boundaries. (Note, no matter how complex the boundary object is, only the bounding box is being compared. This is sloppy but allows rapid determination of nearness. It is not crucial that nearness be exactly computed. What is typically essential is that a process listens to enough nearby regions. It usually matters little if a couple too many regions are attended to.)

A set of regions that neighbor each other pairwise is called a scene. (One can move incrementally step by step from one region to another in a scene using spThingLocalize; however, the only way to get from one scene to another is to teleport to the destination by explicitly setting the Parent of the object to be moved.)

12.2 Boundary

shared public/ spBoundary Boundary; //* [sp]

The following access functions are available:

sp spRegionGetBoundary(sp Object)

The Boundary shared instance variable of an spRegion object specifies the object describing the boundary of the region. The Boundary must be an spBoundary object. It is used to determine whether a object should be placed in a region and what regions are near other regions. The Boundary must be specified when a region is created and cannot be modified later. Information about the Boundary is shared between the processes in a session.

12.3 spRegionNew

sp spRegionNew(sp Boundary)

Creates a new object of the class spRegion, with the local process as its owner. In addition, sets the boundary of the spRegion as specified. To insure proper communication of the Boundary, the Parent of the Boundary is set to be the region created.

To create a region, you might write the following:

  spRegion R;
  R = spRegionNew(spBoundaryNew("http://www.BigRetailer.com/boundaries/BackRoom"));

13 spLink

public abstract class spLink extends sp

Shared objects are divided into two major categories: small objects that can be communicated very rapidly between processes (on the order of a hundred milliseconds) and large slowly changing objects that are communicated more slowly between processes (on the order of a few seconds). The various subclasses of the class spLink comprise the large slowly changing objects.

The key feature of spLink objects is that they refer to an arbitrarily large piece of data via a Uniform Resource Locator (URL). The link object itself, including the URL, is communicated to other processes in the same way, and just as fast, as any other object. However, once the link itself has been received, additional work has to be done to retrieve the data specified by the URL. This can take several seconds.

The data pointed to by the URL is retrieved over the World Wide Web using standard web protocols.

Like any object, the Parent of a link controls how the link is communicated. In particular, the Parent of a link must ensure that the link is known in the region where it is used. Typically, the Parent of a link is set to the object that uses it. To support prefetching of data, one can place links to the data in regions adjacent to the region where it is used. Note that having multiple copies of a link does not cause the data referred to be loaded into memory multiple times.

An application may create links that it is not using at the moment but wishes to be able to use rapidly. For example, suppose that there is an avatar that wishes to be able to switch rapidly between several visual appearances. This can be supported by creating a link for each appearance and making the avatar be the Parent of these links. This will cause the links to be communicated to the region the avatar is in and therefore preloaded in this region. The avatar can then change its appearance instantaneously by changing which link its VisualDefinition variable points to.

The shared class spLink inherits all the instance variables and functions of the class sp. The class spLink defines the following instance variables:

The class spLink defines the following functions:

It is not possible to create instances of the class spLink. Rather, one can only create instances of particular subclasses of the class spLink.

13.1 URL

shared public/ String URL; //* [spFixedAscii]

The following access functions are available:

spFixedAscii spLinkGetURL(sp Object)

The central piece of information in an spLink object is a shared instance variable called the URL, which contains a Uniform Resource Locator (URL) identifying the large piece of data being linked to. For example, an spVisualDefinition link must contain the URL of a 3D graphical model. When a process is informed of the existence of an spVisualDefinition link, it fetches the data referred to by the link's URL for later use.

The URL of an spLink must be a string. It must be less than 500 characters in length, so that the containing object can fit into a single UDP message. It must be specified at the time the link is initially created and cannot be modified after that time. Information about the URL is shared between the processes in a session.

It is inadvisable to have an object you own refer to a link created by another process because the link may get removed at any time. Rather, you should create your own link with the same URL and refer to that. (The system takes care of making sure that there are not two copies of the same URL data in memory at once, even if there are multiple links containing the URL.)

You really should not refer to someone else's URL either, unless you can trust them not to change or delete either the URL or the data it points to. To be completely safe, you should make a copy of the URL data and then refer to your own copy.

13.2 Data

native public/ int Data; //* [void *]

The following access functions are available:

void * spLinkGetData(sp Object)

The data fetched from the URL of an spLink is stored in a local instance variable called the Data. If several links have the same URL, then the data is only read into memory once and each link points to the same block of data.

The kind of data pointed to by the Data pointer of a link differs from one kind of link to another. The Data pointer is automatically filled in by the system when a new link appears in the world model and is automatically updated when the underlying data changes. The Data pointer and the data it points to should never be altered in any way. Information about the Data is maintained separately in each process.

A key feature of the Data variable is that it is guaranteed that the Data value will be filled in before the Link becomes visible in the world model. Specifically, whenever a new link is created, the Data is read in by the New function. In addition, whenever a message is received describing a new link, the act of actually entering the link into the local world model is delayed until after the Data has been obtained. This delay makes things more convenient for processes that want to do something with the data and is irrelevant to ones that don't.

13.3 spLinkNew

sp spLinkNew(spFixedAscii URL)

This function does not exist for the class spLink itself. It is described here because it is an abstract prototype of the creation function that must be written when a new subclass of spLink is defined. In particular, link creation functions must take a URL string as their first argument and create a link object containing the URL. This is the only way the URL in a link can be set.

The code below shows the general form that the New function for a subclass spL of spLink must have. In particular, it must call spClassNewLink to create the link itself. This insures that the ReadData function will be called when the link is created.

sp spLNew(spFixedAscii URL) {
  return spClassNewLink(spLC(), URL);
}

13.4 spLinkReadData

void spLinkReadData(sp Link)

This function does not exist for the class spLink itself. It is described here because it is an abstract prototype of the URL data reading function that must be written when a new subclass of spLink is defined.

The purpose of the data reading functions for link classes is to fetch the information corresponding to a link's URL and read it into memory and store a pointer to the result in the data variable of the link object. It may initialize other local fields of the object as well.

14 spVisualDefinition

public class spVisualDefinition extends spLink

An instance of the class spVisualDefinition is a link to a 3D graphical model specifying a visual appearance. The URL of an spVisualDefinition link identifies a graphical model in VRML. Ordinary applications do not read in these 3D models. Rather, they are only read in by the visual renderer. When they are read in, they are converted into scene graphs in the internal format used by the renderer. The chunk of scene graph created is stored in the data variable of the spVisualDefinition link.

The shared class spVisualDefinition inherits all the instance variables and functions of the classses spLink and sp. The class spVisualDefinition does not define any additional instance variables. The class spVisualDefinition defines the following functions:

The basic approach for creating images of a virtual world centers around spThings and their visual definitions. The appearances of all the spThings in view at a given moment are combined together into a scene graph and rendered. The positions and orientations of the various appearances are taken from the Transforms of the corresponding spThings. Getting an appearance entered into the world model is a three step process.

(1) One first creates (or locates) a 3D model in VRML, or whatever other format the renderer you are using supports. This is done using standard modeling tools.

(2) Next one has to decide how the model should be positioned on an object in the world model. As discussed in detail below, this positioning is specified using an intermediate description file.

(3) Finally, to get the appearance into a particular virtual world, you create an spVisualDefinition link whose URL refers to the intermediate description file and make it be the appearance of one or more spThing objects. The following code shows an example of this.

  look = spVisualDefinitionNew("http://www.models.com/demo/models/table");
  object = spThingNew();
  spThingSetVisualDefinition(object, look);
  spSetParent(look, object);

The intermediate description files have a two level structure. First, they can have one or more entries corresponding to alternate 3D models. These models might be in different formats or at different levels of detail. The process loading a link is free to choose whichever model is most appropriate for it. Second, each individual entry contains several key pieces of information, each on a separate line. (Note that this information is utilized only by the visual renderer.)

The central element above is the positioning transform. It allows you to take an arbitrary model and use it without having to alter it in any way. For example, suppose that you are creating a virtual world that contains a virtual car that can move around. To conform with the API's object orientation standards, the spThing for the car must be oriented with the Y axis up and with the front of the car facing down the negative Z axis. Further, to make it easy to move the car around, it is convenient to place the origin of the car on the ground under the center of the car's appearance. In addition, the car in the virtual world has some intended size. (These latter two needs would remain even if the API had no object orientation standards.) It is very unlikely that a given model for the car's appearance will meet these conditions. However, using a positioning transform it is trivial to adapt the model to its use in the virtual world without having to modify the model itself.

Continuing the example above, "http://www.models.com/demo/models/table" might contain:

model/vrml
  http://www.models.com/demo/models/table.vrml
  88347136
  -1.0 8.0 0.2  0.0 0.0 1.0  1.523  1.0 1.0 1.0  0.0 0.0 1.0 0.0  0.0 0.0 0.0

The programming API considers graphical models to be completely opaque. All it cares about is that the visual renderer being used can understand the models created by the modeling tools being used. The system itself never looks at what is in a graphical model. In particular, it is not intended that an application dynamically modify models, but rather only effect the overall scene graph by affecting the aspects of spThings (such as their Transforms) that are visible in the world model.

14.1 spVisualDefinitionNew

sp spVisualDefinitionNew(spFixedAscii URL)

Creates a new object of the class spVisualDefinition, with the local process as its owner. In addition, sets the URL of the spVisualDefinition as specified.

15 spSound

public class spSound extends spLink

An important feature of the API is a facility for prerecorded sound. This is intended to be used for (usually short) things that get used over and over (like the sound of a door closing). Their advantage is that the data can be prestored at the destination processes instead of having to be transmitted every time it is used.

An instance of the class spSound is a link to a recorded block of sound data such as ambient background sound or a sound effect. The data can be created using a number of standard tools such as sound editors and written in files in a number of standard formats. They are then referred to using spSound links.

The shared class spSound inherits all the instance variables and functions of the classses spLink and sp. The class spSound defines the following instance variables:

The class spSound defines the following functions:

The basic approach presented here for creating sound effects in a virtual world centers around spSound and spSoundSource objects. The spSound objects specify what the various sound effects sound like. The positions of the spSoundSources specify where sounds emanate. Getting a sound effect to happen in a virtual world is a three step process.

(1) One first creates (or locates) sound files in one of several common formats. This is done using standard sound capture and editing tools.

(2) As discussed in detail below, one then creates an intermediate description file that summarizes key features of one or more versions of the sound.

(3) Finally, to get the sound into a particular virtual world, you create an spSound link whose URL refers to the intermediate description file. You then create one or more spSoundSource objects and use spSoundPlay to play the sound at the right places and the right times. The following code shows an example of this.

  bang = spSoundNew("http://www.models.com/sounds/bang23.wav");
  source = spAudioSourceNew();
  spSetParent(bang, source);
  spSoundPlay(bang, source, FALSE, 1.0);

The intermediate description files have a two level structure. The first line of the file specifies the duration of the sound rounded to the nearest millisecond. This value is needed so that the Duration variable of an spSound can be set correctly even if the sound data itself is not read into memory.

Subsequent groups of lines specify one or more sound files that correspond to the sound. Each sound file entry contains several key pieces of information:

It is expected that the various sound files referred to will have different encodings or different sample rates. For example, you might have 8 KHz file for the use of low powered machines and a 32 KHz high fidelity version for more powerful machines. The process loading a sound is free to choose whichever sound file is most appropriate for it.

Continuing the example above, "http://www.models.com/sounds/bang23" might contain:

123
audio/x-wav
  6638759684996246
  http://www.models.com/sounds/bang23.wav
  4500
  88347136
audio/x-aiff
  5754345865388749
  http://www.models.com/sounds/bang23.aiff
  9000
  72545754

The programming API considers stored sounds to be completely opaque. All it cares about is that the audio renderer being used can understand the sound files created by the modeling tools being used. The system itself never looks at what is in a sound file.

15.1 Duration

native public/ int Duration; //* [spDuration]

The following access functions are available:

spDuration spSoundGetDuration(sp Object)

spSound objects have a local instance variable call the Duration that records the period of time corresponding to the sound data referred to. This is recorded in a variable so that processes that processes other than an audio renderer can reason about how much time is required to play back the data without having to load the entire sound file into memory.

The Duration is a 32-bit integer of the type spDuration specifying the time in milliseconds. It is set by spSound ReadData function based on the URL data even when the full sound data is not read into memory. If the number of samples does not correspond to an exact integer number of milliseconds, then the time is calculated by rounding it to the nearest millisecond. The Duration should not be altered after that time. Information about the Duration is maintained separately in each process.

15.2 spSoundNew

sp spSoundNew(spFixedAscii URL)

Creates a new object of the class spSound, with the local process as its owner. In addition, sets the URL of the spSound as specified.

15.3 spSoundPlay

sp spSoundPlay(sp Sound, sp Source, spBoolean Loop, float Gain)

Causes a stored sound to be played through an spAudioSource. The key feature of spSoundPlay is that it does not actually output the sound through the source, but rather creates an action that simulates the receipt of the sound by each process that can hear it, without having to send the sound data over the network.

The playing of sound continues until: the data runs out, the action object returned by spSoundPlay is removed, the source object is removed, the spSound object is removed, or some other sound is played through the same spAudioSource. Note you can only be playing one sound at a time through a sound source.

16 spBoundary

public class spBoundary extends spLink //* [+SendToContact]

Boundaries encapsulate the basic concept of a machine manipulable description of a 3D volume. Their primary use is in conjunction with spRegion objects. However, they can also be used in other situations as well.

The shared class spBoundary inherits all the instance variables and functions of the classses spLink and sp. The class spBoundary defines the following instance variables:

The class spBoundary defines the following functions:

The URL in an spBoundary link identifies a file containing a detailed description of the boundary. In the case of spBoundary objects themselves, the description is an axis aligned bounding box. Specifically, the file identified by the URL must contain one line of text consisting of 6 floats separated by spaces. The six floats specify the minimum X value, minimum Y, minimum Z, maximum X, maximum Y, and maximum Z values of the spBoundaries bounding box respectively. For instance, the file might contain:

-1.1 2.333 0.0 10.9 5.2 1.0

The boundary information provided by an spBoundary object is very simple, and sufficient for some applications. Subclasses of spBoundary are free to use more detailed representations such as BSP trees as long as they also specify the bounding box.

spBoundary objects are separate objects from spRegions because they support a completely separate concept. spRegions need spBoundaries to work, but spBoundaries can be useful without spRegions. In particular, an spBoundary smaller than (or larger than) an spRegion can be used by itself as part of controlling a motion, without wanting to have any effect on communication.

16.1 Min

native public/ float[] Min; //* [spVector:3]

The following access functions are available:

spVector spBoundaryGetMin(sp Object)

The Min local instance variable of an spBoundary object contains the coordinates of the corner of the bounding box that has the smallest X, Y, and Z values. This is set when the boundary information is loaded into memory and cannot be changed later. Note that specializations of spBoundary must be sure to set these values. Information about the Min is maintained separately in each process.

16.2 Max

native public/ float[] Max; //* [spVector:3]

The following access functions are available:

spVector spBoundaryGetMax(sp Object)

The Max local instance variable of an spBoundary object contains the coordinates of the corner of the bounding box that has the greatest X, Y, and Z values. This is set when the boundary information is loaded into memory and cannot be changed later. Note that specializations of spBoundary must be sure to set these values. Information about the Max is maintained separately in each process.

16.3 spBoundaryNew

sp spBoundaryNew(spFixedAscii URL)

Creates a new object of the class spBoundary, with the local process as its owner. In addition, sets the URL of the spBoundary as specified.

16.4 spBoundaryBelow

spBoolean spBoundaryBelow(sp Boundary, spVector P, spVector Q)

Given an (X,Y,Z) position P, this function determines whether a point P is above a boundary, and if it is, the position on the floor that is immediately below P. Specifically, spBoundaryBelow casts a ray downward from P and determines whether and where this ray intersects the boundary. Since `down' is defined to be along the negative Y axis, if there is a point below P on the boundary, then this point has the same X and Z values as P, but an equal or a lessor Y value.

If there is not a point on the boundary below P, then False is returned. Otherwise, True is returned. In addition, if the Q argument is not Null, then the X, Y, and Z components of Q are altered so that they are equal to the coordinates of the boundary point below P. (It is permissible for the two vector arguments of spBoundaryBelow to be identical.)

For the class spBoundary itself, the definition of Below is trivial. To determine whether the ray case downward from P intersects the boundary, It merely has to determine if the X and Z coordinates of P are within the X-Z bounds of the region. If so the intersection point has the X and Z coordinates of P and has a Y value that is largest of the Min or Max Y value of the boundary that is less than the Y value of P. However, it is expected that the definitions of Below for subclasses of spBoundary will do more detailed and complex calculations.

16.5 spBoundaryAbove

spBoolean spBoundaryAbove(sp Boundary, spVector P, spVector Q)

The Above function for a subclass of spBoundary is identical to the Below function except that it determines whether there is a point on the boundary that is above the test point, rather than below.

16.6 spBoundaryInside

spBoolean spBoundaryInside(sp Boundary, spVector P)

Given an (X,Y,Z) position P, this function determines whether a point P is on or inside the 3D volume specified by an spBoundary. If P is on or within the boundary, then True is returned. Otherwise, False is returned.

For the class spBoundary itself, the definition of Inside is trivial. However, it is expected that the definitions of Below for subclasses of spBoundary will do more detailed and complex calculations.

17 spClass

public class spClass extends spLink

An spClass object describes the layout of the data in the memory representation of a shared object class. The URL of an spClass object identifies a file that contains this information. This file is created by the SPOT Java preprocessor supporting the definition of shared classes.

spClass objects are used extensively in the internal operation of the system. When writing applications they have important uses as well, but primarily only as opaque identifiers that are passed as arguments to functions like spClassExamine.

In addition, knowing the identity of the class descriptor of an object is very useful for runtime dispatching in an application. For example, an application might perform the following test to determine whether an object X is a direct instance of the class spThing. (This expression returns False if X is an instance of a subclass of spThing.)

spGetClass(X) == spThingC()

Alternatively, an application might perform the following test to determine whether X is an instance of spThing or any subclass of spThing.

spClassLeq(spGetClass(X),spThingC())

The shared class spClass inherits all the instance variables and functions of the classses spLink and sp. The class spClass defines the following instance variables:

The class spClass defines the following functions:

Note that the various instance variables above primarily specify information only about the instance variables of a shared class as opposed to about methods. The reason for this is that shared classes are primarily passive data structures. It is therefore better in many ways to view the world model as a database rather than a collection of objects. The key focus is on communicating this data, not on the complex interaction of methods. Users can define new classes with as many new data variables to be communicated as they like.

It is possible to define methods when defining a new shared class; however, in general nothing will be done internally with these methods. (An application may take good advantage of them, however.) The only exceptions to this are the methods stored in the spClass variables InitializationFn, LinkReadDataFn, FunctionFn, PredicateFn, and InsideFn. These methods are each called internally in particular situations. They are recorded in spClass objects for ready accessibility.

This document describes the various built-in shared classes. These built-in classes are specified in a special way and are automatically loaded when the world model is initialized. Except for a few internal objects that are not visible to an application, the spClass objects corresponding to these classes are the only objects that exist in the world model immediately after a world model is created using spWMNew.

New classes defined by an application are handled in exactly the same way as any other kind of link object. They must be created by some process and are then communicated to other processes.

17.1 Superclass

shared public/ spClass Superclass; //* [sp]

The following access functions are available:

sp spClassGetSuperclass(sp Object)

Except for the root class sp, every shared class has a superclass that is another shared class. The superclass is stored in a shared instance variable called the Superclass.

The Superclass of an spClass object must be an spClass. The only exception to this is that the Superclass of the class sp is Null. The Superclass of a class must be specified when the class is initially created and cannot be changed after that time. Information about the Superclass is shared between the processes in a session.

17.2 ClassName

native public/ String ClassName; //* [spAscii32:32]

The following access functions are available:

spAscii32 spClassGetClassName(sp Object)

For debugging convenience, spClass objects have a local instance variable called the ClassName that contains an ASCII representation of the name of the class.

The ClassName is a string no more than 31 characters long. It is set by spClassReadData when a class is loaded from a class descriptor file and cannot be changed. Information about the ClassName is maintained separately in each process.

17.3 spClassNewObj

sp spClassNewObj(sp ClassDescriptor, long Extra)

This is a general operation for creating an instance of any shared object class. Given an spClass descriptor of a shared object class, spClassNewObj creates an instance of the class. If a shared object class has (either directly or by inheritance) a string variable with the C type spFixedAscii, storage for the string value must be allocated at the moment the object is created. The Extra argument specifies how many characters of storage are needed.

When the storage for an object has been allocated, the values of all instance variables are set to all bits zero. After this, the Initialization functions are called for the class and all its ancestors in order to properly initialize any variables that need to have non-zero values. The Initialization functions are called starting with spInitialization and then working down to the most specific class, so that the Initialization function for a class can override the Initialization function for its Superclass.

17.4 spClassNewLink

sp spClassNewLink(sp ClassDescriptor, spFixedAscii URL)

This is a general operation for creating an instance of a subclass of spLink. Given an spClass descriptor of a link class, spClassNewLink creates an instance of the class by calling spClassNewObj. The URL argument specifies the URL to use when creating the object. This is stored in the link created and then the ReadData function for the link is called. This is needed so that the Data in the link will be loaded immediately when the link is created. Just as the New functions for objects in general should start by calling spClassNewObj, the New functions for links should start by calling spClassNewLink.

17.5 spClassNew

sp spClassNew(spFixedAscii URL, sp Superclass)

Creates a new object of the class spClass, with the local process as its owner. In addition, sets the URL and Superclass of the spClass as specified. A class cannot be created unless its Superclass already exists.

17.6 spClassLeq

spBoolean spClassLeq(sp Subclass, sp Superclass)

This predicate tests for subclass relationships between spClass class descriptors. True is returned if the class being tested is either equal to Superclass or a descendent of Superclass in the shared class hierarchy. For example:

spClassLeq(spGetClass(spAvatorNew()), spThingGetC()) == TRUE
spClassLeq(spThingGetC(), spThingGetC()) == TRUE
spClassLeq(spLinkGetC(), spThingGetC()) == FALSE

17.7 spClassExamine

spBoolean spClassExamine(sp C, spMask Mask, spFn F, void * Data)

Applies an operation F to all the objects in a class. This includes objects that are instances of subclasses of the class as well as instances of the class itself. However, it only includes objects that are current in the local world model copy and it only includes objects that are compatible with the specified Mask. There is no guarantee of the order in which F will be applied to objects. If F ever returns True, then examining ceases and spClassExamine returns True. Otherwise False is returned.

17.8 spClassMonitor

sp spClassMonitor(sp C, spMask Mask, spFn F, void * Data)

Applies an operation to present and future objects in the local world model copy that are in the indicated class (and its subclasses) and are compatible with Mask. If F ever returns True, then monitoring ceases.

This is done by first examining the objects that currently exist and then creating a callback that applies F to objects that appear in the future. If a callback is produced, it is returned. If a callback is returned, monitoring can be terminated by removing the callback object.

The following shows how spClassMonitor is implemented using spClassExamine and a callback.

sp spClassMonitor(sp C, spMask mask, spFn F, void * Data) {
  sp callback;
  if (spClassExamine(C, Mask, F, Data)) return NULL;
  callback = spCallbackNew(C, -1, spJustNew, NULL, F, Data);
  spActionSetMask(callback, Mask);
  return callback;  
}

18 spThing

public class spThing extends sp

This class is used to represent objects in the virtual world created by an application, as opposed to objects that are part of the way the software platform operates internally. The key defining characteristics of spThing objects are that they have positions and visual definitions and occupy volumes of space.

The shared class spThing inherits all the instance variables and functions of the class sp. The class spThing defines the following instance variables:

The class spThing defines the following functions:

18.1 Transform

shared public float[] Transform; //* [spTransform:17]

The following access functions are available:

spTransform spThingGetTransform(sp Object)
void spThingSetTransform(sp Object, spTransform X)

The fundamental representation of the position, orientation, and scaling of an spThing is an spTransform. This is stored in a shared instance variable called the Transform. This form of representation is chosen because it is easy to understand and each feature is represented separately, so that individual features are easy to change without effecting any other feature.

The Transform of an spThing is interpreted relative to the coordinate system of the object's Parent. For instance, if the head of a humanoid figure is represented as a child of the torso, then the Transform of the head specifies the position, orientation and scaling of the head relative to the torso. This is convenient both because it makes it easy to move the head relative to the torso and because it makes it possible to move and scale the torso and head together relative to the surrounding world without modifying the Transform of the head.

Note that the Transform effects the appearance of an spThing and other pieces of information as well, such as the size of the units of the InRadius.

The Transform is of type spTransform. When an spThing object is created, its Transform is initialized to a value that indicates, no translation, no rotation, and no scaling. It can be changed whenever desired later. Information about the Transform is shared between the processes in a session.

The value returned by spThingGetTransform shares memory with the spThing itself and in general, cannot be retained across a call to spWMUpdate.

18.2 VisualDefinition

shared public spVisualDefinition VisualDefinition; //* [sp]

The following access functions are available:

sp spThingGetVisualDefinition(sp Object)
void spThingSetVisualDefinition(sp Object, sp X)

An spThing object has a shared instance variable called the VisualDefinition that specifies how it looks. The VisualDefinition is a 3D graphical model that is used by the visual renderer when generating images of the virtual world.

The VisualDefinition of an spThing must be an spVisualDefinition object or Null, indicating that the spThing has no appearance. When an spThing is created, its VisualDefinition is initialized to Null. Information about the VisualDefinition is shared between the processes in a session.

18.3 InRadius

shared public float InRadius; //* [float]

The following access functions are available:

float spThingGetInRadius(sp Object)
void spThingSetInRadius(sp Object, float X)

An spThing object has a shared instance variable called the InRadius that specifies the radius of the largest sphere (positioned at the center of the object's coordinate system) that fits entirely within the object. This is included to provide a crude, but rapid, basis for applications to reason about collisions and other interactions between spThing objects. Note that since the InRadius is with respect to the coordinate system of the object itself, it is scaled by the Transforms of the object and its ancestors. Therefore, from the perspective of an ancestor coordinate system, the InRadius actually specifies an ellipsoid, not a sphere.

The InRadius of an spThing must be a float. When an spThing is created, its InRadius is initialized to zero. Information about the InRadius is shared between the processes in a session.

18.4 OutRadius

shared public float OutRadius; //* [float]

The following access functions are available:

float spThingGetOutRadius(sp Object)
void spThingSetOutRadius(sp Object, float X)

An spThing object has a shared instance variable called the OutRadius that specifies the radius of the smallest sphere (positioned at the center of the object's coordinate system) in which the object can fit. This is included to provide a crude, but rapid, basis for applications to reason about collisions and other interactions between spThing objects. Like the InRadius the OutRadius is scaled by the Transforms of the object and its ancestors and therefore from the perspective of an ancestor coordinate system, actually specifies an ellipsoid, not a sphere.

The OutRadius of an spThing must be a float. When an spThing is created, its OutRadius is initialized to zero. Information about the OutRadius is shared between the processes in a session.

The InRadius, and OutRadius, of spThing objects are included to provide a quick minimal step in the direction of describing the volume occupied by an object. Many other things are possible, like bounding boxes and BSP trees. These were not included because they would take up too much space to support as a general thing. Users can define specializations of spThing that have more detailed volume descriptions if they want.

18.5 spThingMatrix

spMatrix spThingMatrix(sp Thing)

The fundamental representation of the position, orientation, and scaling of an spThing is an spTransform. However, in many computational situations, it is more convenient to manipulate this information when represented as a 4x4 transformation matrix. The function spThingMatrix computes the spMatrix corresponding to the Transform of an spThing. Since this is relatively expensive to compute, the result is cached so that it only has to be recomputed when the Transform changes.

The value returned by spThingMatrix shares memory with the spThing itself and in general, cannot be retained across a call to spWMUpdate.

There is no operation for directly setting the Transform of an spThing from an spMatrix. Rather one should use spThingSetTransform and spTransformFromMatrix. This makes the expense of this operation more obvious to the application writer. As much as possible, it is wise to operate solely in terms of spTransforms.

18.6 spThingMatrixInverse

spMatrix spThingMatrixInverse(sp Thing)

Computes the inverse of the spMatrix corresponding to the Transform of an spThing. Since this is relatively expensive to compute, the result is cached so that it only has to be recomputed when the Transform changes.

The value returned by spThingMatrixInverse shares memory with the spThing itself and in general, cannot be retained across a call to spWMUpdate.

18.7 spThingSetTransform

void spThingSetTransform(sp Thing, spTransform Transform)

Sets the Transform variable in an spThing, doing everything a standard Set accessor would do, but also sets indicators specifying that the corresponding spMatrix must be recalculated. This accessor method is explicitly described in order to indicate that it does not have just the standard default behavior.

18.8 spThingLocalize

spBoolean spThingLocalize(sp Thing, sp Region, spBoolean ChooseSmallest)

Places an object in an appropriate region. The object must have as its direct Parent a region R. The object is either left in R or placed in one of the regions near R. This is done in one of three modes.

(1) If the Region argument is not Null, then the object is placed in this region, which must either equal R or be near R.

(2) If the Region argument is Null and the ChooseSmallest argument is True, then all the regions near R are checked to see which ones have boundaries that encompass the location of the object. If any of the regions contain the object, then the object is placed in the containing region with the smallest bounding box.

(3) If the Region argument is Null and the chooseSmallest argument is False, then the system first checks whether the object is inside R. If it is, no further action is taken. If not, a new region is found for the object as in mode (2). (The reason why this third mode of behavior is included is that it is usually sufficient and is a great deal more efficient than the second mode of operation. The reason that the second mode of operation is included is that the third mode will never move an object from a region R into another region that is contained within R.)

No matter how a new region for the object is chosen, the new region is made the Parent of the object. True is returned if object ends up in the boundary of a region.

Whenever an object whose Parent is a region moves in such a way that it might have moved out of the boundary of its region, spThingLocalize should be called with a Region argument of Null to make sure that the object ends up in an appropriate region. To teleport someplace, you can just set the parent link and matrix of an object yourself to some particular position in some region.

18.9 spThingRelativeMatrix

spMatrix spThingRelativeMatrix(sp POV, sp Thing, spMatrix Matrix)

Computes an spMatrix that specifies the position, orientation, and scaling of an spThing Thing in the coordinate system of another object POV. If POV, is the Parent of Thing, then this is just the matrix of Thing. If POV is an ancestor of Thing, then several matrices have to be multiplied together. If Thing and POV have a more complex relationship, then some matrices have to be inverted. The result is stored in the Matrix argument which is returned.

18.10 spThingRelativeVector

spVector spThingRelativeVector(sp Thing, sp POV, spVector Vector)

Computes the translation portion of the matrix computed by spThingRelativeMatrix. This value is stored in the Vector argument and returned.

18.11 spThingDistance

float spThingDistance(sp Thing, sp POV)

Returns the length of the vector computed by spThingRelativeVector.

18.12 spThingLookAt

void spThingLookAt(sp Thing, sp Target)

Modifies the Transform of an spThing Thing so that Thing is looking at another spThing Target. That is to say, the rotational orientation of Thing is altered so that the negative Z axis of Thing's coordinate system points to the origin of Target's coordinate system. In addition, the Y axis of Thing is oriented so that it and the Y axis of the highest level coordinate system containing Thing are co-planar. If Thing is an spSeeing object controlling visual rendering, this causes objects that are upright from the perspective of the highest level coordinate system containing Thing to appear upright in the images generated.

19 spRoot

public class spRoot extends spThing

An spRoot is an spThing that corresponds to a logical whole. For example, if the torso was the highest level object in an articulated humanoid figure, then it should be created as an spRoot object rather than merely an spThing. In addition, it is expected that in general, only spRoot objects will have regions as their direct parents. (It is permissible to put an spRoot under another object as long as it is logically possible that the object might sometimes move on its own.)

The class spRoot is included because of its fundamental significance to computer simulations that want to interact with assemblages of objects in the virtual world.

The shared class spRoot inherits all the instance variables and functions of the classses spThing and sp. The class spRoot does not define any additional instance variables. The class spRoot defines the following functions:

20 spAvatar

public class spAvatar extends spRoot

The class spAvatar is a specialization of spRoot that corresponds to an active entity. That is to say, it is the avatar of a human user or the avatar of a computer simulated agent that wishes to interact in more-or-less the same way as if it were a human user.

Internally the system does nothing special with instances of the class spAvatar. Rather, the class is included because of its fundamental significance to computer simulations that want to interact with human users and other computer simulated agents.

The shared class spAvatar inherits all the instance variables and functions of the classses spRoot, spThing and sp. The class spAvatar defines the following instance variables:

The class spAvatar defines the following functions:

20.1 IsBot

shared public boolean IsBot; //* [spBoolean]

The following access functions are available:

spBoolean spAvatarGetIsBot(sp Object)
void spAvatarSetIsBot(sp Object, spBoolean X)

The IsBot shared instance variable of an spAvatar specifies whether the avatar represents a human being or a computer simulation. Specifically, if the IsBot bit is True, then the avatar represents a computer simulation of some kind. If the IsBot bit is False, then the avatar represents a human user.

The IsBot bit of an spAvatar is given a default value of False. Information about the IsBot bit is shared between the processes in a session.

21 spAudioSource

public class spAudioSource extends spThing

spAudioSource objects are used to represent sources of sound in the virtual world. They are used in two primary ways. Most simply, you can connect up with a processes that captures sound with a microphone by creating an spSpeaking object. This indirectly leads to the creation of an appropriate spAudioSource object. Alternatively, you can create spAudioSource objects yourself and play sound effects through them using spSoundPlay. Since spAudioSource is a subclass of spThing, sound sources can be moved around and oriented like any other spThing.

The shared class spAudioSource inherits all the instance variables and functions of the classses spThing and sp. The class spAudioSource defines the following instance variables:

The class spAudioSource defines the following functions:

spAudioSources can be localized point sources or diffuse sources. This is specified using the InRadius and OutRadius variables inherited from spThing. If the InRadius is zero (its default value), then the source is considered to be a point source. Sound drops off in intensity as you move away and is localized to the direction of the source. If the InRadius is not zero, then the source is treated as diffuse---coming with equal intensity from every point within the InRadius of the source. Outside the InRadius, the sound is localized to the direction of the source and drops off to reach zero at the OutRadius.

21.1 Live

shared public boolean Live; //* [spBoolean]

The following access functions are available:

spBoolean spAudioSourceGetLive(sp Object)
void spAudioSourceSetLive(sp Object, spBoolean X)

spAudioSource objects have a shared instance variable called the Live bit, which indicates whether the data associated with the source is being captured from a human being, e.g., by a microphone. It should be noted that this does not just mean that it is being generated in real time but that it is being spoken by a person that is already hearing it as they speak. The only effect of this field is on the processing done by the audio renderer. In particular, care is taken so that users do not hear a delayed version of what they are saying.

The default value of the Live bit is False, which indicates that the source data is not being captured live from a person. If the Live bit is set to True, this indicates that the source data is being captured live. Information about the Live bit is shared between the processes in a session.

If you change the Live bit it may take on the order of a second for the change to make any difference. It is not expected that this bit will be changed often.

21.2 Format

native public/ long Format; //* [spFormat]

The following access functions are available:

spFormat spAudioSourceGetFormat(sp Object)

spAudioSource objects have a local instance variable called the Format, which specifies the format (i.e., encoding, samples per second, etc.) used for the sound data in memory. This format is of type spFormat. It is initialized to zero, which has the meaning that audio is not being written or read via the spAudioSource. It is set by the system when spAudioSourceInit is called. It should not be modified by application programs. Information about the Format is maintained separately in each process.

The format of audio data written by the owner of an spAudioSource may be different from the format used to read audio data in another process. Conversion is done to change source data into the data read out.

21.3 Duration

native public/ int Duration; //* [spDuration]

The following access functions are available:

spDuration spAudioSourceGetDuration(sp Object)

spAudioSource objects have a shared instance variable called the Duration, which specifies the lengths of the chunks of sound data that can be read using spAudioSourceRead. This is specified in milliseconds.

The Duration is initialized to zero when an spAudioSource is created, which has the meaning that audio is not being written or read via the spAudioSource. It is set by the system when spAudioSourceSetup is called. It should not be modified directly by application programs. Information about the Duration is maintained separately in each process.

21.4 spAudioSourceSetup

void spAudioSourceSetup(sp S, spBoolean W, spBoolean R, spFormat F, spDuration D)

Sets up the buffering and communication that is needed to support audio I/O. If W is True, the source must be owned by spWMGetMe and things are set up so that spAudioSourceWrite can be called. If R is True, things are set up so that spAudioSourceRead can be called. If neither are True, audio buffering and communication are shut down, which saves significant system resources.

Things are set up so that the next chunk of data written and/or read will begin at the current moment. If you want to cease writing and/or reading sound at some point, you can simply stop calling spAudioSourceWrite and spAudioSourceRead. However, it is better to call spAudioSourceSetup with the W and R arguments both False. In any event, you must call spAudioSourceSetup again to resynchronize I/O with the current moment before resuming writing and/or reading.

The Format and Duration of the spAudioSource are set to F and D respectively. Either value can be specified as zero, in which case the system picks a reasonable value to use. For the Format, this is spFormat8LINEAR16MONO, which specifies 8000, 16-bit, linearly encoded, mono samples per second. The default Duration is 40 milliseconds.

As discussed in MERL TR 96-09, a critical requirement is that for every chunk of sound read or written the duration in milliseconds must be a value that corresponds to an exact integer number of samples. If the rate is divisible by 1000, then this requirement is trivial to satisfy. However, when this is not the case, the restriction can be significant. For example, CD quality sound is at the rate 44100, which implies that the Duration must be a multiple of 10 milliseconds.

To make buffering work efficiently, it is further required that the Duration in an spAudioSource must be a divisor of a specially chosen internal number (a week in milliseconds). Since this number has many divisors, this places only a small additional constraint on durations (basically they cannot have any large prime divisors).

Lastly, to make communication easy, the Duration is required to be short enough so that the whole chunk of data will fit into a single UDP message.

If the Duration passed to spAudioSourceSetup does not meet the criteria above, it is changed by to the nearest duration (above or below) that does meet the criteria.

21.5 spAudioSourceWrite

void spAudioSourceWrite(sp Source, char * Data)

Writes a chunk of data through an spAudioSource. The source must be an spAudioSource owned by spWMGetMe. The Format and Duration of the Data must be as specified when spAudioSourceSetup was called.

It is required that the data must be in the future i.e., begin now or later. However, due to limited buffer sizes, the data must in general end less than a second in the future.

Each time spAudioSourceWrite is called, it is assumed that the data being transmitted exactly follows the previous data transmitted. You are responsible for supplying the data before the time it is needed, (but not more than a second before) with a margin for error of not more than a few milliseconds outside these bounds. (Note you can only be writing one sequence of data chunks at a time through a source.)

21.6 spAudioSourceRead

char * spAudioSourceRead(sp Source)

Reads a chunk of sound data from an spAudioSource. The data returned has the Format and Duration specified when spAudioSourceSetup was called. It is identical to the data that was written through the spAudioSource by the owner of the spAudioSource object, except that a format conversion may have been applied and periods of very quiet sound may have been forced to be exactly zero. (Significant bandwidth can be saved by omitting the communication of blocks of data corresponding to silence.)

It is required that the data read must refer to the past i.e., end now or earlier. (For instance, you must wait at least duration milliseconds before reading the first chunk of data form an spAudioSource after calling spAudioSourceSetup.) However, due to limited buffer sizes, the data must in general begin less than a second in the past.

Each time spAudioSourceRead is called, it is assumed that the data being requested exactly follows the previous data requested. You are responsible for asking for the data after the time it is ready, (but not more than a second after) with a margin for error of not more than a few milliseconds beyond these bounds.

22 spBeacon

public class spBeacon extends spThing //* [+SendToContact]

The purpose of beacons is to provide a way for application processes to communicate with each other independent of the location-addressable communication operating in conjunction with regions. In particular, they provide a way to locate an object even when there is no knowledge about what region the object is in.

The shared class spBeacon inherits all the instance variables and functions of the classses spThing and sp. The class spBeacon defines the following instance variables:

The class spBeacon defines the following functions:

An in-depth discussion of the concepts underlying Beacons can be found in [Barrus J.W., Waters R.C., and Anderson D.B., ``Locales and Beacons: Efficient and Precise Support for Large Multi-User Virtual Environments,'' IEEE Virtual Reality Annual International Symposium, (Santa Clara CA, March 1996), pages 204--213].

22.1 Using Beacons

Beacons provide a way to bootstrap from an initial state of knowing nothing about a part of a virtual world to a state of having located a region of interest and become aware of the objects in that part of the world model. This is done in a three part process.

(1) Decide on a beacon tag that is of interest.

(2) Decide which if any of the beacons with that tag are of interest.

(3) Connect to the part(s) of the world model that contains the beacon(s) of interest.

The first step concerns solely the tags of beacons. In a given situation, there must be an a priori method for selecting tags of interest. Once a tag has been selected, spBeaconExamine is used to get access to all the beacons with the tag no matter what region they are in.

When a beacon B has been obtained by spBeaconExamine it is likely that the local world model copy will not contain any information about the region B is in except for B itself. Therefore the decision in the second step above of what beacons are interesting must be done solely based on data that is in the beacons themselves. To make this easier in a given application, a new subclass of beacons might be defined with some additional information.

To obtain information about the other objects in the region B is in, a process must create a POV object and put it (or an object the POV is a descendant of) in the same region. The only reliable way to do this is to initially make the POV be a child of B. The reason that this is the only reliable approach is that B may well be the only object in the local world model copy that is in the region in question.

Note that B has a Parent P. It might well be the case that it would be more convenient to make the POV be a direct child of P; however, in general, this is not possible. The reason is that if P is not in the local world model copy, then there is no way for the process in question to get its hands on P until after the POV has been placed in the region and information has been obtained about the objects in the region.

More specifically, when applied to B, spGetParent returns P only if P is in the local world model copy. If P is not in the local world model, then Null is returned. Therefore trying to set the Parent of the POV to be the same as the Parent of B will not work.

After starting with the POV as a child of B, one can change the Parent of the POV to be the Parent of B once information about the region has been obtained. This change is necessary if you wish the POV to move around the region under its own control.

There are several standard ways in which beacons are used. Perhaps the most common is to connect to a service provider such as a visual rendering process. In order to communicate with a server, a format for tags must be agreed on in advance. For instance to communicate between visual renderers and processes, the API uses tags of the form "spVisual@ip-address" (e.g., "spVisual@earth.merl.com").

To contact a visual renderer, you create an spSeeing beacon with an appropriate tag. The renderer monitors beacons looking for tags with its fully qualified DNS address string in it. When it finds one goes to work. If the beacon it is following gets removed, the renderer will then look for another beacon. (If two beacons both attempt to use the same renderer, the renderer will render based on the beacon it encounters first.)

A second prototypical example is using beacons to mark a fixed location. For example, this is done to indicate the landing pad that is the entry point for Diamond Park. In particular, the outside region in Diamond Park contains a beacon whose Transform indicates the position and orientation of the center of the landing pad. This beacon is created by the process that creates the park. The beacon has a tag like "/www.merl.com/Diamond-Park" that is communicated (e.g., in Email) to people that might be interested in entering the park.

Someone who wants to visit the park creates an avatar; examines beacons with the tag above (finding only one); makes the avatar be a child of the beacon found; and then after the park region itself appears in the local world model copy, makes the avatar be a direct child of the park region with the same matrix as the beacon. Once the last step has been completed, the avatar can freely move about the park and into other regions such as the insides of the various buildings.

22.2 Tag

shared public/ String Tag; //* [spFixedAscii]

The following access functions are available:

spFixedAscii spBeaconGetTag(sp Object)

The central instance variable of an spBeacon is a shared variable called the Tag. The key feature of beacons is that a global name service is maintained that maps from Tags to the beacon objects so that beacons can be accessed based on their Tags no matter where they are located. To allow many different people to create Tags that do not conflict with each other, it is intended that beacon Tags will have a format similar to URLs. It is generally not a good idea to have lots of beacons with the same Tag, but there can certainly be several beacons with the same Tag.

A beacon Tag is a string of type spFixedAscii. It must be less than 500 characters in length, so that the containing object can fit into a single UDP message. It must be specified when a beacon is initially created and cannot be changed after that time. Information about the Tag is shared between the processes in a session.

22.3 Suppress

shared public boolean Suppress; //* [spBoolean]

The following access functions are available:

spBoolean spBeaconGetSuppress(sp Object)
void spBeaconSetSuppress(sp Object, spBoolean X)

spBeacon objects have a shared instance variable called the Suppress bit that can be used to suppress the activity of processes that would otherwise by triggered by the beacon. Specifically, a process that sees a beacon targeted at it that has the Suppress bit True should not perform its normal activity, but should be ready at any moment to start doing so if the Suppress bit changes to False.

The Suppress bit of a beacon is set to False when the beacon is created. It can be changed at any time later. Information about the Suppress bit is shared between the processes in a session.

As an example of the use of the Suppress bit, consider the interaction of spSeeing beacons and visual rendering. Normally an spSeeing beacon triggers the creation of an image. In contrast, if the Suppress bit is on, then no image is created. However, all the loading of model files and the like that is needed to create an image is done, so that the creation of an image can begin instantaneously when requested.

This can be used in two ways. First, you an toggle Suppress bits in order to jump rapidly from one view to another. Second, you can utilize an spSeeing beacon whose suppress bit is always False to trigger the preloading of visual information by moving it into regions in the direction you are going in advance of your actually going to these regions. This will not change how much is rendered at any one time, but will mean changes of scene can happen faster.

22.4 spBeaconNew

sp spBeaconNew(spFixedAscii Tag)

Creates a new object of the class spBeacon, with the local process as its owner. In addition, sets the tag of the spBeacon as specified.

23 spSpeaking

public class spSpeaking extends spBeacon //* [+SendToContact]

The audio renderer has the built-in capability of creating a sound source corresponding to the sound captured by the microphone on a machine. To use this capability, you create an spSpeaking beacon. (The name is mnemonic for the fact that you use this kind of beacon as the `mouth' of an avatar.) The Transform of an spSpeaking beacon specifies the position and orientation of the sound created by the user.

The shared class spSpeaking inherits all the instance variables and functions of the classses spBeacon, spThing and sp. The class spSpeaking does not define any additional instance variables. The class spSpeaking defines the following functions:

Upon seeing the creation of an spSpeaking beacon, the appropriate audio rendering process creates an spAudioSource sound source with the Live bit set to True and makes this source be a child of the beacon. The audio renderer then starts outputting all the sound captured by the microphone through this source. Outputting continues until the beacon is removed. Note that to change the position of the source, one merely needs to change the position of the beacon that is its Parent.

23.1 spSpeakingNew

sp spSpeakingNew(spFixedAscii HostName)

Creates an spSpeaking beacon with a tag of the form "spAudio@" where the HostName is a DNS address string (e.g., "server.cs.hoople.und.edu"). If HostName is Null, the name of the machine you are on is used in the tag created.

When an audio rendering process running on machine X notices an spSpeaking beacon with the tag "spAudio@X", it creates an appropriate spAudioSource and starts to output sound. (This can be done for several spSpeaking beacons at once.)

24 spHearing

public class spHearing extends spBeacon //* [+SendToContact]

The audio renderer has the built-in capability of creating a localized sound image and playing it through the headphones connected to a machine. To use this capability, you create an spHearing beacon. (The name is mnemonic for the fact that you use this kind of beacon as the `ears' of an avatar.) The Transform of an spHearing beacon specifies the position and orientation from which sound is being observed.

The shared class spHearing inherits all the instance variables and functions of the classses spBeacon, spThing and sp. The class spHearing defines the following instance variables:

The class spHearing defines the following functions:

Upon seeing the creation of an spHearing beacon, the appropriate spAudio process starts routing localized sound from this observer to the headphones. This continues until the beacon is removed.

You can change the Focus and Live variables later, in which case the change will be followed (with up to a second or so delay) by the audio renderer. Note that to change the position from which sound is being observed, you merely need to change the position of the spHearing beacon.

24.1 Focus

shared public spAudioSource Focus; //* [sp]

The following access functions are available:

sp spHearingGetFocus(sp Object)
void spHearingSetFocus(sp Object, sp X)

spHeadphones objects have a shared instance variable called the Focus that specifies which surrounding sources will be included as part of the rendered audio. If a particular spAudioSource is specified as the Focus, then only the sound from that source will be received. Otherwise data from all nearby sources will be mixed together. The Focus can be used both to zero in on a particular sound source so that it can be heard clearly amid many others and to limit the amount of processing that is required to support audio rendering.

The default value of the Focus is Null. You can change it at will, but there may be a delay of a second or so before the change has any effect. (It is not expected that it will be changed often.) Information about the Focus is shared between the processes in a session.

24.2 Live

shared public boolean Live; //* [spBoolean]

The following access functions are available:

spBoolean spHearingGetLive(sp Object)
void spHearingSetLive(sp Object, spBoolean X)

spHeadphones objects have a shared instance variable called the Live bit, which specifies whether Live sources with the same owner should be included in the rendered sound image. Specifically, if the Live bit is False then Live spAudioSources that have the same owner as the spHearing beacon itself are ignored. if the Live bit is True, every source is treated the same. (This feature is used to suppress sound rendering of a user's spoken input in the headphones he is wearing.)

The default value of the live bit is False. You can change it at will, but there may be a delay of a second or so before the change has any effect. (It is not expected that it will be changed often.) Information about the live bit is shared between the processes in a session.

24.3 Format

shared public long Format; //* [spFormat]

The following access functions are available:

spFormat spHearingGetFormat(sp Object)
void spHearingSetFormat(sp Object, spFormat X)

spHearing objects have a shared instance variable called the Format, which controls the format being used when creating localized sound data. It can be set the zero with the meaning that the audio renderer should use whatever format it considers best.

When an spHearing beacon is created, the Format is set to zero. It can be changed later, but this change may take on the order of a second to take effect. Information about the Format is shared between the processes in a session.

24.4 spHearingNew

sp spHearingNew(spFixedAscii HostName)

Creates an spHearing beacon with a tag of the form "spAudio@" where the string HostName is a DNS address string (e.g., "server.cs.hoople.und.edu"). If HostName is Null, the name of the machine you are on is used in the tag created.

When an audio rendering process running on machine X notices an spHearing beacon with the tag "spAudio@X", it starts routing localized sound to the headphones. (Unless it is already occupied supporting a previous spHearing beacon.)

25 spSeeing

public class spSeeing extends spBeacon //* [+SendToContact]

The visual renderer has the built-in capability of rendered an image of the virtual world for the user to see. To use this capability, one creates an spSeeing beacon. (The name is mnemonic for the fact that you use this kind of beacon as the `eyes' of an avatar.) The Transform of an spSeeing beacon specifies the position and orientation from which the scene is being observed.

The shared class spSeeing inherits all the instance variables and functions of the classses spBeacon, spThing and sp. The class spSeeing defines the following instance variables:

The class spSeeing defines the following functions:

Upon seeing the creation of an spSeeing beacon, the appropriate visual renderer begins displaying images. This continues until the spSeeing beacon is removed (or the Suppress bit is set to True).

You can change the control variables in the spSeeing object later, in which case the change will be followed as closely as possible (with up to a second or so delay) by the visual renderer. Note that to change the viewpoint, you merely need to change the position and orientation of the spSeeing beacon.

25.1 FarClip

shared public float FarClip; //* [float]

The following access functions are available:

float spSeeingGetFarClip(sp Object)
void spSeeingSetFarClip(sp Object, float X)

The FarClip shared instance variable of spSeeing objects specifies the distance to the far clipping plain that should be used by the visual renderer. The FarClip is a float and is set to zero when an spSeeing is created. This indicates that the visual renderer should pick whatever value it feels is most appropriate. It can be changed later, but may take on the order of a second to take effect. Information about the FarClip is shared between the processes in a session.

25.2 NearClip

shared public float NearClip; //* [float]

The following access functions are available:

float spSeeingGetNearClip(sp Object)
void spSeeingSetNearClip(sp Object, float X)

The NearClip shared instance variable of spSeeing objects specifies the distance to the near clipping plain that should be used by the visual renderer. The NearClip is a float and is set to zero when an spSeeing is created. This indicates that the visual renderer should pick whatever value it feels is most appropriate. It can be changed later, but may take on the order of a second to take effect. Information about the NearClip is shared between the processes in a session.

25.3 Field

shared public float Field; //* [float]

The following access functions are available:

float spSeeingGetField(sp Object)
void spSeeingSetField(sp Object, float X)

The Field shared instance variable of spSeeing objects specifies the vertical viewing angle in radians that should be used by the visual rendering process. The Field is a float and is set to zero when an spSeeing is created. This indicates that the visual renderer should pick whatever value it feels is most appropriate. It can be changed later, but may take on the order of a second to take effect. Information about the Field is shared between the processes in a session.

25.4 Interval

shared public int Interval; //* [spDuration]

The following access functions are available:

spDuration spSeeingGetInterval(sp Object)
void spSeeingSetInterval(sp Object, spDuration X)

The interval shared instance variable of spSeeing objects specifies the desired time interval between frames in milliseconds. For instance, an interval of 33 specifies 30 frames per second. The Interval should be viewed as a target. It will not be possible for the visual renderer to achieve a very small Interval given a complex scene to render. The Interval is set to zero when an spSeeing is created. This indicates that the visual renderer should create frames as fast as possible. It can be changed later, but may take on the order of a second to take effect. Information about the Interval is shared between the processes in a session.

25.5 spSeeingNew

sp spSeeingNew(spFixedAscii HostName)

Creates an spSeeing beacon with a tag of the form "spVisual@" where the HostName is a DNS address string (e.g., "server.cs.hoople.und.edu"). If HostName is Null, the name of the machine you are on is used in the tag created.

When a visual rendering process running on machine X notices an spSeeing beacon with the tag "spVisual@X", it starts rendering. (Unless it is already occupied supporting a previous spSeeing beacon.)

26 spPOV

public class spPOV extends spThing

Point Of View (POV) objects control the communication between processes and therefore which objects are in the local world model copy. spPOVs exercise this control by triggering the receipt of information about the region they are in and typically about all nearby regions as well. Different subclasses of spPOV give access to different kinds of information. (spPOV objects themselves trigger the receipt of information that is relevant to computer simulations.)

The shared class spPOV inherits all the instance variables and functions of the classses spThing and sp. The class spPOV defines the following instance variables:

The class spPOV defines the following functions:

26.1 IgnoreNearby

shared public boolean IgnoreNearby; //* [spBoolean]

The following access functions are available:

spBoolean spPOVGetIgnoreNearby(sp Object)
void spPOVSetIgnoreNearby(sp Object, spBoolean X)

When False the IgnoreNearby shared instance variable of an spPOV causes the system to receive messages about objects in all the regions near the region the POV is in. If IgnoreNearby is True then the system attends only to the single region the POV is in.

The IgnoreNearby bit of an spPOV can be freely turned on and off. By default, the IgnoreNeighbors bit is False. Information about the IgnoreNearby is shared between the processes in a session.

27 spAction

public abstract class spAction extends sp //* [-SendToRegion]

Action objects contain functions that are executed during calls on spWMUpdate. Some actions are used to support operations such as callbacks that are part of the local computation required by an application. Other actions are used to reduce communication costs by making it possible to infrequently send small programs that make it unnecessary to frequently send data updates.

The class spAction is in the external API so that application writers can create new subclasses of spAction that support new kinds of activities. However, this is done infrequently. Much more common is the use of two specific kinds of actions: spCallback and spIntervalCallback. The discussion of how to create new subclasses of spActions is delayed until the discussion of spRemoteActions. There are a number of predefined subclasses of spActions, but only a select few are part of the external API.

The shared class spAction inherits all the instance variables and functions of the class sp. The class spAction defines the following instance variables:

The class spAction defines the following functions:

It is not possible to create instances of the class spAction. Rather, one can only create instances of particular subclasses of the class spAction.

Actions get triggered in two completely orthogonal ways, based on time intervals and based on events involving changes in objects. Triggering based on intervals is controlled by the Interval variable. An Interval value of I causes an action to be run every I milliseconds. A negative Interval value means that time having elapsed should never cause the action to run.

Triggering based on events is controlled by the Parent and Mask variables as well as a test defined as part of the action. Basically, the action is triggered when an object specified by the Parent matches the Mask and satisfies the test. If the Parent is Null, then the action is never triggered based on any events. If an action has both a non-negative Interval and a Parent, then it is triggered whenever either the Interval is satisfied or the event has occurred.

spActions attach special meaning to the Parent variable in addition to its standard meaning. In particular, if the Parent is a class descriptor, then the action is potentially applied to every object in that class (or its subclasses). If the Parent is not a class descriptor, then the action is only applied to the one object that is the Parent. The above notwithstanding, if an action is triggered by an Interval, then the action is applied to the Parent itself, be it a class, an object, or Null.

As in any object, the Parent variable of an action also controls what region the action is in and therefore how it will be communicated to other processes. In particular, if the Parent is Null then an action will not be communicated to any other processes. In addition, if the Parent is an spClass object, then the action will not be communicated anywhere except in the unlikely event that the spClass has a Parent that puts it in a region.

The Parent of an action must be set when the action is initially created, and cannot be changed later. (This is essential for efficiency. If you want to alter the Parent, you can do so by removing the action in question and creating a corresponding new action with a new Parent.)

If the object that is the Parent of an action is removed, then the action itself will also be removed the next time spWMUpdate is called. However, if the application of the action should be triggered by the removal of the object, then the action will be run before the action itself is removed.

By default, subclasses of spAction are not communicated to other processes; however, subclasses of spRemoteAction are. Actions are used to support operations such as callbacks that are part of the local computation required by an application. Remote actions are used primarily to reduce communication costs by (infrequently) sending a small program that makes it unnecessary to send frequent data updates.

27.1 Interval

shared public/ int Interval; //* [spDuration]

The following access functions are available:

spDuration spActionGetInterval(sp Object)

spActions have a shared instance variable called the Interval that specifies a time in milliseconds after which the action should be triggered. When an action is created, it is not immediately triggered, but rather only after the Interval has expired. If an action that was triggered due to its Interval expiring does not remove itself from the world model, then it is requeued to trigger again after the Interval expires again. For example, an spAction in the world model with an Interval of 100 will be triggered 10 times per second. An spAction with an Interval of zero will be called every time spWMUpdate is called. If the Interval for an action is negative, then the action is never triggered based on time, but rather only based on events.

The Interval for an action is set when an action is created and cannot be altered later. (This is essential for efficiency. If you want to alter the value, you can do so by removing the action in question and creating a corresponding new action with a new value.) Information about the Interval is shared between the processes in a session.

An action cannot be applied to objects more often than once each time spWMUpdate is called. As a result, if the Interval specified for an action is less than the Interval between calls on spWMUpdate, it cannot possibly be satisfied. Even if the Interval specified for an action is greater than the Interval between calls on spWMUpdate, the Interval can at best only be approximated, because the actual time the action is called must coincide with a call on spWMUpdate and there is considerably sloppiness in the underlying timing mechanisms that the system relies on. However, the system insures that for sufficiently large action Intervals, the average timing error experienced by a repetitively applied action tends toward zero.

27.2 Mask

shared public int Mask; //* [spMask]

The following access functions are available:

spMask spActionGetMask(sp Object)
void spActionSetMask(sp Object, spMask X)

spActions have a shared instance variable called the Mask that controls which objects the action can be applied to. The Mask is a world model visibility mask. Events that trigger the action must involve an object that matches the Mask of the action.

The default value of the Mask is spMaskNORMAL, which is appropriate for most situations. It can be changed to any other spMask value. Information about the Mask is shared between the processes in a session.

28 spCallback

public class spCallback extends spAction //* [-SendToRegion]

The class spCallback lets you easily define operations and tests locally and use them as event-triggered actions that run only in the local process. In particular, an spCallback object causes an operation to be applied to the object(s) specified by its Parent when an event occurs in the future.

spCallbacks cannot be communicated between processes. The reason why this must be so is that the system cannot communicate locally defined operations and tests. If you want an action that is communicated between processes, then you have to define a new subclass of spRemoteAction.

The shared class spCallback inherits all the instance variables and functions of the classses spAction and sp. The class spCallback defines the following instance variables:

The class spCallback defines the following functions:

As discussed above for actions in general, spCallbacks are triggered based on intervals and/or events. Whenever the Interval expires, F is applied. In addition, Whenever there has been a change in the shared instance variables of a relevant object, the spCallback predicate P is applied to the changed object to determine whether an event of interest has occurred. (What objects are relevant is determined by the Parent of the spCallback object as discussed above in conjunction with actions in general.)

If the spCallback predicate P returns True, then F is applied to the object in question as well. If F returns True, then the spCallback is removed. If not, the spCallback continues its monitoring.

For example, to be notified whenever any spSeeing object in the local world model moves, you might do the following.

C = spCallbackNew(spSeeingC(), -1, spChangedTransform, NULL, myFn, NULL)
You could turn this off as follows.
spRemove(C);
To be notified when any spSeeing object in the local world model moves, but in any event after ten seconds, you might do the following.
C = spCallbackNew(spSeeingC(), 10000, spChangedTransform, NULL, myFn, NULL)
To be notified purely based on an interval, use an spIntervalCallback.

28.1 P

native public/ spFn P; //* [spFn]

The following access functions are available:

spFn spCallbackGetP(sp Object)

SpCallback objects have a local instance variable called P that holds the locally defined predicate to be called when testing whether the callback should be applied. It must be set when an spCallback is created and cannot be changed later. Information about the P is maintained separately in each process.

28.2 PState

native public/ int PState; //* [void *]

The following access functions are available:

void * spCallbackGetPState(sp Object)

SpCallback objects have a local instance variable called PState that holds state information that is passed to the P operation when it is called. This must be set when an spCallback is created and cannot be changed later. Information about the PState is maintained separately in each process.

28.3 F

native public/ spFn F; //* [spFn]

The following access functions are available:

spFn spCallbackGetF(sp Object)

SpCallback objects have a local instance variable called F that holds the locally defined operation to be called when the spCallback is applied. It must be set when an spCallback is created and cannot be changed later. Information about the F is maintained separately in each process.

28.4 FState

native public/ int FState; //* [void *]

The following access functions are available:

void * spCallbackGetFState(sp Object)

SpCallback objects have a local instance variable called FState that holds state information that is passed to the F operation when it is called. This must be set when an spCallback is created and cannot be changed later. Information about the FState is maintained separately in each process.

28.5 spCallbackNew

sp spCallbackNew(sp X, spDuration I, spFn P, void * PState, spFn F, void * FState)

Creates as spCallback with the specified Parent X, Interval I, P, PState, F, and FState. None of the above can subsequently be changed. The Mask is set to spMaskNORMAL. You can change it to a different value. The callback can be terminated by removing the callback object.

29 spIntervalCallback

public class spIntervalCallback extends spCallback //* [-SendToRegion]

The class spIntervalCallback is a simplified version of spCallback that makes it easy to trigger an operation purely on the basis of time rather than by an event. It is the same as an spCallback except that you cannot specify a Parent object or a test. The Predicate is redefined, but the Function is inherited from spCallback.

Like spCallbacks, spIntervalCallbacks cannot be communicated between processes. The reason why this must be so is that the system cannot communicate locally defined operations. If you want an action that is communicated between processes, then you have to define a new subclass of spRemoteAction.

The shared class spIntervalCallback inherits all the instance variables and functions of the classses spCallback, spAction and sp. The class spIntervalCallback does not define any additional instance variables. The class spIntervalCallback defines the following functions:

29.1 spIntervalCallbackNew

sp spIntervalCallbackNew(spDuration Interval, spFn F, void * FState)

Creates an interval callback with the specified Interval, F, and FState. The Parent of the callback is set to Null. (Therefore, F will be called with Null as its first argument.) None of the above can subsequently be changed. The Mask is set to spMaskNORMAL. You can change it to a different value. An spIntervalCallback can be prematurely terminated by using spRemove to get rid of the spIntervalCallback object.

If the spIntervalCallback operation F returns False when it is called, then the spIntervalCallback is automatically reinstalled for interval milliseconds in the future. Otherwise, it is removed.

To have an operation myFn called every ten seconds, you might do the following.

C = spIntervalCallbackNew(10000, myFn, NULL)

30 spBeaconExamine

public class spBeaconExamine extends spCallback //* [-SendToRegion +SendToContact]

Applies an operation to every currently existing beacon with the indicated tag. This is done by first applying the operation to every beacon with the tag that is currently in the local world model copy. The spBeaconExamine callback then applies the operation to every newly appearing beacon with the tag. In addition, a request is sent to the beacon name service, which is answered by sending descriptions of every currently existing beacon with the indicated tag so that they can be entered in the local world model copy. As is typically the case with a name service, the reply only specifies beacons that currently exist. To check whether beacons appear in the future, you have to create new spBeaconExamine objects in the future.

The shared class spBeaconExamine inherits all the instance variables and functions of the classses spCallback, spAction and sp. The class spBeaconExamine defines the following instance variables:

The class spBeaconExamine defines the following functions:

30.1 Tag

shared public/ String Tag; //* [spFixedAscii]

The following access functions are available:

spFixedAscii spBeaconExamineGetTag(sp Object)

The Tag shared instance variable of spBeaconExamine object specifies the tag of the beacons to be examined. The Tag is set when the spBeaconExamine object is first created and cannot be changed later. Information about the Tag is shared between the processes in a session.

30.2 spBeaconExamineNew

sp spBeaconExamineNew(spFixedAscii Tag, spFn F, void * FState)

Creates an spBeaconExamine object with the indicated Tag, F, and Fstate. If the operation F ever returns True, the spBeaconExamine object is removed and examining ceases.

Examining happens in two stages. At the moment the spBeaconExamine object is created, an spClassExamine is done to locate any beacons in the local world model copy with the specified tag. If F returns True during this process, then examining is terminated and the spBeaconExamine object created is set up with an operation that will cause it to remove itself the first time it gets an opportunity to run.

If the examination of the objects currently in the world model does not cause F to return True, then F is used as the F value of the spBeaconExamine callback created. The beacon name service is contacted to obtain information about all beacons that currently exist with the indicated Tag in the global world model. Examining continues on the beacons that come from the name service.

In the callback created, the Parent is the class spBeacon and the Interval is set to a system specified value that causes the spBeaconExamine to timeout and apply F (to spBeaconC() after the name service has (with very high probability) communicated all currently existing beacons with the indicated Tag to the local process.

31 spRemoteAction

public abstract class spRemoteAction extends spAction //* [+SendToRegion]

The class spRemoteAction is identical to spAction except that spRemoteAction objects are communicated to other processes using region-based communication rather than being run purely locally. It is expected that applications that define their own actions will typically define remote actions.

The shared class spRemoteAction inherits all the instance variables and functions of the classses spAction and sp. The class spRemoteAction does not define any additional instance variables. The class spRemoteAction defines the following functions:

It is not possible to create instances of the class spRemoteAction. Rather, one can only create instances of particular subclasses of the class spRemoteAction.

31.1 spRemoteActionNew

sp spRemoteActionNew(sp Parent, spDuration Interval)

Each action has a creation function that makes instances of the class. In the classes spAction and spRemoteAction, the creation function does not exist. It is described here to show how the creation functions for subclasses of spAction and spRemoteAction must be defined. In particular, the Parent and Interval instance variables of an action must be set at the time the action is created and cannot be changed later. The creation function must either have arguments corresponding to these variables or set the variables to fixed values.

31.2 spRemoteActionPredicate

spBoolean spRemoteActionPredicate(sp Action, sp Object)

Each action class is associated with a Predicate that defines the events that are a primary determiner of when the action will be triggered. In the class spRemoteAction, this method has a default value that always returns False. Action classes that inherit this default definition of the Predicate are triggered purely based on their Intervals.

The Predicate is described more generally here to show how the Predicate methods for subclasses of spAction and spRemoteAction are defined. In particular, they must take the arguments shown and return a boolean value.

Action event processing proceeds as follows. Each time spWMUpdate is called, each action is considered for possible event-driven triggering. For a given action A, the system first determines the set of objects compatible with the parent of A. If the parent is an spClass object, this is every object that is in that class or any of its subclasses. If the parent is an ordinary object then this is the only object that needs to be considered. If the parent is Null then there are no compatible objects and no events ever get triggered.

The set of compatible objects is then refined as follows. Every object that does not have some shared variable that has changed since the last call on spWMUpdate is discarded. Then, every object that is not compatible with the action's mask is discarded. The Predicate is then applied to each of the remaining objects, and every object for which the Predicate returns False is also discarded. The action Function is then applied to every object that remains. (For example, the Predicate might specify that Function be applied only when the position of the object under consideration has changed.)

The Predicate for a subclass of spAction should be purely a function, not having any side-effects anywhere. In addition, it is very important that the Predicate run quickly---i.e., in a few microseconds at most---because it is being called from inside spWMUpdate and not in a separate thread.

As an example, the following pseudo-code shows how the Predicate of spCallback is defined.

spBoolean spCallbackPredicate(sp callback, sp object) {
  spFn P = spCallbackGetP(callback);
  void * PState = spCallbackGetPState(callback);
  return P(object, PState);
}

31.3 spRemoteActionFunction

spBoolean spRemoteActionFunction(sp Action, sp Object)

Each action class is associated with a Function that performs the action in question. In particular, the Function is called whenever the action is triggered. The Function does not exist in the class spAction or spRemoteAction. It is described here to show how the Function methods for subclasses of spAction and spRemoteAction must be defined. In particular, they must take the arguments shown and return a boolean value.

If an action is triggered due to its Interval, then the Function is called with the Parent of the action as its second argument. If the action is triggered by an event, the action is applied to the object that triggered the event.

The purpose of the Function is to perform some operation, possible modifying the target object and potentially accumulating a summary value. (For example, an action supporting smooth motion might interpolate between positions at particular times to determine intermediate positions.)

The return value of Function specifies what should happen to the action after it is applied to an object. If Function returns True, then the action is removed, but only from the local world model copy. In particular, even if the action is being applied in the process that owns it, the removal occurs only in the local world model copy with no effect on the world model in any other process. This is important so that the copies of the action in other processes can run to normal completion without having their existence terminated prematurely.

Subclasses of spAction and spRemoteAction typically contain additional instance variables for the purpose of representing state information. An action Function can modify objects in the world model; however, the action Function is considered to be a proxy for the process that created the action. Therefore, the action Function is only allowed to modify objects that have the same owner as the action itself. As a general matter, actions Functions should never delete or create objects.

It is very important that Function run quickly---i.e., in much less than 1 Millisecond---because it is being called from inside spWMUpdate and not in a separate thread. If you have a computation intensive thing to do, put it in a thread triggered by an action.

As an example, the following pseudo-code shows how the Function of spCallback is defined.

spBoolean spCallbackFunction(sp callback, sp object) {
  spFn F = spIntervalCallbackGetF(callback);
  void * FState = spIntervalCallbackGetFState(callback);
  return F(object, FState);
}

32 spOwnershipRequest

public class spOwnershipRequest extends spRemoteAction

Each shared object has only one owner and only the owner can alter any of the shared instance variables of an object. This is an important restriction that is essential for achieving consistent real-time interaction. However, it can be constricting when groups of users wish to exercise joint control over an object. To facilitate joint control of objects, the ownership of an object can be transferred easily from one process to another by merely setting the Owner variable. However, this can only be done by the present owner.

To further facilitate the transfer of ownership, a handshaking protocol centered around a special shared object called an spOwnershipRequest is provided to make it easy for a process to ask for and obtain ownership of an object. The basic protocol is that someone who wishes to get ownership of an object creates an spOwnershipRequest whose Parent is the object. The current owner of the object can choose to grant the request, in which case ownership is transferred.

The shared class spOwnershipRequest inherits all the instance variables and functions of the classses spRemoteAction, spAction and sp. The class spOwnershipRequest defines the following instance variables:

The class spOwnershipRequest defines the following functions:

The object that is the target of the ownership request is indicated by making it be the Parent of the ownership request object. The Parent is set when the ownership request is first made and cannot be changed thereafter.

spOwnershipRequest objects are communicated between processes, but the specified operation is only called in the creating process.

32.1 Ownership Transfer

The owner of an object can force the object on another process. However, the transfer of ownership of an object typically involves two processes: a requesting process that decides it wants to obtain ownership and the current owner who is the only one that can change who the owner is. Moreover, once the owner has been changed, the old owner can no longer do anything with the object. Therefore, the new owner must realize that it has become the owner and take over.

In recognition of the above, the ownership transfer protocol operates in three basic steps.

(1) A process that wants to gain ownership creates a request. The request must be left in existence for a substantial time (seconds) because otherwise, the request might come and go in the world model so quickly that a slowly reacting owner might never notice its existence.

(2) A process that owns an object it might be willing to give up monitors the world model looking for the appearance of appropriate ownership requests. To grant a request, the owner calls spOwnershipRequestGrant, which effects the change. After that time, the old owner need take no further action.

Being on the lookout for appropriate ownership requests is best done with a callback. In particular, the callback predicate spRelevantOwnershipRequest exists to make this easy. Using this callback predicate causes the callback operation to be applied whenever a new callback request appears that is asking for an object owned by the local process. For example you might write:

spCallbackNew(spOwnershipRequestC(),-1,spRelevantOwnershipRequest, NULL, F, NULL);

If it is possible that there are preexisting spOwnershipRequests that are relevant, then you should do an spClassExamine to find them before setting up the callback above.

(3) The process that wants to gain ownership must monitor the world model to determine whether the request is granted. This is done by the spOwnershipRequest callback itself.

It is worthy of note that joint control over an object can also be obtained by having a central neutral broker that controls the object in response to requests from other processes. This might be a convenient way to handle the ball in a virtual soccer game, especially in situations where the ball is far from any player. However, it has the problem that it increases the latency of interaction with the object in comparison to having the processes that is effecting the object most at any given moment be the owner of the object and effect it directly.

32.2 F

native public/ spFn F; //* [spFn]

The following access functions are available:

spFn spOwnershipRequestGetF(sp Object)

SpOwnershipRequest objects have a local instance variable called F that holds the locally defined operation to be called when the ownership request succeeds or times out. It must be set when an SpOwnershipRequest is created and cannot be changed later. Information about the F is maintained separately in each process.

32.3 FState

native public/ int FState; //* [void *]

The following access functions are available:

void * spOwnershipRequestGetFState(sp Object)

spOwnershipRequest objects have a local instance variable called FState that holds state information that is passed to the F operation when it is called. This must be set when an spOwnershipRequest is created and cannot be changed later. Information about the FState is maintained separately in each process.

32.4 spOwnershipRequestNew

sp spOwnershipRequestNew(sp Object, spFn F, void * FState, spDuration Timeout)

Creates an spOwnershipRequest object seeking ownership of an object. It is up to the current owner of the object to decide whether to grant a given ownership request.

The spOwnershipRequest action created applies F to the target object either when the timeout interval is reached or when the owner of the object changes to spWMGetMe. It is up to F to detect which has occurred (by looking at the owner variable of Object) and decide what to do next.

In either case, the spOwnershipRequest removes itself as soon as it is triggered. A negative Timeout causes the request to never Timeout. It the Timeout is not negative, it must be greater than 2 seconds. The Interval of the spOwnershipRequest is set to 1 second less than Timeout. Only on the local machine, the spOwnershipRequest is requeued for a final second before timing out.

In the spOwnershipRequest created, the Parent is the target object. Note that ownership request objects are communicated between processes. However, the operation F is only called in the process that created the spOwnershipRequest.

(It is possible to cancel an ownership request by removing the spOwnershipRequest object. However, this is unwise because you might then fail to notice that another process granted the request just before you removed it.)

If a process creates an ownership request whose target is an object it already owns, then everything proceeds in the same way as above. How long it takes before F is applied to Object depends on whether the process grants ownership of the object to itself or not.

32.5 spOwnershipRequestGrant

void spOwnershipRequestGrant(sp Request)

If the process owning an object decides to grant an ownership request, it does so by calling spOwnershipRequestGrant on the spOwnershipRequest object in question. If the process does not wish to grant the request, then it need not take any action.

A Java Declaration File

The following shows a Java definition appropriate for input to SPOT that corresponds to the Open Community Version 0.9 API presented in this document. Since all of the functions are part of the system core, they are all native. In the actual full Java definition of the system, there are additional functions that are used by the system core, but not shown here. The functions spWMRegister and spWMDeregister are not included in the Java below, because they are not part of the Java API and cannot be expressed in Java.

The Java code below is included both as an extended example of SPOT input and as a concise summary of what is available in the Open Community Version 0.9 API. Discussions of the variables and functions shown can be found by looking them up in the table of contents of this documentation. They appear in the same order below as in the table of contents.

public class spWM {
  protected int CPtr;
  native public static int Me;                                //* [spName]
  native public/ static int MainOwner;                        //* [spName]
  native public static String Error;                          //* [char *:500]
  native public/ static String LastError;                     //* [char *:500]
  native public/ static spDuration Interval;                  //* [spDuration]
  native public static spDuration DesiredInterval;            //* [spDuration]
  native public static int Window;                            //* [spWindow]
  native static int spWM.New(String Server, spTransferVector V);
   //* [spWM spWMNew(char * Server, spTransferVector V)]
  native static void spWM.Remove();
   //* [void spWMRemove()]
  native static void spWM.Update();
   //* [void spWMUpdate()]
  native static int spWM.GenerateOwner();
   //* [spName spWMGenerateOwner()]
  native static void spWM.ReportError(sp Object, int Code, String Description);
   //* [void spWMReportError(sp Object, long Code, char *:500 Description)]
}
public class spFn {
  public abstract boolean F(sp object);
}
public class spMask {
  native public static final int MINE = 1;                    //* [spMask]
  native public static final int OTHERS = 2;                  //* [spMask]
  native public static final int NORMAL = 3;                  //* [spMask]
}
public class spTransform {
  native public static final int X = 0;                       //* [long]
  native public static final int Y = 1;                       //* [long]
  native public static final int Z = 2;                       //* [long]
  native public static final int RX = 3;                      //* [long]
  native public static final int RY = 4;                      //* [long]
  native public static final int RZ = 5;                      //* [long]
  native public static final int RA = 6;                      //* [long]
  native public static final int SX = 7;                      //* [long]
  native public static final int SY = 8;                      //* [long]
  native public static final int SZ = 9;                      //* [long]
  native public static final int SOX = 10;                    //* [long]
  native public static final int SOY = 11;                    //* [long]
  native public static final int SOZ = 12;                    //* [long]
  native public static final int SOA = 13;                    //* [long]
  native public static final int CX = 14;                     //* [long]
  native public static final int CY = 15;                     //* [long]
  native public static final int CZ = 16;                     //* [long]
  native static float[] spTransform.Copy(float[] Destination, float[] Source);
   //* [spTransform:17 spTransformCopy(spTransform:17 Destination, spTransform:17 Source)]
  native static float[] spTransform.FromIdent(float[] Transform);
   //* [spTransform:17 spTransformFromIdent(spTransform:17 Transform)]
  native static float[] spTransform.GetTranslation(float[] Transform);
   //* [spVector:3 spTransformGetTranslation(spTransform:17 Transform)]
  native static float[] spTransform.SetTranslation(float[] Transform, float[] Translation);
   //* [spTransform:17 spTransformSetTranslation(spTransform:17 Transform, spVector:3 Translation)]
  native static float[] spTransform.GetRotation(float[] Transform);
   //* [spRotation:4 spTransformGetRotation(spTransform:17 Transform)]
  native static float[] spTransform.SetRotation(float[] Transform, float[] Rotation);
   //* [spTransform:17 spTransformSetRotation(spTransform:17 Transform, spRotation:4 Rotation)]
  native static float[] spTransform.GetScale(float[] Transform);
   //* [spVector:3 spTransformGetScale(spTransform:17 Transform)]
  native static float[] spTransform.SetScale(float[] Transform, float[] Vector);
   //* [spTransform:17 spTransformSetScale(spTransform:17 Transform, spVector:3 Vector)]
  native static float[] spTransform.GetScaleOrientation(float[] Transform);
   //* [spRotation:4 spTransformGetScaleOrientation(spTransform:17 Transform)]
  native static float[] spTransform.SetScaleOrientation(float[] Transform, float[] R);
   //* [spTransform:17 spTransformSetScaleOrientation(spTransform:17 Transform, spRotation:4 R)]
  native static float[] spTransform.GetCenter(float[] Transform);
   //* [spVector:3 spTransformGetCenter(spTransform:17 Transform)]
  native static float[] spTransform.SetCenter(float[] Transform, float[] Center);
   //* [spTransform:17 spTransformSetCenter(spTransform:17 Transform, spVector:3 Center)]
}
public class spVector {
  native public static final int X = 0;                       //* [long]
  native public static final int Y = 1;                       //* [long]
  native public static final int Z = 2;                       //* [long]
  native public static final float[] ZERO = {0.0, 0.0, 0.0};  
          //* [spVector:3=((spVector)spVectorDataZero)]
  native public static final float[] AXISX = {1.0, 0.0, 0.0}; 
          //* [spVector:3=((spVector)spVectorDataAxisX)]
  native public static final float[] AXISY = {10.0, 1.0, 0.0}; 
          //* [spVector:3=((spVector)spVectorDataAxisY)]
  native public static final float[] AXISZ = {10.0, 0.0, 1.0}; 
          //* [spVector:3=((spVector)spVectorDataAxisZ)]
  native static float[] spVector.Copy(float[] Destination, float[] Source);
   //* [spVector:3 spVectorCopy(spVector:3 Destination, spVector:3 Source)]
  native static float[] spVector.SetFromScalar(float[] Vector, float Scalar);
   //* [spVector:3 spVectorSetFromScalar(spVector:3 Vector, float Scalar)]
  native static boolean spVector.Equals(float[] A, float[] B);
   //* [spBoolean spVectorEquals(spVector:3 A, spVector:3 B)]
  native static boolean spVector.EqualsDelta(float[] A, float[] B, float Tolerance);
   //* [spBoolean spVectorEqualsDelta(spVector:3 A, spVector:3 B, float Tolerance)]
  native static float[] spVector.Add(float[] A, float[] B);
   //* [spVector:3 spVectorAdd(spVector:3 A, spVector:3 B)]
  native static float[] spVector.Subtract(float[] A, float[] B);
   //* [spVector:3 spVectorSubtract(spVector:3 A, spVector:3 B)]
  native static float[] spVector.MultiplyByScalar(float[] Vector, float Scalar);
   //* [spVector:3 spVectorMultiplyByScalar(spVector:3 Vector, float Scalar)]
  native static float[] spVector.DivideByScalar(float[] Vector, float Scalar);
   //* [spVector:3 spVectorDivideByScalar(spVector:3 Vector, float Scalar)]
  native static float[] spVector.CrossProduct(float[] A, float[] B);
   //* [spVector:3 spVectorCrossProduct(spVector:3 A, spVector:3 B)]
  native static float spVector.DotProduct(float[] A, float[] B);
   //* [float spVectorDotProduct(spVector:3 A, spVector:3 B)]
  native static float[] spVector.ComposeScales(float[] A, float[] B);
   //* [spVector:3 spVectorComposeScales(spVector:3 A, spVector:3 B)]
  native static float spVector.Length(float[] Vector);
   //* [float spVectorLength(spVector:3 Vector)]
  native static float[] spVector.Normalize(float[] Vector);
   //* [spVector:3 spVectorNormalize(spVector:3 Vector)]
}
public class spRotation {
  native public static final int X = 0;                       //* [long]
  native public static final int Y = 1;                       //* [long]
  native public static final int Z = 2;                       //* [long]
  native public static final int A = 3;                       //* [long]
  native static float[] spRotation.Copy(float[] Destination, float[] Source);
   //* [spRotation:4 spRotationCopy(spRotation:4 Destination, spRotation:4 Source)]
  native static float[] spRotation.FromIdent(float[] Rotation);
   //* [spRotation:4 spRotationFromIdent(spRotation:4 Rotation)]
  native static float[] spRotation.GetAxis(float[] Rotation);
   //* [spVector:3 spRotationGetAxis(spRotation:4 Rotation)]
  native static float[] spRotation.SetAxis(float[] Rotation, float[] Axis);
   //* [spRotation:4 spRotationSetAxis(spRotation:4 Rotation, spVector:3 Axis)]
  native static float spRotation.GetAngle(float[] Rotation);
   //* [float spRotationGetAngle(spRotation:4 Rotation)]
  native static float[] spRotation.SetAngle(float[] Rotation, float Angle);
   //* [spRotation:4 spRotationSetAngle(spRotation:4 Rotation, float Angle)]
  native static float[] spRotation.ToQuat(float[] Rotation, float[] Quat);
   //* [spQuaternion:4 spRotationToQuat(spRotation:4 Rotation, spQuaternion:4 Quat)]
  native static float[] spRotation.FromQuat(float[] Rotation, float[] Quat);
   //* [spRotation:4 spRotationFromQuat(spRotation:4 Rotation, spQuaternion:4 Quat)]
  native static float[] spRotation.ToAngles(float[] Rotation, float[] Vector);
   //* [spVector:3 spRotationToAngles(spRotation:4 Rotation, spVector:3 Vector)]
  native static float[] spRotation.FromAngles(float[] Rotation, float[] Angles);
   //* [spRotation:4 spRotationFromAngles(spRotation:4 Rotation, spVector:3 Angles)]
  native static float[] spRotation.Mult(float[] A, float[] B);
   //* [spRotation:4 spRotationMult(spRotation:4 A, spRotation:4 B)]
  native static float[] spRotation.LookAt(float[] R, float[] From, float[] To, float[] Up);
   //* [spRotation:4 spRotationLookAt(spRotation:4 R, spVector:3 From, spVector:3 To, spVector:3 Up)]
}
public class spQuaternion {
  native static float[] spQuaternion.Copy(float[] Destination, float[] Source);
   //* [spQuaternion:4 spQuaternionCopy(spQuaternion:4 Destination, spQuaternion:4 Source)]
  native static float[] spQuaternion.FromIdent(float[] Quaternion);
   //* [spQuaternion:4 spQuaternionFromIdent(spQuaternion:4 Quaternion)]
  native static float[] spQuaternion.Mult(float[] A, float[] B);
   //* [spQuaternion:4 spQuaternionMult(spQuaternion:4 A, spQuaternion:4 B)]
}
public class spMatrix {
  native static float[] spMatrix.Copy(float[] Destination, float[] Source);
   //* [spMatrix:16 spMatrixCopy(spMatrix:16 Destination, spMatrix:16 Source)]
  native static float[] spMatrix.FromIdent(float[] Matrix);
   //* [spMatrix:16 spMatrixFromIdent(spMatrix:16 Matrix)]
  native static float[] spMatrix.GetTranslation(float[] Matrix);
   //* [spVector:3 spMatrixGetTranslation(spMatrix:16 Matrix)]
  native static float[] spMatrix.SetTranslation(float[] Matrix, float[] Translation);
   //* [spMatrix:16 spMatrixSetTranslation(spMatrix:16 Matrix, spVector:3 Translation)]
  native static float[] spMatrix.FromTransform(float[] Matrix, float[] Transform);
   //* [spMatrix:16 spMatrixFromTransform(spMatrix:16 Matrix, spTransform:17 Transform)]
  native static float[] spMatrix.ToTransform(float[] Matrix, float[] Transform);
   //* [spTransform:17 spMatrixToTransform(spMatrix:16 Matrix, spTransform:17 Transform)]
  native static float[] spMatrix.Inverse(float[] spMatrix);
   //* [spMatrix:16 spMatrixInverse(spMatrix:16 spMatrix)]
  native static float[] spMatrix.Mult(float[] A, float[] B);
   //* [spMatrix:16 spMatrixMult(spMatrix:16 A, spMatrix:16 B)]
  native static float[] spMatrix.MultVector(float[] Matrix, float[] Vector);
   //* [spVector:3 spMatrixMultVector(spMatrix:16 Matrix, spVector:3 Vector)]
}
public class spFormat {
  native public static final long LINEAR8MONO16 = 0x1F40000000100001; //* [spFormat]
  native public static final long LINEAR16MONO16 = 0x3E80000000100001; //* [spFormat]
  native public static final long LINEAR32MONO16 = 0x7D00000000100001; //* [spFormat]
  native static int spFormat.DurationFromLength(long Format, int Bytes);
   //* [spDuration spFormatDurationFromLength(spFormat Format, long Bytes)]
  native static int spFormat.LengthFromDuration(long Format, int Duration);
   //* [long spFormatLengthFromDuration(spFormat Format, spDuration Duration)]
}
public abstract class sp { //* [+SendToRegion -SendToContact]
  protected int CPtr;
  shared public/ spClass Class;                               //* [sp]
  shared public int Owner;                                    //* [spName]
  shared public sp Parent;                                    //* [sp]
  shared public/ boolean IsRemoved;                           //* [spBoolean]
  native public/ boolean IsNew;                               //* [spBoolean]
  native public int AppData;                                  //* [void *]
  native public static final float DEGREES = 0.0174532925199; //* [float]
  native static spClass sp.C();
   //* [sp spC()]
  native static abstract int sp.New();
   //* [sp spNew()]
  native void sp.Initialization();
   //* [void spInitialization(sp Object)]
  native void sp.Remove();
   //* [void spRemove(sp Object)]
  native void sp.ExamineChildren(int Mask, spFn F);
   //* [void spExamineChildren(sp Object, spMask Mask, spFn F, void * Data)]
  native void sp.ExamineDescendants(int Mask, spFn F);
   //* [void spExamineDescendants(sp Object, spMask Mask, spFn F, void * Data)]
  native sp sp.Topmost();
   //* [sp spTopmost(sp Object)]
  native void sp.Print();
   //* [void spPrint(sp Object)]
}
public class spRegion extends sp { //* [-SendToRegion +SendToContact]
  shared public/ spBoundary Boundary;                         //* [sp]
  native static int spRegion.New(spBoundary Boundary);
   //* [sp spRegionNew(sp Boundary)]
}
public abstract class spLink extends sp {
  shared public/ String URL;                                  //* [spFixedAscii]
  native public/ int Data;                                    //* [void *]
  native static abstract int spLink.New(String URL);
   //* [sp spLinkNew(spFixedAscii URL)]
  native abstract void spLink.ReadData();
   //* [void spLinkReadData(sp Link)]
}
public class spVisualDefinition extends spLink {
  native static int spVisualDefinition.New(String URL);
   //* [sp spVisualDefinitionNew(spFixedAscii URL)]
}
public class spSound extends spLink {
  native public/ int Duration;                                //* [spDuration]
  native static int spSound.New(String URL);
   //* [sp spSoundNew(spFixedAscii URL)]
  native spRemoteAction spSound.Play(spAudioSource Source, boolean Loop, float Gain);
   //* [sp spSoundPlay(sp Sound, sp Source, spBoolean Loop, float Gain)]
}
public class spBoundary extends spLink { //* [+SendToContact]
  native public/ float[] Min;                                 //* [spVector:3]
  native public/ float[] Max;                                 //* [spVector:3]
  native static int spBoundary.New(String URL);
   //* [sp spBoundaryNew(spFixedAscii URL)]
  native boolean spBoundary.Below(float[] P, float[] Q);
   //* [spBoolean spBoundaryBelow(sp Boundary, spVector:3 P, spVector:3 Q)]
  native boolean spBoundary.Above(float[] P, float[] Q);
   //* [spBoolean spBoundaryAbove(sp Boundary, spVector:3 P, spVector:3 Q)]
  native boolean spBoundary.Inside(float[] P);
   //* [spBoolean spBoundaryInside(sp Boundary, spVector:3 P)]
}
public class spClass extends spLink {
  shared public/ spClass Superclass;                          //* [sp]
  native public/ String ClassName;                            //* [spAscii32:32]
  native int spClass.NewObj(int Extra);
   //* [sp spClassNewObj(sp ClassDescriptor, long Extra)]
  native int spClass.NewLink(String URL);
   //* [sp spClassNewLink(sp ClassDescriptor, spFixedAscii URL)]
  native static int spClass.New(String URL, spClass Superclass);
   //* [sp spClassNew(spFixedAscii URL, sp Superclass)]
  native boolean spClass.Leq(spClass Superclass);
   //* [spBoolean spClassLeq(sp Subclass, sp Superclass)]
  native boolean spClass.Examine(int Mask, spFn F);
   //* [spBoolean spClassExamine(sp C, spMask Mask, spFn F, void * Data)]
  native spCallback spClass.Monitor(int Mask, spFn F);
   //* [sp spClassMonitor(sp C, spMask Mask, spFn F, void * Data)]
}
public class spThing extends sp {
  shared public float[] Transform;                            //* [spTransform:17]
  shared public spVisualDefinition VisualDefinition;          //* [sp]
  shared public float InRadius;                               //* [float]
  shared public float OutRadius;                              //* [float]
  native float[] spThing.Matrix();
   //* [spMatrix:16 spThingMatrix(sp Thing)]
  native float[] spThing.MatrixInverse();
   //* [spMatrix:16 spThingMatrixInverse(sp Thing)]
  native void spThing.SetTransform(float[] Transform);
   //* [void spThingSetTransform(sp Thing, spTransform:17 Transform)]
  native boolean spThing.Localize(spRegion Region, boolean ChooseSmallest);
   //* [spBoolean spThingLocalize(sp Thing, sp Region, spBoolean ChooseSmallest)]
  native float[] spThing.RelativeMatrix(spThing Thing, float[] Matrix);
   //* [spMatrix:16 spThingRelativeMatrix(sp POV, sp Thing, spMatrix:16 Matrix)]
  native float[] spThing.RelativeVector(spThing POV, float[] Vector);
   //* [spVector:3 spThingRelativeVector(sp Thing, sp POV, spVector:3 Vector)]
  native float spThing.Distance(spThing POV);
   //* [float spThingDistance(sp Thing, sp POV)]
  native void spThing.LookAt(spThing Target);
   //* [void spThingLookAt(sp Thing, sp Target)]
}
public class spRoot extends spThing {
}
public class spAvatar extends spRoot {
  shared public boolean IsBot;                                //* [spBoolean]
}
public class spAudioSource extends spThing {
  shared public boolean Live;                                 //* [spBoolean]
  native public/ long Format;                                 //* [spFormat]
  native public/ int Duration;                                //* [spDuration]
  native void spAudioSource.Setup(boolean W, boolean R, long F, int D);
   //* [void spAudioSourceSetup(sp S, spBoolean W, spBoolean R, spFormat F, spDuration D)]
  native void spAudioSource.Write(String Data);
   //* [void spAudioSourceWrite(sp Source, char * Data)]
  native String spAudioSource.Read();
   //* [char * spAudioSourceRead(sp Source)]
}
public class spBeacon extends spThing { //* [+SendToContact]
  shared public/ String Tag;                                  //* [spFixedAscii]
  shared public boolean Suppress;                             //* [spBoolean]
  native static int spBeacon.New(String Tag);
   //* [sp spBeaconNew(spFixedAscii Tag)]
}
public class spSpeaking extends spBeacon { //* [+SendToContact]
  native static int spSpeaking.New(String HostName);
   //* [sp spSpeakingNew(spFixedAscii HostName)]
}
public class spHearing extends spBeacon { //* [+SendToContact]
  shared public spAudioSource Focus;                          //* [sp]
  shared public boolean Live;                                 //* [spBoolean]
  shared public long Format;                                  //* [spFormat]
  native static int spHearing.New(String HostName);
   //* [sp spHearingNew(spFixedAscii HostName)]
}
public class spSeeing extends spBeacon { //* [+SendToContact]
  shared public float FarClip;                                //* [float]
  shared public float NearClip;                               //* [float]
  shared public float Field;                                  //* [float]
  shared public int Interval;                                 //* [spDuration]
  native static int spSeeing.New(String HostName);
   //* [sp spSeeingNew(spFixedAscii HostName)]
}
public class spPOV extends spThing {
  shared public boolean IgnoreNearby;                         //* [spBoolean]
}
public abstract class spAction extends sp { //* [-SendToRegion]
  shared public/ int Interval;                                //* [spDuration]
  shared public int Mask;                                     //* [spMask]
}
public class spCallback extends spAction { //* [-SendToRegion]
  native public/ spFn P;                                      //* [spFn]
  native public/ int PState;                                  //* [void *]
  native public/ spFn F;                                      //* [spFn]
  native public/ int FState;                                  //* [void *]
  native static int spCallback.New(sp X, int I, spFn P, spFn F);
   //* [sp spCallbackNew(sp X, spDuration I, spFn P, void * PState, spFn F, void * FState)]
}
public class spIntervalCallback extends spCallback { //* [-SendToRegion]
  native static int spIntervalCallback.New(int Interval, spFn F);
   //* [sp spIntervalCallbackNew(spDuration Interval, spFn F, void * FState)]
}
public class spBeaconExamine extends spCallback { //* [-SendToRegion +SendToContact]
  shared public/ String Tag;                                  //* [spFixedAscii]
  native static int spBeaconExamine.New(String Tag, spFn F);
   //* [sp spBeaconExamineNew(spFixedAscii Tag, spFn F, void * FState)]
}
public abstract class spRemoteAction extends spAction { //* [+SendToRegion]
  native static abstract int spRemoteAction.New(sp Parent, int Interval);
   //* [sp spRemoteActionNew(sp Parent, spDuration Interval)]
  native boolean spRemoteAction.Predicate(sp Object);
   //* [spBoolean spRemoteActionPredicate(sp Action, sp Object)]
  native abstract boolean spRemoteAction.Function(sp Object);
   //* [spBoolean spRemoteActionFunction(sp Action, sp Object)]
}
public class spOwnershipRequest extends spRemoteAction {
  native public/ spFn F;                                      //* [spFn]
  native public/ int FState;                                  //* [void *]
  native static int spOwnershipRequest.New(sp Object, spFn F, int Timeout);
   //* [sp spOwnershipRequestNew(sp Object, spFn F, void * FState, spDuration Timeout)]
  native void spOwnershipRequest.Grant();
   //* [void spOwnershipRequestGrant(sp Request)]
}

B Quick Function Reference

The following is a quick reference and index for all the functions in the Open Community Version 0.9 API. Each entry shows the complete signature of a function and the page on which it is described in detail.

For greater convenience in looking up the functions, the entries are ordered in a somewhat unusual way. To start with, they are sorted only by the names of the functions rather than other aspects of the signature. Further, they are not sorted merely by the lexicographic order of the function names, but rather by a more complex order.

For ordinary functions, the entries are sorted primarily on the name of the method itself and secondarily on the name of the class that contains the method. That is to say, spClassExamine is ordered primarily by ``Examine" and only secondarily by ``spClass''.

For instance variable accessors, the entries are sorted primarily on the name of the variable itself, secondarily on the name of the class, and tertiarily on the actual function name itself. That is to say spClassGetSuperclass is ordered primarily by ``Superclass'', secondarily under ``spClass'', and only then under ``Get''.

A in spRotation
#define spRotationA 3
AXISX in spVector
#define spVectorAXISX ((spVector)spVectorDataAxisX)
AXISY in spVector
#define spVectorAXISY ((spVector)spVectorDataAxisY)
AXISZ in spVector
#define spVectorAXISZ ((spVector)spVectorDataAxisZ)
Above in spBoundary
spBoolean spBoundaryAbove(sp Boundary, spVector P, spVector Q)
Add in spVector
spVector spVectorAdd(spVector A, spVector B)
AppData in sp
void * spGetAppData(sp Object)
AppData in sp
void spSetAppData(sp Object, void * X)
Below in spBoundary
spBoolean spBoundaryBelow(sp Boundary, spVector P, spVector Q)
Boundary in spRegion
sp spRegionGetBoundary(sp Object)
C in various classes
sp spC()
C in various classes
sp spActionC()
C in various classes
sp spAudioSourceC()
C in various classes
sp spAvatarC()
C in various classes
sp spBeaconC()
C in various classes
sp spBeaconExamineC()
C in various classes
sp spBoundaryC()
C in various classes
sp spCallbackC()
C in various classes
sp spClassC()
C in various classes
sp spHearingC()
C in various classes
sp spIntervalCallbackC()
C in various classes
sp spLinkC()
C in various classes
sp spOwnershipRequestC()
C in various classes
sp spPOVC()
C in various classes
sp spRegionC()
C in various classes
sp spRemoteActionC()
C in various classes
sp spRootC()
C in various classes
sp spSeeingC()
C in various classes
sp spSoundC()
C in various classes
sp spSpeakingC()
C in various classes
sp spThingC()
C in various classes
sp spVisualDefinitionC()
CX in spTransform
#define spTransformCX 14
CY in spTransform
#define spTransformCY 15
CZ in spTransform
#define spTransformCZ 16
Class in sp
sp spGetClass(sp Object)
ClassName in spClass
spAscii32 spClassGetClassName(sp Object)
ComposeScales in spVector
spVector spVectorComposeScales(spVector A, spVector B)
Copy in spMatrix
spMatrix spMatrixCopy(spMatrix Destination, spMatrix Source)
Copy in spQuaternion
spQuaternion spQuaternionCopy(spQuaternion Destination, spQuaternion Source)
Copy in spRotation
spRotation spRotationCopy(spRotation Destination, spRotation Source)
Copy in spTransform
spTransform spTransformCopy(spTransform Destination, spTransform Source)
Copy in spVector
spVector spVectorCopy(spVector Destination, spVector Source)
CrossProduct in spVector
spVector spVectorCrossProduct(spVector A, spVector B)
DEGREES in sp
#define spDEGREES 0.0174532925199
Data in spLink
void * spLinkGetData(sp Object)
Deregister in spWM
void spWMDeregister(sp * Pointer)
DesiredInterval in spWM
spDuration spWMGetDesiredInterval()
DesiredInterval in spWM
void spWMSetDesiredInterval(spDuration X)
Distance in spThing
float spThingDistance(sp Thing, sp POV)
DivideByScalar in spVector
spVector spVectorDivideByScalar(spVector Vector, float Scalar)
DotProduct in spVector
float spVectorDotProduct(spVector A, spVector B)
Duration in spAudioSource
spDuration spAudioSourceGetDuration(sp Object)
Duration in spSound
spDuration spSoundGetDuration(sp Object)
DurationFromLength in spFormat
spDuration spFormatDurationFromLength(spFormat Format, long Bytes)
Equals in spVector
spBoolean spVectorEquals(spVector A, spVector B)
EqualsDelta in spVector
spBoolean spVectorEqualsDelta(spVector A, spVector B, float Tolerance)
Error in spWM
char * spWMGetError()
Error in spWM
void spWMSetError(char * X)
Examine in spClass
spBoolean spClassExamine(sp C, spMask Mask, spFn F, void * Data)
ExamineChildren in sp
void spExamineChildren(sp Object, spMask Mask, spFn F, void * Data)
ExamineDescendants in sp
void spExamineDescendants(sp Object, spMask Mask, spFn F, void * Data)
F in spCallback
spFn spCallbackGetF(sp Object)
F in spOwnershipRequest
spFn spOwnershipRequestGetF(sp Object)
FState in spCallback
void * spCallbackGetFState(sp Object)
FState in spOwnershipRequest
void * spOwnershipRequestGetFState(sp Object)
FarClip in spSeeing
float spSeeingGetFarClip(sp Object)
FarClip in spSeeing
void spSeeingSetFarClip(sp Object, float X)
Field in spSeeing
float spSeeingGetField(sp Object)
Field in spSeeing
void spSeeingSetField(sp Object, float X)
Focus in spHearing
sp spHearingGetFocus(sp Object)
Focus in spHearing
void spHearingSetFocus(sp Object, sp X)
Format in spAudioSource
spFormat spAudioSourceGetFormat(sp Object)
Format in spHearing
spFormat spHearingGetFormat(sp Object)
Format in spHearing
void spHearingSetFormat(sp Object, spFormat X)
FromAngles in spRotation
spRotation spRotationFromAngles(spRotation Rotation, spVector Angles)
FromIdent in spMatrix
spMatrix spMatrixFromIdent(spMatrix Matrix)
FromIdent in spQuaternion
spQuaternion spQuaternionFromIdent(spQuaternion Quaternion)
FromIdent in spRotation
spRotation spRotationFromIdent(spRotation Rotation)
FromIdent in spTransform
spTransform spTransformFromIdent(spTransform Transform)
FromQuat in spRotation
spRotation spRotationFromQuat(spRotation Rotation, spQuaternion Quat)
FromTransform in spMatrix
spMatrix spMatrixFromTransform(spMatrix Matrix, spTransform Transform)
Function in spRemoteAction
spBoolean spRemoteActionFunction(sp Action, sp Object)
GenerateOwner in spWM
spName spWMGenerateOwner()
GetAngle in spRotation
float spRotationGetAngle(spRotation Rotation)
GetAxis in spRotation
spVector spRotationGetAxis(spRotation Rotation)
GetCenter in spTransform
spVector spTransformGetCenter(spTransform Transform)
GetRotation in spTransform
spRotation spTransformGetRotation(spTransform Transform)
GetScale in spTransform
spVector spTransformGetScale(spTransform Transform)
GetScaleOrientation in spTransform
spRotation spTransformGetScaleOrientation(spTransform Transform)
GetTranslation in spMatrix
spVector spMatrixGetTranslation(spMatrix Matrix)
GetTranslation in spTransform
spVector spTransformGetTranslation(spTransform Transform)
Grant in spOwnershipRequest
void spOwnershipRequestGrant(sp Request)
IgnoreNearby in spPOV
spBoolean spPOVGetIgnoreNearby(sp Object)
IgnoreNearby in spPOV
void spPOVSetIgnoreNearby(sp Object, spBoolean X)
InRadius in spThing
float spThingGetInRadius(sp Object)
InRadius in spThing
void spThingSetInRadius(sp Object, float X)
Initialization in sp
void spInitialization(sp Object)
Inside in spBoundary
spBoolean spBoundaryInside(sp Boundary, spVector P)
Interval in spAction
spDuration spActionGetInterval(sp Object)
Interval in spSeeing
spDuration spSeeingGetInterval(sp Object)
Interval in spSeeing
void spSeeingSetInterval(sp Object, spDuration X)
Interval in spWM
spDuration spWMGetInterval()
Inverse in spMatrix
spMatrix spMatrixInverse(spMatrix spMatrix)
IsBot in spAvatar
spBoolean spAvatarGetIsBot(sp Object)
IsBot in spAvatar
void spAvatarSetIsBot(sp Object, spBoolean X)
IsNew in sp
spBoolean spGetIsNew(sp Object)
IsRemoved in sp
spBoolean spGetIsRemoved(sp Object)
LINEAR16MONO16 in spFormat
#define spFormatLINEAR16MONO16 0x3E80000000100001
LINEAR32MONO16 in spFormat
#define spFormatLINEAR32MONO16 0x7D00000000100001
LINEAR8MONO16 in spFormat
#define spFormatLINEAR8MONO16 0x1F40000000100001
LastError in spWM
char * spWMGetLastError()
Length in spVector
float spVectorLength(spVector Vector)
LengthFromDuration in spFormat
long spFormatLengthFromDuration(spFormat Format, spDuration Duration)
Leq in spClass
spBoolean spClassLeq(sp Subclass, sp Superclass)
Live in spAudioSource
spBoolean spAudioSourceGetLive(sp Object)
Live in spAudioSource
void spAudioSourceSetLive(sp Object, spBoolean X)
Live in spHearing
spBoolean spHearingGetLive(sp Object)
Live in spHearing
void spHearingSetLive(sp Object, spBoolean X)
Localize in spThing
spBoolean spThingLocalize(sp Thing, sp Region, spBoolean ChooseSmallest)
LookAt in spRotation
spRotation spRotationLookAt(spRotation R, spVector From, spVector To, spVector Up)
LookAt in spThing
void spThingLookAt(sp Thing, sp Target)
MINE in spMask
#define spMaskMINE 1
MainOwner in spWM
spName spWMGetMainOwner()
Mask in spAction
spMask spActionGetMask(sp Object)
Mask in spAction
void spActionSetMask(sp Object, spMask X)
Matrix in spThing
spMatrix spThingMatrix(sp Thing)
MatrixInverse in spThing
spMatrix spThingMatrixInverse(sp Thing)
Max in spBoundary
spVector spBoundaryGetMax(sp Object)
Me in spWM
spName spWMGetMe()
Me in spWM
void spWMSetMe(spName X)
Min in spBoundary
spVector spBoundaryGetMin(sp Object)
Monitor in spClass
sp spClassMonitor(sp C, spMask Mask, spFn F, void * Data)
Mult in spMatrix
spMatrix spMatrixMult(spMatrix A, spMatrix B)
Mult in spQuaternion
spQuaternion spQuaternionMult(spQuaternion A, spQuaternion B)
Mult in spRotation
spRotation spRotationMult(spRotation A, spRotation B)
MultVector in spMatrix
spVector spMatrixMultVector(spMatrix Matrix, spVector Vector)
MultiplyByScalar in spVector
spVector spVectorMultiplyByScalar(spVector Vector, float Scalar)
NORMAL in spMask
#define spMaskNORMAL 3
NearClip in spSeeing
float spSeeingGetNearClip(sp Object)
NearClip in spSeeing
void spSeeingSetNearClip(sp Object, float X)
New in sp
sp spNew()
New in spAudioSource
sp spAudioSourceNew()
New in spAvatar
sp spAvatarNew()
New in spBeacon
sp spBeaconNew(spFixedAscii Tag)
New in spBeaconExamine
sp spBeaconExamineNew(spFixedAscii Tag, spFn F, void * FState)
New in spBoundary
sp spBoundaryNew(spFixedAscii URL)
New in spCallback
sp spCallbackNew(sp X, spDuration I, spFn P, void * PState, spFn F, void * FState)
New in spClass
sp spClassNew(spFixedAscii URL, sp Superclass)
New in spHearing
sp spHearingNew(spFixedAscii HostName)
New in spIntervalCallback
sp spIntervalCallbackNew(spDuration Interval, spFn F, void * FState)
New in spLink
sp spLinkNew(spFixedAscii URL)
New in spOwnershipRequest
sp spOwnershipRequestNew(sp Object, spFn F, void * FState, spDuration Timeout)
New in spPOV
sp spPOVNew()
New in spRegion
sp spRegionNew(sp Boundary)
New in spRemoteAction
sp spRemoteActionNew(sp Parent, spDuration Interval)
New in spRoot
sp spRootNew()
New in spSeeing
sp spSeeingNew(spFixedAscii HostName)
New in spSound
sp spSoundNew(spFixedAscii URL)
New in spSpeaking
sp spSpeakingNew(spFixedAscii HostName)
New in spThing
sp spThingNew()
New in spVisualDefinition
sp spVisualDefinitionNew(spFixedAscii URL)
New in spWM
spWM spWMNew(char * Server, spTransferVector V)
NewLink in spClass
sp spClassNewLink(sp ClassDescriptor, spFixedAscii URL)
NewObj in spClass
sp spClassNewObj(sp ClassDescriptor, long Extra)
Normalize in spVector
spVector spVectorNormalize(spVector Vector)
OTHERS in spMask
#define spMaskOTHERS 2
OutRadius in spThing
float spThingGetOutRadius(sp Object)
OutRadius in spThing
void spThingSetOutRadius(sp Object, float X)
Owner in sp
spName spGetOwner(sp Object)
Owner in sp
void spSetOwner(sp Object, spName X)
P in spCallback
spFn spCallbackGetP(sp Object)
PState in spCallback
void * spCallbackGetPState(sp Object)
Parent in sp
sp spGetParent(sp Object)
Parent in sp
void spSetParent(sp Object, sp X)
Play in spSound
sp spSoundPlay(sp Sound, sp Source, spBoolean Loop, float Gain)
Predicate in spRemoteAction
spBoolean spRemoteActionPredicate(sp Action, sp Object)
Print in sp
void spPrint(sp Object)
RA in spTransform
#define spTransformRA 6
RX in spTransform
#define spTransformRX 3
RY in spTransform
#define spTransformRY 4
RZ in spTransform
#define spTransformRZ 5
Read in spAudioSource
char * spAudioSourceRead(sp Source)
ReadData in spLink
void spLinkReadData(sp Link)
Register in spWM
void spWMRegister(sp * Pointer)
RelativeMatrix in spThing
spMatrix spThingRelativeMatrix(sp POV, sp Thing, spMatrix Matrix)
RelativeVector in spThing
spVector spThingRelativeVector(sp Thing, sp POV, spVector Vector)
Remove in sp
void spRemove(sp Object)
Remove in spWM
void spWMRemove()
ReportError in spWM
void spWMReportError(sp Object, long Code, char * Description)
SOA in spTransform
#define spTransformSOA 13
SOX in spTransform
#define spTransformSOX 10
SOY in spTransform
#define spTransformSOY 11
SOZ in spTransform
#define spTransformSOZ 12
SX in spTransform
#define spTransformSX 7
SY in spTransform
#define spTransformSY 8
SZ in spTransform
#define spTransformSZ 9
SetAngle in spRotation
spRotation spRotationSetAngle(spRotation Rotation, float Angle)
SetAxis in spRotation
spRotation spRotationSetAxis(spRotation Rotation, spVector Axis)
SetCenter in spTransform
spTransform spTransformSetCenter(spTransform Transform, spVector Center)
SetFromScalar in spVector
spVector spVectorSetFromScalar(spVector Vector, float Scalar)
SetRotation in spTransform
spTransform spTransformSetRotation(spTransform Transform, spRotation Rotation)
SetScale in spTransform
spTransform spTransformSetScale(spTransform Transform, spVector Vector)
SetScaleOrientation in spTransform
spTransform spTransformSetScaleOrientation(spTransform Transform, spRotation R)
SetTransform in spThing
void spThingSetTransform(sp Thing, spTransform Transform)
SetTranslation in spMatrix
spMatrix spMatrixSetTranslation(spMatrix Matrix, spVector Translation)
SetTranslation in spTransform
spTransform spTransformSetTranslation(spTransform Transform, spVector Translation)
Setup in spAudioSource
void spAudioSourceSetup(sp S, spBoolean W, spBoolean R, spFormat F, spDuration D)
Subtract in spVector
spVector spVectorSubtract(spVector A, spVector B)
Superclass in spClass
sp spClassGetSuperclass(sp Object)
Suppress in spBeacon
spBoolean spBeaconGetSuppress(sp Object)
Suppress in spBeacon
void spBeaconSetSuppress(sp Object, spBoolean X)
Tag in spBeacon
spFixedAscii spBeaconGetTag(sp Object)
Tag in spBeaconExamine
spFixedAscii spBeaconExamineGetTag(sp Object)
ToAngles in spRotation
spVector spRotationToAngles(spRotation Rotation, spVector Vector)
ToQuat in spRotation
spQuaternion spRotationToQuat(spRotation Rotation, spQuaternion Quat)
ToTransform in spMatrix
spTransform spMatrixToTransform(spMatrix Matrix, spTransform Transform)
Topmost in sp
sp spTopmost(sp Object)
Transform in spThing
spTransform spThingGetTransform(sp Object)
Transform in spThing
void spThingSetTransform(sp Object, spTransform X)
URL in spLink
spFixedAscii spLinkGetURL(sp Object)
Update in spWM
void spWMUpdate()
VisualDefinition in spThing
sp spThingGetVisualDefinition(sp Object)
VisualDefinition in spThing
void spThingSetVisualDefinition(sp Object, sp X)
Window in spWM
spWindow spWMGetWindow()
Window in spWM
void spWMSetWindow(spWindow X)
Write in spAudioSource
void spAudioSourceWrite(sp Source, char * Data)
X in spRotation
#define spRotationX 0
X in spTransform
#define spTransformX 0
X in spVector
#define spVectorX 0
Y in spRotation
#define spRotationY 1
Y in spTransform
#define spTransformY 1
Y in spVector
#define spVectorY 1
Z in spRotation
#define spRotationZ 2
Z in spTransform
#define spTransformZ 2
Z in spVector
#define spVectorZ 2
ZERO in spVector
#define spVectorZERO ((spVector)spVectorDataZero)


Copyright 1997, MERL - A Mitsubishi Electric Research Lab. All rights reserved.