Create 3d Effects using director polygons
The newest version and full example files are always available at http://www.dave-cole.com/3D/
-- Dave's Lingo 3D Engine v7.1 FULL - Last Modified 12/14/99
-- Dave Cole - dcole@sigma6.com
-- Special thanks to Terry Schussler of Trevi Media (http://www.trevimedia.com) for optimizations!
--
-- Dave's 3D Engine is Shareware. If you use this source for commercial gain, you
-- must send a $20 shareware registration fee to:
--
-- Dave Cole
-- 575 Leroy
-- Ferndale, MI 48220
--
--
--
-- Copyright (c) 1998 David Cole
-- All rights reserved.
-- Redistribution and use in source and binary forms are permitted
-- provided that the above copyright notice and this paragraph are
-- duplicated in all such forms and that any documentation,
-- advertising materials, and other materials related to such
-- distribution and use acknowledge that the software was developed
-- by David Cole, as well as that the user, if using this software
-- for financial gain, has paid the $20 shareware registration fee.
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND WITHOUT ANY EXPRESS OR
-- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-- WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
--
-- You may freely redistribute this 3D engine so long as the credits and accompanying
-- credits README file remain with the engine at all times. If you modify it, please
-- annotate notes about your modification and notify me about your changes -- I would
-- like to learn what others can do with this thing!
--
-- The intention of this code is to allow the representation, transformation, and 2D
-- projection and display of 3-dimensional graphics from within a director film. The
-- interface has been styled loosely after OpenGL for ease of use.
--
-- If this source exists in its original distribution, a manual is available in cast
-- member 3.
--
-- This graphics engine uses a right-handed coordinate system with a view that defaults
-- to looking down the negative z axis from the origin.
--
-- This is the FULL release of Dave's Lingo 3D Engine. That means that although care
-- has been taken to optimize the routines for speed, the primary intention of this code is
-- to offer a flexibile, accurate, fully-functional 3D graphics system.
--
-- All points use homogenous coordinates. 4x4 Matrices are represented as nested lists:
-- (i.e. [[],[],[],[]]). 1x4 Matrices are represented as a single 4-element list (i.e.
-- [0.0, 0.0, 0.0, 1.0]).
--
-- *Note: If this engine is removed from its original distribution movie, the lingo below
-- in green can be deleted, all green lingo below is demo-movie specific and not necessary
-- for the 3D Engine to function properly.
global mModelView -- A 4x4 matrix that represents the current ModelView matrix.
global mProjection -- A 4x4 matrix that represents the current Projection matrix.
global mModelViewStack -- A stack of saved ModelView matrices
global mProjectionStack -- A stack of saved Projection matrices
global mv -- 1: Current Matrix Operations performed on ModelView matrix, 0: Current Matrix Ops Performed on Projection Matrix
global point -- A point datatype for misc. use (in homogenous coordinates)
global xCoefficient -- used in viewport transformations
global yCoefficient -- used in viewport transformations
global halffov -- used to scale sprites accurately
global sortSprites -- 1: Sprites with a nearer Z value will take a higher # sprite channel (slower)
-- -- 0: Sprites will stay in their channels (less accurate looking but faster)
global cullBackfaces -- 1: Cull backfacing quads, 0: don't
global cullBackfaces_cw -- 1: Clockwise defined vertices determine the front face of a quad, 0: Counterclockwise
-- -- .. This property only is checked if cullBackfaces = 1
global activateClipping -- 1: Quads/Sprites are clipped when outside the frustum, 0: no clipping is done
global lookupList
global DWatcher
global DWatcher_newChange
global use3DWatcher
global lightingList -- a list of lights (format: [uniqueID:[x, y, z, r, g, b]])
global debug -- Set to 1 to dump tons of debugging information.
global xSlider, ySlider, zSlider, xScale, yScale, zScale, Frustum, rox, roy, roz, roton -- custom demo program specific
set activateClipping = 0
set use3DWatcher = 0
set cullBackfaces = 1
set cullBackfaces_cw = 1
set DWatcher_newChange = 1
set mModelViewStack = []
set mProjectionStack = []
set point = [0, 0, 0, 1.0]
set lookupList = [:]
sort lookupList
set mv = 1
set sortSprites = 1
set halffov = 1
set debug = 0 -- Set to 1 to print tons of debugging info
---- custom for-the-demo-only, you can delete this stuff
set Frustum = 0.8
set xSlider = 0.0
set ySlider = 0.0
set zSlider = -40.0
set xScale = 1.0
set yScale = 1.0
set zScale = 1.0
set rox = 0.0
set roy = 0.0
set roz = 0.0
set roton = 1
---- end custom for-the-demo-only
-- xFormPoint takes a 4-element list which defines a point as [x, y, z, w] (homogeneous coordinates) and
-- transforms it into a screen position point in the form of a 3-element list [h, v, z] which represents
-- where on the screen the point should appear after being transformed by the ModelView matrix, the
-- projection matrix, the perspective division, and the viewport transformation. These are known as the
-- window coordinates. The z element is returned so that the programmer can illicit depth information
-- about this point. For more info on this process, research a 3D graphics tutorial near you.
-- 0 is returned if the point is not to be drawn. (i.e. it is outside of the clipping planes.)
on xFormPoint p
-- if debug then put "xFormPoint--------------"
-- if debug then put "ModelView: "&RETURN&mModelView
-- if debug then put "Projection:"&RETURN&mProjection
-- if debug then put "p = "&p
set z = getAProp(lookupList, p)
if z = 0 then
set m = Matrix1x4Mult(p, mModelView) -- Do ModelView xformation
if debug then put "XFormMV:"&RETURN&m
if (m <> 0) then set m = Matrix1x4Mult(m, mProjection) -- Do Projection xformation
else
beep
put "ERROR!: Point couldn't be converted: "&p
return 0
end if
if debug then put "XFormP:"&RETURN&m
set m = PerspectiveDivide(m) -- Do Perspective division & clipping
if debug then put "XFormPD:"&RETURN&m
if (m <> 0) then set m = ViewPortXForm(m) -- Do viewport transformation
if debug then put "XFormV:"&RETURN&m
addProp(lookUplist, p, m)
return m
else
return z
end if
end
-- Nudge can be used by a 3DSprite or 3DQuad, or your own custom code, to retrieve the transformed
-- coordinates of a geometry based on the current ModelView matrix without performing any of the
-- projection transformation, perspective division, clipping, or viewport transformations.
-- Useful in multi-step transformations.
on Nudge p
return(Matrix1x4Mult(p, mModelView))
end
-- pFrustum creates a perspective projection matrix with the viewing frustum volume
-- described by the arguments and multiplies it to the current projection matrix. It is recommended
-- you call mLoadIdentity before calling this, since its effects are cumulative. (left, bottom, -near) and
-- (right, top, -near) specify the (x, y, z) coordinates of the lower-left and upper-right corners of the
-- near clipping plane; near and far give the distances from the viewpoint to the near and far clipping planes.
-- near & far should be positive.
on pFrustum left, right, bottom, top, near, far
lookupList = [:]
set nn = 2.0*near
set rl = right - left
set tb = top - bottom
set fn = far - near
set mProjection = [[integer(1000*nn/rl), 0, integer(1000*(right+left)/rl), 0], [0, integer(1000*nn/tb), integer(1000*(top+bottom)/tb), 0], [0, 0, integer(1000*-(far+near)/fn), integer(1000*-(far*nn)/fn)], [0, 0, -1000, 0]]
set halffov = (1.5708 / abs(atan(float(top) / float(near))))
return 1
end
-- pOrtho creates an orthagonal projection matrix with the parallel viewing volume described by the given
-- arguments and multiplies it to the current projection matrix. It is recommended you call mLoadIdentity before
-- calling this, since its effects are cumulative. (left, bottom, -near) and (right, top, -near) are mapped to
-- the lower-left and upper-right corners of the viewport window in the near clipping plane. (left, bottom, -far)
-- and (right, top, -far) are mapped to the lower-left and upper-right corners of the far clipping plane which
-- are also mapped to the same corners of the viewport window. Both near and far can be positive or negative.
on pOrtho left, right, bottom, top, near, far
lookUpList = [:]
set rl = right - left
set tb = top - bottom
set fn = far - near
set mProjection = [[integer(2000/rl), 0, 0, integer(1000*(right+left)/rl)], [0, integer(2000/tb), 0, integer(1000*(top+bottom)/tb)], [0, 0, integer(-200/fn), integer(1000*(far+near)/fn)], [0, 0, 0, 1000]]
set halffov = -1
return 1
end
-- pViewPort defines the viewport for the view onto the 3D world. It defines a pixel rectangle in the
-- window where the final image is mapped. x and y specify the upper lefthand corner of the viewport,
-- width and height are the size of the viewport rectangle in pixels. An example use of this would
-- be calling pViewPort(0, 0, windowWidth, windowHeight).
on pViewPort x, y, width, height
set xCoefficient = (width/2) + x
set yCoefficient = (height/2) + y
return 1
end
----------------------------------------------------------------------------------------------------------
-- MATRIX OPERATIONS -----------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
-- mSelectMatrix takes either "Projection" or "ModelView" as arguments, and designates the current matrix
-- to have any of the transformations or matrix operations done to it.
on mSelectMatrix arg
if (arg) = "Projection" then set mv = 0
else set mv = 1
return 1
end
-- Push current matrix onto stack, current matrix is a copy of what is on top of the stack until you change it.
on mPush
if (mv) then
addAt(mModelViewStack, 1, mModelView)
else
addAt(mProjectionStack, 1, mProjection)
end if
return 1
end
-- Pop a matrix off the stack, current matrix is now what was on top of the stack
on mPop
lookUpList = [:]
if (mv) then
if count(mModelViewStack) then
set mModelView = getAt(mModelViewStack, 1)
deleteAt(mModelViewStack, 1)
end if
else
if count(mProjectionStack) then
set mProjection = getAt(mProjectionStack, 1)
deleteAt(mProjectionStack, 1)
end if
end if
return 1
end
-- Load the identity matrix into the current matrix
on mLoadIdentity
lookupList = [:]
if (mv) then
set mModelView = [[1000, 0, 0, 0], [0, 1000, 0, 0], [0, 0, 1000, 0], [0, 0, 0, 1000]]
else
set mProjection = [[1000, 0, 0, 0], [0, 1000, 0, 0], [0, 0, 1000, 0], [0, 0, 0, 1000]]
end if
return 1
end
---------------------------------------------------------------------------------------------------
-- MODELVIEW TRANSFORMATIONS ------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
-- MODELING TRANSFORMATIONS:
-- Translate the current matrix by x, y, z
-- Viewing transformations should always preceed modeling transformation in your code.
on xTranslate x, y, z
lookUpList = [:]
set m1 = [[1000, 0, 0, integer(1000*x)], [0, 1000, 0, integer(1000*y)], [0, 0, 1000, integer(1000*z)], [0, 0, 0, 1000]]
set mModelView = Matrix4x4Mult(m1, mModelView)
if mModelView <> 0 then return 1
else return 0
end
-- Rotate the current matrix on the X axis by angle (in radians)
-- Viewing transformations should always preceed modeling transformation in your code.
on xRotateX a
lookUpList = [:]
set m3 = [[1000, 0, 0, 0], [0, integer(1000*cos(a)), integer(1000*-sin(a)), 0], [0, integer(1000*sin(a)), integer(1000*cos(a)), 0], [0, 0, 0, 1000]]
set mModelView = Matrix4x4Mult(m3, mModelView)
if mModelView <> 0 then return 1
else return 0
end
-- Rotate the current matrix on the Y axis by angle (in radians)
-- Viewing transformations should always preceed modeling transformation in your code.
on xRotateY a
lookUpList = [:]
set m4 = [[integer(1000*cos(a)), 0, integer(1000*sin(a)), 0], [0, 1000, 0, 0], [integer(1000*-sin(a)), 0, integer(1000*cos(a)), 0], [0, 0, 0, 1000]]
set mModelView = Matrix4x4Mult(m4, mModelView)
if mModelView <> 0 then return 1
else return 0
end
-- Rotate the current matrix on the Z axis by angle (in radians)
-- Viewing transformations should always preceed modeling transformation in your code.
on xRotateZ a
lookUpList = [:]
set m5 = [[integer(1000*cos(a)), integer(1000*-sin(a)), 0, 0], [integer(1000*sin(a)), integer(1000*cos(a)), 0, 0], [0, 0, 1000, 0], [0, 0, 0, 1000]]
set mModelView = Matrix4x4Mult(m5, mModelView)
if mModelView <> 0 then return 1
else return 0
end
-- Scale the current matrix by x, y, z (>1.0 grows, <1.0 shrinks)
-- Viewing transformations should always preceed modeling transformation in your code.
on xScale x, y, z
lookUpList = [:]
set m2 = [[integer(1000*x), 0, 0, 0], [0, integer(1000*y), 0, 0], [0, 0, integer(1000*z), 0], [0, 0, 0, 1000]]
set mModelView = Matrix4x4Mult(m2, mModelView)
if mModelView <> 0 then return 1
else return 0
end
-- VIEWING TRANSFORMATIONS: Camera placement routines:
-- pilotView uses the analogy of a plane to position the camera, with the runway at the origin and the
-- plane at coordinates (plane_x, plane_y, plane_z), with a roll, pitch, and heading (in all in degrees)
-- Viewing transformations should always preceed modeling transformation in your code.
on pilotView plane_x, plane_y, plane_z, roll, pitch, heading
lookUpList = [:]
set roll = (roll/360.0) * (2.0*PI)
set pitch = (pitch/360.0) * (2.0*PI)
set heading = (heading/360.0) * (2.0*PI)
if (xRotateZ(roll) AND xRotateY(pitch) AND xRotateX(heading) AND xTranslate(-plane_x, -plane_y, -plane_z)) then
return 1
else
return 0
end if
end
-- polarView uses the analogy of a camera orbiting around an object that's centered at the origin, constantly
-- pointing at the origin. Distance defines the radius of the orbit, azimuth describes the angle of rotation
-- of the camera around the object on the x-y plane, measured from the positive y-axis. Elevation measures
-- the angle of rotation of the camera in the y-z plane, measured from the positive z-axis. Twist represents
-- the rotation of the viewing volume around its line of sight.
-- Viewing transformations should always preceed modeling transformation in your code.
on polarView distance, twist, elevation, azimuth
lookUpList = [:]
if (xTranslate(0, 0, -distance) AND xRotateZ(-twist) AND xRotateX(-elevation) AND xRotateZ(azimuth)) then
return 1
else
return 0
end if
end
-----------------------------------------------------------------------------------------------------
-- Utility Routines ---------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------
on spawnDWatcher
set DWatcher = new (script "3DWatcher")
end
on killDWatcher
repeat while deleteOne(the actorlist, DWatcher)
end repeat
end
-- isBackfacing determines if, from the given list of transformed vertices and certain environmental variables,
-- a quad's front or back is facing the camera, and returns a 1 if it is backfacing, 0 otherwise.
on isBackfacing vPList
a = 0
repeat with i = 0 to 3
set z = ((i + 1) mod 4)
set m = i+1
set n = z+1
a = a + ((vPList[m][1] * vPList[n][2]) - (vPList[n][1] * vPList[m][2]))
end repeat
set a = a/2.0
if cullBackfaces_cw then
if a > 0 then return 0
else return 1
else
if a > 0 then return 1
else return 0
end if
end
-- zSort sets the locZ properties of the
on zSort listB
put count(listB) into listBCount
repeat with i = 1 to listBCount
set vP = listB[i].myVPos
if vP <> 0 then
sprite(listb[i].mySprite).locZ = 1000-(vP[3] * 1000)
end if
end repeat
return 0
end
-- Juggle is an old sprite sorting routine for D6.5 and below. If you are using D7, use the zSort handler above.
-- It works just like Juggle but doesn't move any sprites around, it instead uses the locZ property and takes one
-- less argument.
on Juggle listB, lowSprite
global passCtr
global countlist
set listA = [:]
sort listA
put count(listB) into listBCount
repeat with i = 1 to listBCount
set vP = the myVPos of getAt(listB, i)
if vP <> 0 then
addProp(listA, getAt(vP, 3), getAt(listB, i))
else
addProp(listA, 0.0, getAt(listB, i))
end if
end repeat
set countlist = count(lista)
--sort listA
--put listA
set j = 0
put (lowSprite + listBCount - 1) into highSprite
repeat with i = highSprite down to lowSprite
set j = j + 1
set z= getAt(listA, j)
-- fixScriptInstanceList(z, i)
setSprite(z, i)
set the member of sprite i = the myMember of z
set the rect of sprite i = the myRect of z
set the blend of sprite i = the myBlend of z
set the ink of sprite i = the myInk of z
-- backColor, foreColor, editable, moveableSprite, constraint, trails
end repeat
return 0
end
------------------------------------------------------------------------------------------
-- OTHER UTILITY & MATH FUNCTIONS YOU PROBABLY DON'T NEED TO DIRECTLY ACCESS -------------
------------------------------------------------------------------------------------------
-- PerspectiveDivide converts a homogeneous coordinate into a normalized device coordinate.
-- point is a 4 element list. Returns a 3-element list on success, 0 if the point is not to be drawn.
on PerspectiveDivide thePoint
if count(thePoint) = 4 then
set w = getAt(thePoint, 4)
if w <> 0 then
set x = getAt(thePoint, 1)
set y = getAt(thePoint, 2)
set z = getAt(thePoint, 3)
if activateClipping = TRUE then
case TRUE of
(z < -w), (z > w), (y < -w), (y > w), (x < -w), (x > w) : return 0 -- clipping
end case
end if
return [x/w, y/w, abs(z/w)]
else
return 0
end if
else
return 0
end if
end
-- ViewportXForm takes a normalized device coordinate point and transforms it into a
-- window coordinate. The argument is a 3-element list. A 3-element list, [h, v, z] is
-- returned on success. 0 if the point is not to be drawn
on ViewportXForm thePoint
if count(thePoint) = 3 then
return [(getAt(thePoint, 1) + 1.0) * xCoefficient, (getAt(thePoint, 2) + 1.0) * yCoefficient, getAt(thePoint, 3)]
else
return 0
end if
end
--Useful math functions
-- 4x4 Matrices are defined as nested lists: [[],[],[],[]]
-- 1x4 Matrices are defined as a 4 element list: [,,,]
on test
a = [3,3,3,3]
starttimer
repeat with i = 1 to 500000
set z = getAt(a, 3)
end repeat
put the timer
starttimer
repeat with i = 1 to 500000
z = a[3]
end repeat
put the timer
end
--Matrix4x4Mult takes two 4x4 matrices, multiples them and returns the 4x4 result.
-- (Thanks to Jakob Hede Madsen for the optimization!)
on Matrix4x4Mult a, b
--
-- Matrix4x4Mult takes two 4x4 matrices, multiples them and returns the 4x4 result.
--on Matrix4x4Mult a, b
--
-- if count(a[1]) = count(b) then
-- put a[1][1] into a11
-- put a[2][1] into a21
-- put a[3][1] into a31
-- put a[4][1] into a41
-- put a[1][2] into a12
-- put a[2][2] into a22
-- put a[3][2] into a32
-- put a[4][2] into a42
-- put a[1][3] into a13
-- put a[2][3] into a23
-- put a[3][3] into a33
-- put a[4][3] into a43
-- put a[1][4] into a14
-- put a[2][4] into a24
-- put a[3][4] into a34
-- put a[4][4] into a44
-- put b[1][1] into b11
-- put b[2][1] into b21
-- put b[3][1] into b31
-- put b[4][1] into b41
-- put b[1][2] into b12
-- put b[2][2] into b22
-- put b[3][2] into b32
-- put b[4][2] into b42
-- put b[1][3] into b13
-- put b[2][3] into b23
-- put b[3][3] into b33
-- put b[4][3] into b43
-- put b[1][4] into b14
-- put b[2][4] into b24
-- put b[3][4] into b34
-- put b[4][4] into b44
-- return [¬
--[((a11*b11)+(a12*b21)+(a13*b31)+(a14*b41))/1000,¬
-- ((a11*b12)+(a12*b22)+(a13*b32)+(a14*b42))/1000,¬
-- ((a11*b13)+(a12*b23)+(a13*b33)+(a14*b43))/1000,¬
-- ((a11*b14)+(a12*b24)+(a13*b34)+(a14*b44))/1000],¬
--[((a21*b11)+(a22*b21)+(a23*b31)+(a24*b41))/1000,¬
-- ((a21*b12)+(a22*b22)+(a23*b32)+(a24*b42))/1000,¬
-- ((a21*b13)+(a22*b23)+(a23*b33)+(a24*b43))/1000,¬
-- ((a21*b14)+(a22*b24)+(a23*b34)+(a24*b44))/1000],¬
--[((a31*b11)+(a32*b21)+(a33*b31)+(a34*b41))/1000,¬
-- ((a31*b12)+(a32*b22)+(a33*b32)+(a34*b42))/1000,¬
-- ((a31*b13)+(a32*b23)+(a33*b33)+(a34*b43))/1000,¬
-- ((a31*b14)+(a32*b24)+(a33*b34)+(a34*b44))/1000],¬
--[((a41*b11)+(a42*b21)+(a43*b31)+(a44*b41))/1000,¬
-- ((a41*b12)+(a42*b22)+(a43*b32)+(a44*b42))/1000,¬
-- ((a41*b13)+(a42*b23)+(a43*b33)+(a44*b43))/1000,¬
-- ((a41*b14)+(a42*b24)+(a43*b34)+(a44*b44))/1000]]
-- else
-- put "Error: Could not multiply matrices, a.cols != b.rows != 4."
-- return 0
-- end if
--end
-- Matrix1x4Mult takes a 1x4 matrix as its first argument and a 4x4 matrix as its second argument
-- and multiplies them, returning the resulting 1x4 matrix.
on Matrix1x4Mult c, b
if count(b[1]) = count(c) then
a1 = integer(c[1]*1000)
a2 = integer(c[2]*1000)
a3 = integer(c[3]*1000)
a4 = integer(c[4]*1000)
return [¬
((b[1][1]*a1)+(b[1][2]*a2)+(b[1][3]*a3)+(b[1][4]*a4))/100000.0,¬
((b[2][1]*a1)+(b[2][2]*a2)+(b[2][3]*a3)+(b[2][4]*a4))/100000.0,¬
((b[3][1]*a1)+(b[3][2]*a2)+(b[3][3]*a3)+(b[3][4]*a4))/100000.0,¬
((b[4][1]*a1)+(b[4][2]*a2)+(b[4][3]*a3)+(b[4][4]*a4))/100000.0]
else
put "Error: Could not multiple matrices, a.cols != b.rows."
return 0
end if
end
-- location = x, y, z, intensity = r, g, b
-- creates a light and returns its unique ID in the lightingList
on createLight location, intensity
set x = count(lightinglist)
-- deletes a light
on deleteLight uniqueID
lightingList.deleteProp(uniqueID)
end
on deleteAllLights
lightinglist = [:]
end
on calculateIntensity polygon, lightingList --, viewPoint
set x = count(lightingList)
repeat with i = 1 to x
end repeat
if x = 0 then return 0
end
on vGetViewingVector viewPoint, normalTail
return( [0, 0, 1] )
end
-- vGetH takes the lighting vector and the viewing vector and returns the unit vector halfway between these two.
-- This is cheaper than using the exact reflection vector.
on vGetH l, v
return vScalarMult(vAdd(l, v)*0.5)
end
-- vGetReflectionVector takes the normal vector of a surface, n, and the incident lighting vector l,
-- and returns the reflected vector.
on vGetReflectionVector n, l
return( vSubtract(vScalarMult(n, 2 * vDotProduct(n, l)), l) )
end
-- vGetLightingVector takes the coordinates of a light (4 element list) and a vertex (4 element list) and
-- returns a the lighting vector associated with them.
on vGetLightingVector light, normalTail
return([light[1] - normalTail[1], light[2] - normalTail[2], light[3] - normalTail[3]])
end
-- vNormal takes three vertices (4 element lists) of a polygon (v2 being "between" v1 and v3) and returns
-- the normal vector to that polygon.
on vNormal v1, v2, v3
-- convert vertices to two vectors
nv1 = [v3[1] - v2[1], v3[2] - v2[2], v3[3] - v2[3]]
nv2 = [v1[1] - v2[1], v1[2] - v2[2], v1[3] - v2[3]]
-- calculate normal
return(vCrossProduct(nv1, nv2))
end
-- vCrossProduct takes two vectors (3 element lists each) and returns their cross product (a 3 element list)
on vCrossProduct v, w
return([v[2]*w[3] - v[3]*w[2], v[3]*w[1] - v[1]*w[3], v[1]*w[2] - v[2]*w[1]])
end
-- vDotProduct takes two vectors (3 element lists each) and returns their dot product
on vDotProduct v, w
return(v[1]*w[1] + v[2]*w[2] + v[3]*w[3])
end
-- vScalarMult multiplies vector v by a scalar
on vScalarMult v, scalar
return([v[1] * scalar, v[2] * scalar, v[3] * scalar])
end
-- vNormalize takes a vector and returns the normalize unit vector of that vector
on vNormalize v
z = vMagnitude(v)
return([v[1]/float(z[1]), v[2]/float(z[2]), v[3]/float(z[3])])
end
-- vMagnitude takes a vector and returns its magnitude
on vMagnitude v
return(sqrt((v[1]*v[1] + v[2]*v[2] + v[3]*v[3])))
end
-- vAdd adds one vector to another
on vAdd v, w
return([v[1] + w[1], v[2] + w[2], v[3] + w[3]])
end
-- vSubtract subtracts one vector from another
on vSubtract v, w
return([v[1] - w[1], v[2] - w[2], v[3] - w[3]])
end
-- scaleRect takes a rect and scales the size of the rect based on the scalar coefficient
-- returns the new Rect()
on scaleRect oldrect, scalar
-- set oldh = (the bottom of oldrect) - (the top of oldrect)
-- set oldw = (the right of oldrect) - (the left of oldrect)
set oldh = the height of oldrect
set oldw = the width of oldrect
set midw = (oldw/2)+the left of oldrect
set midh = (oldh/2)+the top of oldrect
set halfh = (scalar * oldh)/2
set halfw = (scalar * oldw)/2
------------------------------------------------------------------------------
-- The functions below aren't used directly in Dave's 3D Engine, but are --
-- provided for backwards compatibility with previous versions. --
------------------------------------------------------------------------------
-- acos() - arccosine function (Thanks Hopper-Ex!)
-- takes values between -1 and 1, returns values between 0 and PI
on acos x
if abs(x) > 1 then
return 0
else if x = 0 then
return 0
else
return(atan(sqrt(1.0 - x*x)/float(x)))
end if
end
-- asin() - arcsine function (Thanks Again Hopper-Ex!)
-- takes values between -1 and 1, returns values between -PI/2.0 and PI/2.0
on asin x
if abs(x) > 1 then
return void
else if x = 1 then
return(PI/2.0)
else if x = -1 then
return(-PI/2.0)
else
return atan(float(x)/sqrt(1.0 - x*x))
end if
end asin
on floatDiv floatA, floatB
return integer(floatA - 0.5) / integer(floatB - 0.5)
end
Contact
MMI
36 South Court Sq
Suite 300
Newnan, GA 30263
USA