[N-World Contents] [Book Contents] [Prev] [Next] [Index]

This chapter provides a basic overview of attributes, materials, attribute domains, and attribute values. It explains how:

The file new-domain.lisp, located in /usr/local/ngc/src, contains a complete definition of a new domain. Reviewing this code will help you to understand many of the concepts presented in this section.


A Conceptual Overview of Rendering

Rendering is the process of melding geometrical data and attributes of an object (such as surface texture data, opacity, etc.) into a finished scene. Rendering is accomplished by software packages collectively referred to as renderers. Renderers operate on data which is derived from the geometry and attributes of objects in a scene, and then parsed by an interpreter. Figure 9.1 provides a conceptual view of the rendering process.

Figure 9.1 The Rendering Process.


Attributes

Attributes are variables which control some aspect of the appearance of an object in a final, rendered scene. Examples of attributes include opacity, reflectivity, and color. In addition, you can define your own attributes to meet your particular rendering needs.

Attribute Domains

An attribute domain is a set of attributes which are defined for a particular renderer. Different attribute domains are designed to accommodate different rendering requirements. Examples of attribute domains include GL Shade, which is designed to make optimal use of the internal rendering hardware of SGI workstations, and the render domain, which is for use with N-Render.

Attribute Domain Hierarchy

Attribute domains are instances of CLOS classes. As a result, a hierarchy of domains can be constructed, with domains at lower points in the hierarchy inheriting attributes from domains at higher levels. The N·World domain hierarchy is based on the Basic Domain. Beneath this is the render domain, which is in turn followed by various domains defined for specific renderers (Figure 9.2 illustrates this basic hierarchy).

Figure 9.2 N·World attribute domain hierarchy.

Inheritance means that any attribute defined for the :render domain is automatically defined for any domain which declares itself to "depend" on :render. You can define your own domains to include domains beginning at any level.

Suites of attribute values, called materials, can be defined as well. Materials can also be inherited. For example, you may find many objects in a scene which share many basic attributes, such as pieces of furniture made of wood. You could define a basic material for these wooden items, then apply it to each piece of furniture. Then, if you decide to change some aspect of your wood material, these changes will automatically be reflected on all of the objects which have the wood material. You might also define another material, say, walnut, which shares all of the basic attributes of the wood material, but differs in a few subtle respects, like color or the depth of a grain bump map.


Attribute Values for Bodies

An interpreter must determine, or derive, attribute values for objects in a scene. This is accomplished by traversing attribute hierarchies on objects in a scene. For a given attribute on a given body, the interpreter first determines whether its value has been set specifically for that body. Only attributes which are object-settable can have values defined on a particular object. In the domains which are defined for N·World, only mappers and a base-material are object settable, but you can define any attribute in your domains to be object-settable.

Next, materials assigned to the body are examined. The hierarchy of materials is traversed until either a locally set value is encountered, or until the default value for the top level material (or base material) is reached. If no base material is assigned, then the value is obtained from the domain defaults.

Attribute Values for Face Parts

Attribute values for face parts are derived somewhat differently than for bodies. If no explicit attribute values have been assigned for a face part, then it inherits attribute values from the material of its parent object. However, if even a single attribute value is set explicitly for a face part, then all attribute values for that part revert to the values defined in the face parts base material. If no base material is defined, domain defaults are used. This can result in parts having dramatically different appearances than the objects they're associated with. You can avoid this complication by assigning the material of the object as the base attribute of the face part. Then, when a face part attribute value is set explicitly and the other values in the domain revert to default, the default value is obtained from the base material, which is in turn the material of the face parts object.

Surface Contexts

The above traversal path is relatively straightforward for terminal objects. However, it is often the case that a single body will be referenced by numerous objects. How, then, to determine which materials to use to derive attribute values? The solution is provided by a device called a surface-context. A surface context is defined by a BDI and a render domain. For a given render domain, there is only one context for each bdi, and thus for each object. This relationship provides a way of pinpointing a specific object and domain from which to derive attribute values.

