Script.as

actionscript · 778 lines · part of Three cubes test · raw

/*

v 004
-----

added colours to shapes
added filled polygon rendering
draws split lines in a different colour (inefficient + doesn't always work!)
added case for camera behind or in front of plane in BSPNode.render (inefficient!)
bodged 3d perspective rendering (look out for those 20's)

v 005
-----

got white lines working properly (but inefficiently)
added debug display
added stop & go buttons


v 007
-----

28/Aug/2005

Fixing bug where faces disappear when cubes are used


*/



//---- MovieClip -------------------------------------------------------------//



//---- Polygon ---------------------------------------------------------------//

/*
Polygon
Polygon.addPoint( point_coordinate_vector )
Polygon.copyOf( polygon_to_copy )
Polygon.render( movie_clip_to_render_into )
Polygon.toString( )

*/

function Polygon ( )
{
	//trace("Polygon <9init>")

	this.normal = null
	this.facing = 0
	this.point = new Array()
	this.white = new Array()
}

//---- Polygon.addPoint

Polygon.prototype.addPoint = function( p )
{
  this.point.push ( p )

  //- Calculate normal if we have just added a third point

  if(this.point.length == 3 )
	{
		var a = [ this.point[0][0] - this.point[1][0] ,
		          this.point[0][1] - this.point[1][1] ,
		          this.point[0][2] - this.point[1][2] ]

    var b = [ this.point[2][0] - this.point[1][0] ,
		          this.point[2][1] - this.point[1][1] ,
		          this.point[2][2] - this.point[1][2] ]

		this.normal = [ a[1] * b[2] - a[2] * b[1] ,
   		              a[2] * b[0] - a[0] * b[2] ,
		                a[0] * b[1] - a[1] * b[0] ]
  }
}

//---- Polygon.copyOf - duplicates a polygon

Polygon.prototype.copyOf = function ( p )
{
	this.point = p.point
	this.normal = p.normal
	this.col = p.col
	this.white = p.white
}

//---- Polygon.render

Polygon.prototype.render = function ( b )
{
  if( this.point.length <= 0 ) return

  b.lineStyle( 1, 0x000000, 100 )

  var i = this.point.length-1;
	b.moveTo( 200 + 400 * this.point[ i ][0] / (this.point[i][2] + 20), 200 + 400 *this.point[ i ][1] / (this.point[i][2] + 20) )

  b.beginFill( this.col, 95 )

  for( i=0; i<this.point.length; i++)
	{
		ii = i-1; if(ii<0) ii = this.point.length-1

    if(this.white[ii])   b.lineStyle()// 1, 0x000000, 50 )  //  Draw feint line if it's not a main edge (the edge has been created by a split)

  	b.lineTo( 200 + 400 * this.point[i][0] / (this.point[i][2] + 20), 200 + 400 * this.point[i][1] / (this.point[i][2] + 20))

    if(this.white[ii])   b.lineStyle( 1, 0x000000, 100 )
  }

	b.endFill()


  //- TEMP: Draw reflection
/*
  var refl_plane = 32

  b.lineStyle( 1, 0x000000, 20 )

  var i = this.point.length-1;
	b.moveTo( 200 + 100 * this.point[ i ][0] / (this.point[i][2] + 20), 200 + 100 *(refl_plane-this.point[ i ][1]) / (this.point[i][2] + 20) )

  b.beginFill( this.col, 75 )

  for( i=0; i<this.point.length; i++)
	{
		ii = i-1; if(ii<0) ii = this.point.length-1

    if(this.white[ii])   b.lineStyle( 1, 0xFFFFFF, 15 )

  	b.lineTo( 200 + 100 * this.point[i][0] / (this.point[i][2] + 20), 200 + 100 * (refl_plane-this.point[ i ][1]) / (this.point[i][2] + 20))

    if(this.white[ii])   b.lineStyle( 1, 0x000000, 20 )
  }

	b.endFill()
*/
}




//---- Polygon.toString

Polygon.prototype.toString = function ( )
{
  var s

  s  = '<poly numpoints="' + this.point.length + '">\n'

//  s += '</poly>\n'

  return s
}



//---- Shape -----------------------------------------------------------------//

