home
StrataCafe Forums
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 StrataCafe
 Strata Design 3D CX: Lua Scripting
 Tutorial: Random Replication Part I
 Forum Locked  Topic Locked
 Printer Friendly
Author Previous Topic Topic Next Topic  

jbradley
Inquisitive

USA
64 Posts

Posted - 06/12/2006 :  12:06:53  Show Profile
Time: 10 minutes
Difficulty: Novice - Advanced
Author: jbradley

Overview:
We'll write a function that will duplicate a selected object a certain number of times and change the scale, position, and rotation of each copy. For this example, we'll create a random Replicate function that operates on a single selected object.

The final script for Part I
Copy / Paste the following into your Console (Scripting -> Console menu). Select the text and press "Evaluate" on the lower left of the console window.

-- A function to replicate a selected object a certain number of times
function Replicate(numCopies)

	local c = s3d.database.GetActiveContext()
	local m = c and c:GetManipulatorManager()
	local size = m and m:GetSize()

	if size > 0 then

        local i = m:GetIndexedInstance(1)

         for t=1, numCopies do
            print("Creating copy ".. t)
            
            local inst = i:Clone()
            c:AddInstance(inst)
            
            --	Random translation within 5 inch boundary
            local tx = math.random(360) - math.random(360)
            local ty = math.random(360) - math.random(360)
            local tz = math.random(360) - math.random(360)
            local position = s3d.vec.Point3d(tx,ty,tz)

            --	Make sure our objects are at least 0.5 inches plus some random deviation
            local ns = 36 + math.random(72)
            local scale = s3d.vec.Point3d(ns,ns,ns)
            
            --	See if you can find the bug in the code for rotation below.
            --	If you get stumped, check out the end of the tutorial for the
            --	final, debugged code.

            local angle = math.random() * 360 * math.pi
            local rotation = s3d.vec.Quaternion(math.random(), math.random(), math.random(), angle)

            inst:SetTranslationAt(0,position)
            inst:SetScaleAt(0,scale)
            inst:SetRotationAt(0, rotation )
        end

        c:Refresh()

	else
		s3d.ui.Alert ("You must select an object.")
	end
end


You've now created a function stored in the scripting system that can be used over and over until you exit the application. It's beyond the scope of this tutorial to explain the storage system in CX 5. Expect more advanced tutorials in the near future that will discuss this and many other topics.

Now that we've created this function, select some object in your view and then type the following into the console.

Replicate(3)

Select the text you just entered and press "Evaluate".

Results:
Three copies of the selected object with a random position, scale and rotation will be created.

How this works:
What follows is almost a line-by-line description of the code above and how it works. Due to the documentation shortages at this time, I hope this will open the doors for many questions. Feel free to post those questions in this thread for questions on this specific script, or in a new thread for specific questions that can get a targeted response.


Create a new function, Replicate and a parameter numCopies.


function Replicate(numCopies)


Get the active context or 'view'. This is basically "Where am I right now in the database?" I could be inside a shape, inside an edit, inside the root model (main scene), etc. The GetActiveContext will return the context of your current position basically in the database tree. This isn't probably the most accurate description of "context", btw.


local c = s3d.database.GetActiveContext()


Get the manipulator manager for this context. This allows us to iterate over the context. Think of the context as a branch node in the CX database tree that we need to loop over to do certain things (like, get all sub branches). Here, we're making sure that it exists by doing "c and c...". This a proper way to handle errors from an undefined value that may result if the manipulator manager did not exist. If the value does not exist, m will become "nil" which Lua will actually understand to be nothing, instead of some unknown value returned by the CX 5 internal code.


local m = c and c:GetManipulatorManager()


Grab the size of our manipulator manager. Same process as above basically to avoid any problems if GetSize() returns a value that might make no sense to Lua. Getting the size is similar to doing an Array.length in other programming languages.


local size = m and m:GetSize()


If our size is greater than 0 (something must be selected).


if size > 0 then


Get the first selected instance in the list.


local i = m:GetIndexedInstance(1)


Perform a loop for 1 to the number of copies we're going to create from the selected instance.


for t=1, numCopies do


Create the actual copy. We use the "Clone()" method on the selected instance and store the result in a variable named inst. Finally, this newly created instance needs to be added to the current context, which could possibly be inside a shape or somewhere else in the database.


print("Creating copy ".. t)
local inst = i:Clone()
c:AddInstance(inst)


Now that we created our cloned object, we want to add some random position scale and rotation to it. Starting with position, we'll create three variables to store a random x, y, and z position. Create a Point3d object from these values, which is one of the native CX 5 vector classes.


-- Random translation within 5 inch boundary
local tx = math.random(360) - math.random(360)
local ty = math.random(360) - math.random(360)
local tz = math.random(360) - math.random(360)
local position = s3d.vec.Point3d(tx,ty,tz)