Derived Attribute Values: an Example

Consider the following simplified example:

If MAT1 is assigned to the body, then the value of the color attribute is red, since that attribute is set locally. The value of brightness is not set, so it's value is determined by the MAT 1s base material, MAT 3. For set 3, the value is 0.5, so the value of brightness for MAT 1 is also 0.5. If MAT 2 is assigned to the body, then the value of color is blue, but brightness and opacity values are derived from the domain defaults.

If MAT 3 is assigned to the face part, then all other attribute values revert to domain defaults, so color = grey and opacity = 0.75. However, if MAT 1 is assigned as the base material for the face part, then the value for color is red (inherited from MAT 2), the value for brightness is 0.5 (inherited from MAT 3), and only the value of opacity is inherited from the domain defaults.


Defining Attributes and Attribute Domains

Defining an attribute domain is relatively straightforward. Since domains are CLOS classes, many elements of the declaration should be familiar. Define your new domains with ATTR:DEFINE-ATTRIBUTE-DOMAIN, using the generalized form:

(ATTR:DEFINE-ATTRIBUTE-DOMAIN domain-name &KEY depends-on if-exists pretty-name domain-properties)

Table 9.1 describes the properties which you can define for your new domains:

Table 9.1 Domain Instantiation Parameters
Parameter Options Description
:depends-on

:domain-name

Defines precedence list. Your new domain will inherit attributes from this domain and all of its superiors.

:if-exists

:skip

If the domain already exists, skip redefining it

:error

:If the domain exists, respond with an error

:warn

If domain exists, warn user with confirm menu. User can redefine domain by (CLICK-L) on Yes

:ask

If domain exists, ask user to confirm redefinition

:pretty-name

string

The name as it will appear on buttons defined for the Materials Editor window.

:domain-properties

(list)

Miscellaneous properties

The :domain-properties slot serves as a sort of catch-all for any properties you care to define. Potential examples include:

Example

The following code defines the sample domain:

(attr:define-attribute-domain :SAMPLE

                  :pretty-name "Sample"

                  :depends-on :render

                  :domain-properties 

                  `(:default-map-directory "/usr/local/"

                    :default-map-type :RGB

                    :default-machine-type :MAC))

Defining Attributes

Attributes are defined with ATTR:DEFINE-NAMED-ATTRIBUTE, which has the following general form:

(ATTR:DEFINE-NAMED-ATTRIBUTE attribute-id &KEY name domain value-type element-types
default-value documentation properties)

Table 9.2 Attribute instantiation parameters
Parameter Options Description
:pretty-name

string

The name of the attribute, nicely formatted.

:domain

domain-id

The domain with which the attribute is associated.

:element-type

3d:surface

3d:surface-object

3d:light-mixin

The class which can be attributed with this attribute

:default-value

value

An appropriate value which serves as the default value for this attribute

:documentation

string

Documentation for the attribute

:properties

list

Miscellaneous properties for the attribute

:alias

An alias id for finding the attribute, e.g. :opacity

:class

The class used by dynamics to display the attributes for animation

Table 9.2 details the properties you can specify for each domain you define:

Example

Now we'll use ATTR:DEFINE-NAMED-ATTRIBUTE to define two attributes, :blivet and :foo, for our new :sample domain.

(ATTR:DEFINE-NAMED-ATTRIBUTE :blivet

     :domain :SAMPLE

     :documentation "A new attribute, called blivet"

     :element-types `(3d::surface 3d::surface-object))

Blivet is a light-group attribute. Now, we'll define a color attribute, foo:

(attr:define-named-attribute :foo

			:domain :SAMPLE

			:documentation "A new attribute, called blivet"

			:element-types `(3d:surface 3d:surface-object)

			:default-value (gi:make-rgb-color 0.5 0.5 0.5))

An Attribute Definition Macro

Because the attributes you're defining share many properties, you can define a macro to simplify and streamline the attribute definition process. In addition, by including the domain name in the name of the macro, you can provide a clear link in your code between the attributes you're defining and the domain you're defining them for. Here's an example attribute definition macro:

(defmacro DEF-SAMPLE-ATTRIBUTE (id &rest props)

  (unless (getf props :domain)

    (setf (getf props :domain) :sample))

  (if (member :conditions props)

      \Q(attr:define-attribute-parameter ,id

	 			:element-types  `(3d:surface 3d:surface-object)

	 			,@props)

      \Q(attr:define-named-attribute ,id

				 :element-types  `(3d:surfaceU 3d:surface-object)

				 ,@props)))

Understanding Element-Types

An attributes element-type determines which parts of a model or scene can be attributed by a given attribute. These include surfaces (3d::surface), surface-objects (3d:surface-object), lights (3d:light-mixin). Generally, if you want your attributes to apply to geometry objects, use 3d:surface-object.


Adding Your Domain to the Materials Editor

Figure 9.3 details the anatomy of the Materials Editor window. This figure shows the Materials Editor with the :sample domain as the current domain. The examples we'll be presenting will result in menus that look like those in Figure 9.3.

Figure 9.3 Anatomy of the Materials Editor window

If you wish for users to be able to interactively set attribute values for attributes in your new domain, you'll need to add your new domain to the Materials Editor window. As a side-effect to the attr:define-attribute-domain function, a toggle-field button is automatically created in the Materials Editor for your new domain. Before you can view it, though, you'll have to recreate the Materials Editor window.

1. If it is open, close the Materials Editor

2. Create a new Materials Editor window with RA:MAKE-ATTRIBUTES-WINDOW function.

(RA:MAKE-ATTRIBUTES-WINDOW)

Lisp returns some housekeeping details about the window it just created:

#<MULTIPROCESSING:PROCESS attributes wait for events @ #x198e3002>

:INTERNAL

Figure 9.4 details the location and appearance of the new button.

Figure 9.4 The Sample Domain Button in the Materials Editor.

Creating Attributes Editor Menus

The menus for defined attribute domains are created when the Materials Editor window is created. The function which creates this window (ra:make-attributes-window) calls several generic functions which define and create the attribute fields themselves. You'll have to write your own methods for these generic functions which apply to your own attribute domain. Table 9.3 lists these generic functions and summarizes their effects:

Table 9.3 Generic functions called by ra:make-attributes-window
Function Description
ra:make-attributes-menu-for-domain

A function which creates the fields for a domain in the Materials Editor.

ra:make-render-parameter-fields

Creates fields which appear when you (CLICK-L) on defaults.

ra:save-preferences-file

Defines function which allows for saving domain preferences to a file (usually in the .ngc directory)

ra:make-command-fields

Defines command fields which appear at the base of the Materials Editor window.

Defining the Parameters Menu

The parameters menu appears when you (CLICK-L) on the defaults button at the bottom of the Materials Editor window (Figure 9.3). Although this field is optional, it provides a convenient way for users to manipulate important attribute domain parameters such as default directories and default values for variables.

You define render parameters by defining a method to the RA:MAKE-RENDER-PARAMETER-FIELDS generic function:

	(RA:MAKE-RENDER-PARAMETER-FIELDS ((domain (eq :domain-id)

(ui:make-container-field

		(list

			(your field definitions)))))

For example, let's define a parameter field for the :sample domain which allows users to specify a default directory for map files:

			(defmethod MAKE-RENDER-PARAMETER-FIELDS ((self (eql :SAMPLE)))

				(ui:make-container-field

					 (list

						 (ui:make-directory-field :outer-label " Default Map Directory"

							:choose-with-pop-up t

							:width (- *attributes-window-width* *attributes-label-width* 6)

							:property-list `(:resize-for-window t)

							:value-object (ui:make-variable `*nichimen-map-dir*)))

				:equalize-label-sizes-p t))

The resulting pop-up menu looks like the one in Figure 9.5

Figure 9.5 The defaults pop-up menu

Creating Attribute Fields

Attribute fields have a slightly different structure than ordinary fields, in that they are defined within a special type of container called an attribute-source-field.

Figure 9.6 An attribute-source-field

The attribute-source field describes the state of the attributes value, whether it is locally set, inherited, or set from defaults. In addition, if an attributes value is changed during a session with the Materials Editor, the attribute-source-variable is set to changed.

The value object for an attribute-source field itself is created for a given attribute with RA:ATTR-SOURCE-VARIABLE:

(RA:ATTR-SOURCE-VARIABLE attribute-id)

Value objects for attribute-value fields are created using RA:ATTR-VARIABLE:

(RA:ATTR-VARIABLE attribute-id)

When you define your attribute fields for the Materials Editor, you wrap them inside a RA:MAKE-ATTRIBUTE-SOURCE-FIELD form, e.g.:

(RA:MAKE-ATTRIBUTE-SOURCE-FIELD attribute-id
(field-definition)

		:attr-source-variable attribute-id)

You define your attribute fields in a function, which is in turn called by RA:MAKE-ATTRIBUTES-MENU-FOR-DOMAIN. In this example, we'll define fields for our two attributes:

(defmethod make-sample-fields ()

  (let ((mbf-width 100))

    (ui:make-container-field

     (list

      (make-attribute-source-field

       (ui:make-color-field

        :outer-label "Foo"

        :value-object (attr-variable :foo))

       :value-object (attr-source-variable :foo))

      (make-attribute-source-field

       (ui:make-slider-field

        :numeric-type `(float 0.0 2.0 2)

        :outer-label "Blivet"

        :value-object (attr-variable :blivet)

        :redraw-on-select-p t)

       :value-object (attr-source-variable :blivet)))

  :width *attributes-window-width*

  :height (- *attributes-window-height* 450)

  :equalize-label-sizes-p t

  :resizable-p t)))