/*
  Shape( point_array, faces_array, colour )
  Shape.transformPoint( source_point_coord_array, dest_point_coord_array )
  Shape.transform( rx, ry, rz, x, y, z )
*/

function Shape ( points, faces, col )
{
  var i, j

	this.point = points
	this.tpoint = new Array()

  this.normal = new Array()
  this.tnormal = new Array()

  this.poly = new Array()

	this.col = col

  //- Fill tpoint array

  for( i=0; i<this.point.length; i++ ) this.tpoint[i] = [ this.point[i][0], this.point[i][1], this.point[i][2] ]

  //- Create array of polygons

	for( i=0; i<faces.length; i++)
	{
		this.poly[i] = new Polygon ( )

		//  Add points to polygon

    for( j=0; j<faces[i].length; j++ ) this.poly[i].addPoint ( this.tpoint[ faces[i][j] ] )

		//  Get polygon's normal and replace it with transformed normal

		this.normal[i] = this.poly[i].normal
    this.tnormal[i] = [ this.normal[i][0], this.normal[i][1], this.normal[i][2] ]
    this.poly[i].normal = this.tnormal[i]
		this.poly[i].col = col
	}

  //- Set up other vars

	this.srx = 0
	this.crx = 0
	this.sry = 0
	this.cry = 0
}

//---- Shape.transformPoint

Shape.prototype.transformPoint = function ( s, d )
{
	var x, y, z
	var nx, ny, nz

	x = s[0]
	y = s[1]
	z = s[2]

  //  Y rotate
  nx = x * this.cry - this.sry * z
  ny = y
  nz = x * this.sry + this.cry * z

	//  X rotate
  x = nx
  y = ny * this.crx - this.srx * nz
  z = ny * this.srx + this.crx * nz

  //  Z rotate
  d[0] = x * this.crz - this.srz * y
  d[1] = x * this.srz + this.crz * y
  d[2] = z
}

//---- Shape.transform

Shape.prototype.transform = function ( rx, ry, rz, x, y, z )
{
  this.srx = Math.sin(rx)
  this.crx = Math.cos(rx)

 	this.sry = Math.sin(ry)
  this.cry = Math.cos(ry)

 	this.srz = Math.sin(rz)
  this.crz = Math.cos(rz)

  //- Make transformed points

	var i

	for( i=0; i<this.point.length; i++ )
	{
		this.transformPoint ( this.point[i], this.tpoint[i] )
		this.tpoint[i][0] += x
		this.tpoint[i][1] += y
		this.tpoint[i][2] += z
	}

	//- Transform normal

	for( i=0; i<this.point.length; i++ )
	{
  	this.transformPoint ( this.normal[i], this.tnormal[i] )
	}
}



//---- BSPNode ---------------------------------------------------------------//

/*
BSPNode( polygon_object_to_split_space )
BSPNode.addPolygon( polygon_to_add_to_node_and_split_if_necessary )
BSPNode.inFront = function ( point_to_determine_whether_in_front_or_behind )
BSPNode.splitLine = function ( p1, p2 )
BSPNode.splitPolygon = function ( polygon_to_split, polygon_to_reveive_behind_part, polygon_to_receive_in_front_part )
BSPNode.render = function ( movie_clip_to_render_into )
BSPNode.toString = function ( )
*/

function BSPNode ( poly )
{
	//trace( "BSPNode <init> "+poly)

  //- Child nodes

  this.front = null
	this.back = null

  //- Polygon attached to this node

  this.poly = poly

  //- Plane definition

	this.normal = this.poly.normal
	this.point  = this.poly.point[0]
}

//---- BSPNode.addPolygon

BSPNode.prototype.addPolygon = function ( poly )
{
	////trace ("BSPNode.addPolygon "+ poly )
/*
  if( this.back )
	{
		this.back.addPolygon ( poly )
	}
	else
	{
		this.back = new BSPNode ( poly )
	}
*/

  //- Split polygon

	var poly_back  = new Polygon ( )
	var poly_front = new Polygon ( )

  this.splitPolygon( poly, poly_back, poly_front )

	//- Add to child nodes

  if( poly_back.point.length )
	{
    if( this.back  != null ) this.back.addPolygon( poly_back )
                        else this.back = new BSPNode( poly_back )
	}

	if( poly_front.point.length )
	{
    if( this.front != null ) this.front.addPolygon( poly_front )
                        else this.front = new BSPNode( poly_front )
	}

}