Now handle the creation of a variable to represent a random scale. Scale in CX 5 is a Point3d object as well, and we'll just pass the same value three times to keep the size equal.


-- Make sure our objects are at least 0.5 inches plus some random deviation
local ns = 36 + math.random(72)
local scale = s3d.vec.Point3d(ns,ns,ns)


Next we're going to create a random rotation value. The Quaternion class if one of the ways (aside from Matrices) that we can do this.

A Quaternion requires 4 values: values for a directional axis vector (x,y,z) represented by three 0-1 values and a rotation in radians. Here, we will use the math.random() function in Lua (returns a value on [0-1]) and convert that to radians.


local angle = math.random() * 360 * math.pi
local rotation = s3d.vec.Quaternion(math.random(), math.random(), math.random(), angle)


It's worth noting that many of the vector classes can take various forms of parameters. For example, we could just pass the x-axis as a directional vector so all replicated copies are rotated on the same axis by a random angle: local rotation = s3d.vec.Quaternion(s3d.vec.x_axis, angle)

Now, let's add these properties to our newly created instance. The Set... methods also take a first parameter, time , which allows you to set the value at a specific point in time. Since we are just creating copies and not getting into animation as this time, we'll use 0 as the time.


inst:SetTranslationAt(0,position)
inst:SetScaleAt(0,scale)
inst:SetRotationAt(0, rotation )


End our loop for creating the number of copies we want.


end


Refresh the database. There are many, many options for refreshing the database in Strata. This is because you could have a very large database and only want to refresh a single node, or something else. Again, not something we're going to get into at this point in time. Just a Refresh() call is all we need right now.


c:Refresh()


If we find that the user did not select anything (size <= 0), we want to throw an error to the user that they need to actually select something.


else


The actual error we'll be throwing to the user.


s3d.ui.Alert ("You must select an object.")


End the if...else statement that checks to see if our size is greater than 0.


end


Finally end our function.


end



Did You Find The Bug?
Now, you may have noticed something interesting in the results of our Replicate() function. It seems as though the copies of our object are skewed in random directions, instead of being just rotated. Why would that be?

A Quaternion actually holds transformations that not only include rotation but may also scale your object if not used properly. You can look up Quaternions through Google to find out more information. What we need to do is calculate a random directional vector, a random angle in radians, then create a properly constructed Quaternion from those values.

The code to create a properly defined random Quaternion vector with a random angle of orientation is:

        local vector = s3d.vec.Dir3d(math.random(), math.random(), math.random()):Normalize()
        local angle = math.random()*2*math.pi
        local w = math.cos(angle/2)
        local qx = vector.x * sin(angle/2)
        local qy = vector.y * sin(angle/2)
        local qz = vector.z * sin(angle/2)
        local rotation = s3d.vec.Quaternion(qx,qy,qz,w)

Notice that we're also using the native Dir3d() class here to create a random directional vector (x,y,z) and make sure it's normalized, which it should be anyway because math.random() returns a number on the interval 0-1.

So, our final code for our Replicate() function is now:
-- A function to replicate a selected object a certain number of times
function Replicate(numCopies)

	local c = s3d.database.GetActiveContext()
	local m = c and c:GetManipulatorManager()
	local size = m and m:GetSize()

	if size > 0 then

        local i = m:GetIndexedInstance(1)

         for t=1, numCopies do
            print("Creating copy ".. t)
            
            local inst = i:Clone()
            c:AddInstance(inst)
            
            --	Random translation within 5 inch boundary
            local tx = math.random(360) - math.random(360)
            local ty = math.random(360) - math.random(360)
            local tz = math.random(360) - math.random(360)
            local position = s3d.vec.Point3d(tx,ty,tz)

            --	Make sure our objects are at least 0.5 inches plus some random deviation
            local ns = 36 + math.random(72)
            local scale = s3d.vec.Point3d(ns,ns,ns)
            
            local vector = s3d.vec.Dir3d(math.random(), math.random(), math.random()):Normalize()
            local angle = math.random()*2*math.pi
            local w = math.cos(angle/2)
            local qx = vector.x * math.sin(angle/2)
            local qy = vector.y * math.sin(angle/2)
            local qz = vector.z * math.sin(angle/2)
            local rotation = s3d.vec.Quaternion(qx,qy,qz,w)

            inst:SetTranslationAt(0,position)
            inst:SetScaleAt(0,scale)
            inst:SetRotationAt(0, rotation )
        end

        c:Refresh()

	else
		s3d.ui.Alert ("You must select an object.")
	end
end


I hope that starts to get you thinking about scripting in Strata CX 5.

I realize that this may be a lot of info in the post above, but it's a start. I chose this particular script because it covers a few bases in the Lua scripting system for CX 5.

As stated earlier, if there are specific questions on this script, please feel free to post them in this thread. Otherwise, please create a new topic so that we can answer those questions more specifically.

cheers,