The key differences between attribute fields and typical UI fields are:

Defining Command Fields

Command fields are buttons which respond to user input (such as a (CLICK-L) by executing a select-method. You can design command fields to serve any purpose, but you'll typically need to define at least one command field which will call your interpreter function when a user is ready to export or render an image. You define command fields for the Materials Window in much the same way you do in other contexts, with the exception that you do so within a method, and not as part of the field definition function.

In this example, we've described an "Export" button. The select-method for this button, export-function-name, equates to the interpreter you wish to use for your domain:

		(defmethod MAKE-COMMAND-FIELDS ((domain (eql :sample)))

			(list

				(ui:make-command-field `export-function-name 

					:label "Export"

					:documentation "L: Export the current object, after applying current
attributes. M: Export the current object"

					:expose-test (ui:var-test `*current-attribute-domain-symbol* :sample)

					:width 80

					:height 23

					:justification :center)))

Saving Parameters to a Preference File

If you wish your users to be able to save their domain preferences to a file, you must provide a way for them to do so. Generally, domain preferences are stored in the .ngc subdirectory of the users home directory:

~{user}/.ngc/{domain-name}-preferences.lisp

To provide a way for users to save their default settings, define a method for the save-preferences-file generic function. You can use this method as a select-method for the "Save Preferences" button. Here's an example for the sample domain, which save to a file in the .ngc directory called sample-preferences.lisp:

(defparameter *sample-map-dir* "directory-name"

(defmethod save-preferences-file ((domain (eql :SAMPLE))) 

		(with-open-file (stream

						"~/.ngc/sample-preferences.lisp"

						:direction :output

						:if-does-not-exist :create

						:if-exists :new-version)

		(format stream "(*sample-map-dir* \"~a\"")

		))


Manipulating Attributes, Sets, and Domains

You can use the Materials Editor to modify attributes, or you can manipulate attribute values programmatically.

Retrieving Attribute Domains

There are several functions you can use which return the name of currently defined attribute domains.

Finding a specific domain

You can find a specific domain with the ATTR:FIND-DOMAIN. You must specify the name of a domain as an argument to this function:

(ATTR:FIND-DOMAIN domain-name &OPTIONAL if-not-found
error-string)

For example, to find the :sample domain:

(attr:find-domain :sample)

which returns:

#<DOMAIN :sample>

Returning the Current Domain

ATTR:CURRENT-DOMAIN returns the name of the current attribute domain:

(ATTR:CURRENT-DOMAIN)

Which returns:

#<DOMAIN :sample>

The current attribute domain is also bound to the attr:*current-attribute-domain* variable.

You can also obtain the current attribute domain id with ATTR:CURRENT-DOMAIN-ID:

(ATTR:CURRENT-DOMAIN-ID)

which returns the current domain id, i.e.,

:GL

Changing the Current Domain

You can select a new domain to be the current domain by clicking on its button in the Current Material section of the Materials Editor menu. You can also useATTR:MAKE-ATTRIBUTE-DOMAIN-CURRENT to achieve the same effect:

(ATTR:MAKE-ATTRIBUTE-DOMAIN-CURRENT defined-domain)

Retrieving materials

ATTR:DEFINED-ATTRIBUTE-SETS-LIST returns a list of all currently defined materials:

(ATTR:DEFINED-ATTRIBUTE-SETS-LIST &OPTIONAL domains)

returns a list like this:

(#<ATTRIBUTE-SET "Test-material" 419348634>

 #<ATTRIBUTE-SET "initial-attributes" 316703242>

#<ATTRIBUTE-SET "material-1" 318025290>)

Retrieving a Specific material

Given the list generated by attr:defined-attribute-sets-list, ATTR::FIND-ATTRIBUTE-SET-IN-LIST returns a specific material:

(FIND-ATTRIBUTE-SET-IN-LIST 
(attr:defined-attribute-sets-list) set-name)

If the set does not exist, Lisp returns NIL.

ATTR:MENU-CHOOSE-MATERIAL creates a handy pop-up menu, from which you can select an material:

(ATTR:MENU-CHOOSE-MATERIAL)

Creates a pop-up menu like this one:

Figure 9.7 Pop up menu generated by attr:menu-choose-attribute-set.

(CLICK-L) on an material to return that set,e.g:

#<ATTRIBUTE-SET "Material-B" 419335370>

#<EXITING-MENU-ITEM-FIELD "Material-B" 3114534252>

#(UI:MOUSE-BUTTON :LEFT 0)

Retrieving the Current material

RA:CURRENT-ATTRIBUTE-SET returns the current material:

(RA:CURRENT-ATTRIBUTE-SET)

Returns an material, e.g.:

#<ATTRIBUTE-SET "material-1" 318025290>

The current material is also bound to a global variable, RA:*CURRENT-ATTRIBUTE-SET*


Defining materials

You can define materials interactively in the Materials Editor, or use ATTR:DEFINE-ATTRIBUTE-SET to do so programmatically:

(ATTR:DEFINE-ATTRIBUTE-SET									:name set-name

									:domain domain-id)

For example, to define an material called A-Material in the :sample domain:

(attr:define-attribute-set :name "A-Material" :domain :sample)

Retrieving and Setting Attribute Values

An material context is basically analogous to an attribute domain. ATTR:GET-ATTRIBUTE-SET-CONTEXT returns the context for a given domain:

	(attr:get-attribute-set-context (attr:find-domain domain-id ))

This function returns a context, e.g.,

#<ATTRIBUTE-SET-CONTEXT :SAMPLE domain. 419339842

The value of a given attribute depends on whether its value is set locally, or is inherited from another material. A value calculated for a given attribute across the entire hierarchy of materials is called a derived-value. Given a material, an attribute, and an material context, you can obtain the derived value for an attribute with ATTR:DERIVED-ATTRIBUTE-VALUE:

(ATTR:DERIVED-ATTRIBUTE-VALUE attribute-set att-name

att-set-context)

For example, to determine the value of the attribute foo in the material we defined above ("A-Material"), evaluate a form like this one:

(attr:derived-attribute-value 

		(attr:menu-choose-attribute-set) :foo 

			(attr:get-attribute-set-context

				(attr:find-domain :sample)))

Lisp returns a series of printed expressions like this:

#<rgb-color: r=50% g=100% b=0%>

NIL

(#<ATTRIBUTE-SET "Material-A" 317756314>)

:EXPLICIT

The first value is the value of the attribute itself. Because Foo is a color-attribute the returned value is an rgb-color

The second value, NIL, indicates that the attribute is defined in a material, and not for an object. If the attribute is object-settable, then this line will show which objects were traversed to obtain the attribute value.

The third value, a list, contains all of the materials from which this attribute inherits values. Because Material-A does not inherit from any other materials, this list contains only one item (Material-A).

The fourth value, :EXPLICIT, indicates that the value for Foo has been set to a value other than the default value. If the attribute-value is not set at any point in the hierarchy, this value is :DEFAULT.

For example, let's assume another material, Material-B, has been defined which inherits from Material-A. Evaluating the above form (and selecting Material-B from the pop-up) returns this data:

#<rgb-color: r=50% g=100% b=0%>

NIL

(#<ATTRIBUTE-SET "Material-B" 429660178>

#<ATTRIBUTE-SET "Material-A" 429608850>)

:EXPLICIT

The third item, the attribute-set list, shows all of the materials in the hierarchy from which Material-B inherits values.

Deriving A Local Attribute Value

Local attribute values are values for attributes in specific materials. Local attribute values ignore inheritance from other materials. This property means that you'll use local-attribute-values to set attribute-values programmatically, since it's the only way you can be sure that your changes will affect the attribute you intended, and not an attribute at another point in the hierarchy.

ATTR:LOCAL-ATTRIBUTE-VALUE returns the local value of an attribute, given an material, an attribute, and an material context:

(ATTR:LOCAL-ATTRIBUTE-VALUE attribute-set attribute-id
attribute-set-context)

For example, to get the local value of Foo for Material A, evaluate a form like this one:

(local-attribute-value (menu-choose-attribute-set) :foo (get-attribute-set-context (find-domain :sample)))

Lisp returns only the local value, and T, indicating that the function executed successfully:

#<rgb-color: r=50% g=100% b=0%>

T

Changing a Local Attribute Value

You can change local attribute values by adding a setf form and a new value to this form. For example, to specify a new color for foo, use a form like:

(setf (local-attribute-value (menu-choose-attribute-set)

		:foo (get-attribute-set-context (find-domain :sample)))

		(gi:make-rgb-color 1.0 1.0 .5))

Which Objects have Attributes

With certain exceptions, attributes are defined for bodies and face parts of objects. Certain attributes are object-settable, which means they can be defined for objects. Examples include the base-material and the various mappers. Bodies and face parts can have different materials, which means that care must be taken during the interpretation process to ensure that the correct material is applied.

Assigning a Base material to an Object

You can assign a base material to an object with the :base-attribute-set keyword argument to the ATTR:LOCAL-ATTRIBUTE-VALUE function. You must provide this function with a body, a surface-context, and an material.

Let's assume we've created an object in the geometry frame, and bound that object to ?. To get the body, we use the 3d:obod function:

(setf body (3d:obod ?))

Getting the surface-context requires that we have a bdi and an attribute domain. We've already learned how to retrieve an attribute domain, and we can get the bdi with the 3d:obdi function:

(setf bdi (3d:obdi ?))

Given the bdi and an attribute domain, we can retrieve the surface context with 3d:get-surface-context:

(setq context (3d:get-surface-context bdi

						(attr:find-domain :sample)))

Finally, given all of these value, we can manipulate local attribute values:

(setf (local-attribute-value body :base-attribute-set context)(menu-choose-attribute-set))

Deriving Object Attributes

When an material is assigned to an object, you can derive attribute values for that object, given the body, the surface context, and the domain.

(derived-attribute-value body :foo context :domain (attr:find-domain :sample))



[N-World Contents] [Book Contents] [Prev] [Next] [Index]

Another fine product from Nichimen documentation!

Copyright © 1996, Nichimen Graphics Corporation. All rights reserved.