Articles Archive
Articles Search
Director Wiki
 

Adding Behaviors to 3D Models

June 7, 2005
by James Newton

Source files: PickAction.sit (Macintosh) | PickAction.zip (Windows)

The movie above is scarcely spectacular. If you click on the rotating Box model, the background color of the 3D sprite changes, and a message is displayed telling you which face of which mesh has been clicked. Wow.

The big deal is that the code that does this is attached to the Box model itself, in the same way that a behavior is attached to a 2D sprite.

If you thought that you can't attach behaviors directly to models, then you are right. Almost. What you can do is add script instances to the userData list which is a property of every 3D node.

Behaviors have four main advantages over the script-instance-in-a-userData-list approach:

  1. You can drop a behavior directly onto a sprite
  2. You can set the parameters of the behavior without writing any code
  3. The built-in spriteNum property allows you to obtain a pointer to the sprite the behavior is attached to
  4. Director dispatches mouse and keyboard events automatically

You'll see how to work around (or to live with) these limitations in what follows.

Adding a behavior to the userData list

Here's the handler that creates the Box model, and which attaches an instance of the script("Model Instance") to the userData list. As you can see, adding the instance requires only a couple of code. If parameters were required, they could be set in the on new() handler, or in a separate on init() handler if you prefer to work that way. If you are prepared to get your braincells dirty by working in 3D Lingo, the loss of drag and drop functionality and the joys of the getPropertyDescriptionList() handler are not going to upset you.

on beginSprite(me)
  vScene = sprite(me.spriteNum).member
  vScene.resetWorld()
  
  -- Create a box model
  vResource = vScene.newModelResource("Box", #box)
  pModel    = vScene.newModel("Box", vResource)
  -- Add a "behavior" to the model
  vInstance = script("Model Instance").new()
  pModel.userData.addProp(#instance, vInstance)
end beginSprite

Dispatching events

Attaching an instance to the userData list for a model not in itself enough to trigger event handlers when the user clicks on the model. You need to add a behavior to the 3D sprite that dispatches events to the appropriate userData list. You'll find a complete Pick Action behavior in the download source movie. Here's a simplified which deals only with mouseUp events:

on mouseUp(me)
  vSprite = sprite(me.spriteNum)
  vLeft   = vSprite.left
  vTop    = vSprite.top
  
  -- Determine which 3D model is under the mouse
  tSpriteLoc = point(the mouseH - vLeft, the mouseV - vTop)
  tModelData = vSprite.camera.modelsUnderLoc(tSpriteLoc, 1, #detailed)
 
  if not tModelData.count then
    -- There are no models under the mouse
    exit
  end if
  
  -- Retrieve the userData list for the model under the mouse
  tModelData = tModelData.getLast() -- only 1 model is asked for
  tModel = tModelData.model
  tUserData = tModel.userData
  
  -- Call the userData list, sending a pointer to this sprite and
  -- the picking data. NOTE: since D8.5 it is possible to "call" a
  -- property list.
  
  call(anEvent, tUserData, vSprite, tModelData)
end mouseUp

The Pick Action behavior in the download files is more sophisticated, and sends #mouseDown, #mouseUp, #rightMouseDown and #rightMouseUp events, as required.

Obtaining a pointer to the sprite

Here's a simplifed version of the "behavior" that is added to the Box model's userData list:

on mouseUp(me, a3DSprite, aModelData)
  vMember = a3DSprite.member
  vColor  = rgb(random(5)-1, random(5)-1, random(5)-1) * 51
  vMember.bgColor = vColor
end mouseUp

The me handler points to the "behavior" itself, but you can't get at the sprite by using sprite(me.spriteNum), so the sprite has to be passed in as a separate parameter. The picking details for the model are passed in as a third parameter. This will be a property list similar to the following:

[#model:         model("Box"),
 #distance:      148.3333,
 #isectPosition: vector( 23.0958, 6.8110, -12.9864 ),
 #isectNormal:   vector( 0.9848, 0.0000, -0.1736 ),
 #meshID:        2,
 #faceID:        1,
 #vertices:      [vector( 20.2790, 25.0000, -28.9614 ),
                  vector( 28.9614, -25.0000, 20.2790 ),
                  vector( 20.2790, -25.0000, -28.9614 )],
 #uvCoord:       [#u: 0.3244,
                  #v: 0.0394]]

Whether you need some or all of this data, the Pick Action behavior has already prepared it for you.

Conclusion

In conjunction with the Pick Action behavior, adding one or more script instances to the userData list of your 3D models gives you more precise control over your 3D world. The technique is simple and robust. Have fun with it!

Source files: PickAction.sit (Macintosh) | PickAction.zip (Windows)

James Newton started working with Director 5 in 1997. He wrote many of the behaviors that ship with Director since version 7. James lives in Dunoon, near Glasgow, Scotland. His company, OpenSpark Interactive, is responsible for marketing PimZ OSControl Xtra. When not coding he can be found racing his classic Flying Fifteen around the Holy Loch, making things with his four children, or catching escaped hamsters.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.