//---- BSPNode.inFront - returns >0 if the given point is in front of the plane, <0 if it is behind

BSPNode.prototype.inFront = function ( p )
{
	//trace ( p+" :: "+this.point+" :: "+this.normal)
	return ( p[0] - this.point[0] ) * this.normal[0] +
	       ( p[1] - this.point[1] ) * this.normal[1] +
	       ( p[2] - this.point[2] ) * this.normal[2];
}

//---- BSPNode.splitLine - finds the intersection of the given line and this node's plane

BSPNode.prototype.splitLine = function ( p1, p2 )
{
  var a = [ p1[0] - this.point[0] ,
						p1[1] - this.point[1] ,
						p1[2] - this.point[2] ]

  var b = [ p2[0] - this.point[0] ,
						p2[1] - this.point[1] ,
						p2[2] - this.point[2] ]

	var an = a[0]*this.normal[0] + a[1]*this.normal[1] + a[2]*this.normal[2]
	var bn = b[0]*this.normal[0] + b[1]*this.normal[1] + b[2]*this.normal[2]


 // OLD Way - doing t first was resulting in underflow of numbers
/*
	var t = bn / (bn - an)
	var tt = 1.0 - t

  //- Return intersection point

	return [ p1[0] * t + p2[0] * tt ,
	         p1[1] * t + p2[1] * tt ,
	         p1[2] * t + p2[2] * tt ]
*/

  //  NEW way

	var t = bn
  var d = bn - an

  t /= d

  //- Return intersection point

	return [ p2[0] + (p1[0] - p2[0]) * t ,
	         p2[1] + (p1[1] - p2[1]) * t ,
	         p2[2] + (p1[2] - p2[2]) * t ]

}

//---- BSPNode.splitPolygon - splits a polygon around this node's plane

BSPNode.prototype.splitPolygon = function ( poly, back, front )
{
	//trace ( "BSPNode.splitPolygon" )

  //- Check which side of plane each polygon point is on

  var infront = true
	var behind  = true

  var pdist = new Array()

	for( i = 0; i < poly.point.length; i ++ )
	{
		pdist[i] = this.inFront( poly.point[i] )

    if( pdist[i] > -0.001 && pdist[i] < 0.001 ) pdist[i] = 0

		//trace( pdist[i] )
		//trace( poly.normal )

		if( pdist[i] < 0 ) infront = false
		if( pdist[i] > 0 ) behind  = false
	}

  //- Deal with 3 possibilities: totally in front, totally behind, intersecting

	if(infront)
	{
    front.copyOf ( poly )
	}
	else if(behind)
	{
    back.copyOf ( poly )
	}
	else
	{
		var i, ii
		var pt

    //- Clip against plane

		for( i = 0; i < poly.point.length; i++ )
		{
			ii = (i+1) % poly.point.length

			if( pdist[i] < 0 && pdist[ii] < 0)
			{
				back.addPoint( poly.point[i] )
				back.white.push( 0 + poly.white[i] )
			}
			else if( pdist[i] < 0 && pdist[ii] >= 0)
			{
				back.addPoint( poly.point[i] )
				back.white.push( 0 + poly.white[i] )

				pt = this.splitLine( poly.point[i], poly.point[ii] )

        back.addPoint( pt )
				back.white.push( 1 )

        front.addPoint( pt )
				front.white.push( 0 + poly.white[i] )
			}
			else if( pdist[i] >= 0 && pdist[ii] >= 0)
			{
				front.addPoint( poly.point[i] )
				front.white.push( 0 + poly.white[i] )
			}
			else if( pdist[i] >= 0 && pdist[ii] < 0)
			{
				front.addPoint( poly.point[i] )
				front.white.push( 0 + poly.white[i] )

        pt = this.splitLine( poly.point[i], poly.point[ii] )

        front.addPoint( pt )
				front.white.push( 1 )

				back.addPoint( pt )
				back.white.push( 0 + poly.white[i] )

			}
		}
//			front.normal = poly.normal
//			back.normal = poly.normal
//    front.col = ((poly.col >> 1)&0x7F7F7F)+0x7F7F7F
    front.col = poly.col
		back.col = poly.col

  }

}