Jon Bradley
Animator / VFX Artist

Edited by - jbradley on 06/12/2006 15:50:51

Antaguar
A Regular

Germany
112 Posts

Posted - 06/13/2006 :  12:32:36  Show Profile  Visit Antaguar's Homepage
Thanks a lot Jon,

this script helps a lot, especially to show how instances
can be selected. Does 'GetIndexedInstances' only get selected
instances ? Will 'GetInstances' go through all instances within
the context ?

However I cannot start the script as the console gives
Lua result: [string "_s3d_() end<eof>..."]:1: <eof> expected near `end'
after presseing the Evaluation button.

Did I lost a character when copying from the browser ?
Where do I have to place the cursor when pressing the button ?

Thanks,



- Benny (Bernd Gutmann)

Strata 3.9 / Win98
Strata CX5 / Win2000
Go to Top of Page

jbradley
Inquisitive

USA
64 Posts

Posted - 06/13/2006 :  15:56:35  Show Profile
quote:
this script helps a lot, especially to show how instances
can be selected. Does 'GetIndexedInstances' only get selected
instances ? Will 'GetInstances' go through all instances within
the context ?



GetIndexedInstance(index) will return the selected instance based on the index (number) passed into the function. It doesn't return all selected instances at once. You can loop over the selected instances though pretty easily:

for s=1,size do
    local i = m:GetIndexedInstance(s)
    -- do something
end

GetInstances isn't a method on the ManipulatorManager class, so that won't do anything.

quote:
However I cannot start the script as the console gives
Lua result: [string "_s3d_() end<eof>..."]:1: <eof> expected near `end'
after presseing the Evaluation button.

Did I lost a character when copying from the browser ?
Where do I have to place the cursor when pressing the button ?



The entire script needs to be selected and executed. So, from the "function..." to the "end" tag.

If nothing is selected in the Console, Strata will evaluate the current line that your cursor is on, and nothing more. If you put your cursor on the last line "end" and press evaluate (or Enter on your keypad), you will get the error that you posted.

The easiest thing to do is delete all the text in the console, paste it in there, select all of it and Evaluate.

That should get you going.

Jon Bradley
Animator / VFX Artist

Edited by - jbradley on 06/13/2006 15:59:11
Go to Top of Page

tbgriswold
Regularly Educational

USA
1443 Posts

Posted - 06/16/2006 :  17:32:14  Show Profile  Visit tbgriswold's Homepage
Jon,

Is it possible to make your script replicate shapes? Right now when I make a shape of an object and then try to replicate the shape, it seems to replicate the center point and a shape reference, but there is nothing really their, no object appears.

Britt

__________________________
Britt Griswold
NASA Goddard
Code 665 Bldg. 21 Rm 063
Greenbelt, MD 20771
(301) 286-3381
bgriswol@pop200.gsfc.nasa.gov
http://map.gsfc.nasa.gov
Go to Top of Page

jbradley
Inquisitive

USA
64 Posts

Posted - 06/19/2006 :  08:20:06  Show Profile
quote:
Originally posted by tbgriswold

Jon,

Is it possible to make your script replicate shapes? Right now when I make a shape of an object and then try to replicate the shape, it seems to replicate the center point and a shape reference, but there is nothing really their, no object appears.


Britt,

You may try "DeepClone()" instead of "Clone()" on the instance. That should clone shapes as well - basically anything that's a referenced geometry.

I'll look into this - shapes are a different beast in the CX database and are handled differently than normal geometry.

Jon

Jon Bradley
Animator / VFX Artist
Go to Top of Page

j.tassinari
A Regular

Italy
132 Posts

Posted - 03/13/2007 :  08:54:21  Show Profile  Visit j.tassinari's Homepage
hi Jon.

If you do not mind, I have some really little questions about script and the generic scripting with LUA.

I see you need anytime to select the shape you d'like to use.
I guess, colud the shape be called from the script using its name as defined in the object palette or project window palette (this is meaning also to have a unique name/instance for each object on the "stage/scene")?
How can i split string, make trim and subsequences?
How can i better understand the concept of a s3d.database?
Looping recordset or making querys?



jTassinari
My profile | Web

Edited by - j.tassinari on 03/13/2007 08:56:42
Go to Top of Page

optifranklin
Infrequent Poster

9 Posts

Posted - 09/03/2007 :  14:32:22  Show Profile  Visit optifranklin's Homepage
Thanks so much for this tutorial.
Anyway, I was curious about something.
You included the code to limit the random rotation to a specific axis.
I was wondering if you could do the same for both position and scale?
I noodled around with it for a while, but (not suprisingly) wasn't able to
figure out how this is done.

Tim
Go to Top of Page
  Previous Topic Topic Next Topic  
 Forum Locked  Topic Locked
 Printer Friendly
Jump To:
StrataCafe Forums © 2009 StrataCafe Go To Top Of Page
Snitz Forums 2000