//---- BSPNode.render

BSPNode.prototype.render = function ( b )
{
	////trace ( 'BSPNode.render ::'+this.poly+'::' )

  var facing = this.normal[0] * this.point[0] + this.normal[1] * this.point[1] + this.normal[2] * ( this.point[2] + 20)

	if( facing < 0)
	{
    if ( this.back  ) this.back.render  ( b )
    this.poly.render ( b )
    if ( this.front ) this.front.render ( b )
	}
	else
	{
    if ( this.front ) this.front.render ( b )
    this.poly.render ( b )
    if ( this.back  ) this.back.render  ( b )
	}
}

//---- BSPNode.toString

BSPNode.prototype.toString = function ( )
{
	var a, b

  if( this.back )
	{
		a = ""+this.back
  	a = "  " + a.split("\n").join("\n  ")
//		a = "<back>\n"+a+"</back>\n"
	}

  if( this.front )
	{
		b = ""+this.front
  	b = "  " + b.split("\n").join("\n  ")
//		b = "<front>\n"+b+"</back>\n"
	}

  p = "" + this.poly

  return p + a + b

	return "<node>\n"
	     + p
			 + a
			 + b
			 + "</node>"

}



//---- BSPTree ---------------------------------------------------------------//

/*
BSPTree( )
BSPTree.addPolygon = function ( poly )
BSPTree.addShape = function ( shape )
BSPTree.render = function ( b )
BSPTree.toString = function ( )
*/


function BSPTree ( )
{
  this.node = null
}

//---- BSPTree.addPolygon - adds a single polygon to the tree

BSPTree.prototype.addPolygon = function ( poly )
{
	////trace( "BSPTree.addPolygon" )

	if(this.node)
	{
		this.node.addPolygon ( poly )
	}
	else
	{
		this.node = new BSPNode ( poly )
	}
}

//---- BSPTree.addShape - adds a shape to the tree

BSPTree.prototype.addShape = function ( shape )
{
  var i

 	for ( i = 0; i < shape.poly.length; i ++ )
 	{
 		this.addPolygon ( shape.poly[i] )
 	}

}

//---- BSPTree.render - renders the shape stored in the tree into the given MovieClip

BSPTree.prototype.render = function ( b )
{
  if( this.node ) this.node.render ( b )
}

//---- BSPTree.toString - returns debug info about tree

BSPTree.prototype.toString = function ( )
{
	return "" + this.node
}



//---- timer -----------------------------------------------------------------//

function timer ( )
{
//	trace('-----frame-----')

	rx1 += 0.008//*3
	ry1 -= 0.013//*3
	rx2 -= 0.009*3
	ry2 += 0.007*3
	rx3 += 0.011*3
	ry3 -= 0.005*3

/*
	shapes[0].transform ( rx1, ry1, 0,          1.4,  1.3, -1.2 )
	shapes[1].transform ( rx2, 0, ry2,         -3.3, -2.1,  1.1 )
	shapes[2].transform ( ry2-rx1, rx3, ry3,    1.1, -3.3,  2.4)
	shapes[3].transform ( ry1-rx3, rx1-ry2, rx3,    2.1, -1.3,  3.4)
	shapes[4].transform ( rx2+rx1, ry3, rx3-ry1,   -1.4,  2.3,  -2.4)
*/

//	shapes[0].transform ( rx1, ry1, 0,               1.4,  1.3,   5.2 )
//	shapes[1].transform ( rx2, 0, ry2,              -3.3, -2.1,   5.1 )
//	shapes[2].transform ( ry2-rx1, rx3, ry3,         1.1, -3.3,   2.4 )
//	shapes[3].transform ( ry1-rx3, rx1-ry2, rx3,     1.1, 0.3,  0.0 )
//	shapes[4].transform ( rx2+rx1, ry3, rx3-ry1,    -1.4,  2.3,  -2.4 )
//	shapes[5].transform ( ry1-rx2, rx3-ry2, rx1,    -1.7, -2.3,  -1.3 )

	shapes[0].transform ( rx1, ry1, 0,              -2,0,0)
	shapes[1].transform ( rx2, 0, ry2,              2,0,0)
	shapes[2].transform ( ry2-rx1, rx3, ry3,        0,-2,0)
	shapes[3].transform ( ry1-rx3, rx1-ry2, rx3,    0,2,0)

  buffer.clear()

  bsp = new BSPTree ( )

  bsp.addShape ( shapes[0] )
  bsp.addShape ( shapes[1] )
  bsp.addShape ( shapes[2] )
//  bsp.addShape ( shapes[3] )
//  bsp.addShape ( shapes[4] )
//  bsp.addShape ( shapes[5] )

  bsp.render ( buffer )

//	bsp2 = new BSPTree ( )
//	bsp2.addShape (shapes[1])
//	bsp2.render ( buffer )

//  debug = bsp

}



//---- DATA ------------------------------------------------------------------//

points =
[
  [-10.0, -10.0,  10.0],  // 0
  [-10.0, -10.0, -10.0],  // 1
  [-10.0,  10.0,  10.0],  // 2
  [-10.0,  10.0, -10.0],  // 3
  [ 10.0, -10.0,  10.0],  // 4
  [ 10.0, -10.0, -10.0],  // 5
  [ 10.0,  10.0,  10.0],  // 6
  [ 10.0,  10.0, -10.0]   // 7
]

faces =
[
  [2, 3, 7, 6],  // A
  [6, 7, 5, 4],  // B
  [2, 6, 4, 0],  // C
  [1, 0, 4, 5],  // F
  [3, 2, 0, 1],  // D
  [3, 1, 5, 7]   // E
]




points2 =
[
  [-10.0, 0.0,  10.0],  // 0
  [-10.0, 0.0, -10.0],  // 1
  [ 10.0, 0.0, -10.0],  // 2
  [ 10.0, 0.0,  10.0]   // 3
]

faces2 =
[
  [0,1,2,3]
]



points3 =
[
  [-8.5, 0.0,  3.5],  // 0
  [-8.5, 0.0, -3.5],  // 1
  [-2.5, 0.0, -3.5],  // 2
  [-2.5, 0.0,  3.5]   // 3
]

faces3 =
[
  [0,1,2,3]
]




//---- main ------------------------------------------------------------------//


  for( i=0; i<8; i++ )
  {
    points[i][0] *= 0.5
    points[i][1] *= 0.5
    points[i][2] *= 0.5

//    points[i][1] += 5
  }

	shapes = new Array ( )
/*
	shapes[0] = new Shape ( points, faces, 0xCC0000 )
	shapes[1] = new Shape ( points, faces, 0x00CC00 )
	shapes[2] = new Shape ( points, faces, 0x0000CC )
	shapes[3] = new Shape ( points, faces, 0xCCCC00 )
	shapes[4] = new Shape ( points, faces, 0x9900CC )
*/

	shapes[0] = new Shape ( points, faces, 0xFF6600 )
	shapes[1] = new Shape ( points, faces, 0x333344 )
	shapes[2] = new Shape ( points, faces, 0xCCDDFF )
	shapes[3] = new Shape ( points, faces, 0xFF0000 )
//	shapes[4] = new Shape ( points2, faces2, 0xFFFFFF )
//	shapes[5] = new Shape ( points2, faces2, 0xFFFFFF )

	//- Render buffer

  createEmptyMovieClip( "buffer", 1 )

  //- Set angles

	rx1 = 1.318
	ry1 = 4.583
	rx2 = 5.429
	ry2 = 5.947
	rx3 = 1.911
	ry3 = 3.505

	shapes[0].transform ( rx1, ry1, 0,         0, 0, 0 )
	shapes[1].transform ( rx2, 0, ry2,         0, 0, 0 )
	shapes[2].transform ( ry2, rx1, 0,         0, 0, 0 )
	shapes[3].transform ( ry1, ry2, 0,         0, 0, 0 )


  //- Set up timer

  onEnterFrame = timer

//timer()