<HTML xmlns:v="urn:schemas-microsoft-com:vml">
<STYLE>
v\:* { behavior: url(#default#vml); }
</STYLE>
<head>
<style>
button { font-face:arial;font-size:10px;border:1px solid black;}
input { font-face:arial;font-size:10px;border:1px solid black;}
</style>
<script>
/********************************************/
/*********************************************
3D Engine - Faces
BY JOEY FORTUNA
http://neoncelery.com
COPYRIGHT (C) 2004, Joey Fortuna
PERMISSION TO USE GRANTED PROVIDING THE
ADOPTER INCLUDES THIS NOTICE
*********************************************/
/********************************************/
var nGon;
var moveX=0;
var oldMoveX=0;
var moveY=0;
var oldMoveY=0;
var xRotate=0;
var yRotate=0;
var bRightMouseDown=false;
var bLeftMouseDown=false;
var iDistance=525;
var iFov=590;
var iLineYDiff=60;
var iTimeoutId=0;
var iRotationAngle=5;
var iZoom=0;
function drawPolygon() {
nGon=new nGonObj();
getWinSize();
/*******************
X-WING FIGHTER
point coords
..probably better to define faces in triplets
NOTE: the order of points matters somehow for face normals.
example:
-25,-75,25 -50,-50,-50 -50,-50,50 works properly, but:
-25,-75,25 -50,-50,50 -50,-50,-50 does not
********************/
var strPoints="";
strPoints+="-25,-75,25 25,-75,25 25,-75,-25"; // top
strPoints+=" 25,-75,-25 -25,-75,-25 -25,-75,25"; //top
strPoints+=" 25,-75,25 50,-50,50 50,-50,-50"; // right side
strPoints+=" 50,-50,-50 25,-75,-25 25,-75,25"; // right side
strPoints+=" -50,-50,50 50,-50,50 -25,-75,25"; //front
strPoints+=" 50,-50,50 25,-75,25 -25,-75,25"; //front
strPoints+=" -50,-50,-50 -25,-75,-25 50,-50,-50"; //back
strPoints+=" 50,-50,-50 -25,-75,-25 25,-75,-25"; //back
strPoints+=" -25,-75,25 -50,-50,-50 -50,-50,50"; // left side
strPoints+=" -50,-50,-50 -25,-75,25 -25,-75,-25"; //left side
strPoints+=" 50,-50,-50 65,-25,50 65,-25,-50"; // long right side
strPoints+=" 50,-50,-50 50,-50,50 65,-25,50"; // long right side
strPoints+=" -50,-50,-50 -65,-25,-50 -65,-25,50"; // long left side
strPoints+=" -50,-50,-50 -65,-25,50 -50,-50,50"; // long left side
strPoints+=" -50,-50,50 -50,-25,65 50,-25,65"; // long front
strPoints+=" -50,-50,50 50,-25,65 50,-50,50"; // long front
strPoints+=" -50,-50,-50 50,-25,-65 -50,-25,-65"; // long back
strPoints+=" -50,-50,-50 50,-50,-50 50,-25,-65"; // long back
//*** CORNERS ***/
strPoints+=" -50,-50,50 -65,-25,50 -50,-25,65";
strPoints+=" 50,-50,50 50,-25,65 65,-25,50";
strPoints+=" -50,-50,-50 -50,-25,-65 -65,-25,-50";
strPoints+=" 50,-50,-50 65,-25,-50 50,-25,-65";
//* BOTTOM */
strPoints+=" -25,75,25 25,75,-25 25,75,25"; // top
strPoints+=" 25,75,-25 -25,75,25 -25,75,-25"; //top
strPoints+=" 25,75,25 50,50,-50 50,50,50"; // right side
strPoints+=" 50,50,-50 25,75,25 25,75,-25"; // right side
strPoints+=" 50,50,-50 65,25,-50 65,25,50"; // long right side
strPoints+=" 50,50,-50 65,25,50 50,50,50"; // long right side
strPoints+=" 65,-25,50 65,25,50 65,25,-50"; // right main
strPoints+=" 65,-25,50 65,25,-50 65,-25,-50"; // right main
strPoints+=" -50,50,50 -25,75,25 50,50,50"; //front
strPoints+=" 50,50,50 -25,75,25 25,75,25"; //front
strPoints+=" -50,25,65 50,25,65 -50,-25,65"; //front main
strPoints+=" -50,-25,65 50,25,65 50,-25,65"; //front main
strPoints+=" -50,50,-50 50,50,-50 -25,75,-25"; //back
strPoints+=" 50,50,-50 25,75,-25 -25,75,-25"; //back
strPoints+=" -50,25,-65 -50,-25,-65 50,25,-65"; //back main
strPoints+=" -50,-25,-65 50,-25,-65 50,25,-65"; //back main
strPoints+=" -25,75,25 -50,50,50 -50,50,-50"; // left side
strPoints+=" -50,50,-50 -25,75,-25 -25,75,25"; //left side
strPoints+=" -50,50,-50 -65,25,50 -65,25,-50"; // long left side
strPoints+=" -50,50,-50 -50,50,50 -65,25,50"; // long left side
strPoints+=" -65,-25,50 -65,25,-50 -65,25,50"; // left main
strPoints+=" -65,-25,50 -65,-25,-50 -65,25,-50"; // left main
strPoints+=" -50,50,50 50,25,65 -50,25,65"; // long front
strPoints+=" -50,50,50 50,50,50 50,25,65"; // long front
strPoints+=" -50,50,-50 -50,25,-65 50,25,-65"; // long back
strPoints+=" -50,50,-50 50,25,-65 50,50,-50"; // long back
//*** CORNERS ***/
//* short */
strPoints+=" -50,50,50 -50,25,65 -65,25,50";
strPoints+=" 50,50,50 65,25,50 50,25,65";
strPoints+=" -50,50,-50 -65,25,-50 -50,25,-65";
strPoints+=" 50,50,-50 50,25,-65 65,25,-50";
//* top */
strPoints+=" -50,-25,65 -65,25,50 -50,25,65";
strPoints+=" -50,-25,65 -65,-25,50 -65,25,50";
strPoints+=" 50,-25,65 50,25,65 65,25,50";
strPoints+=" 50,-25,65 65,25,50 65,-25,50";
strPoints+=" -50,-25,-65 -50,25,-65 -65,25,-50";
strPoints+=" -50,-25,-65 -65,25,-50 -65,-25,-50";
strPoints+=" 50,-25,-65 65,25,-50 50,25,-65";
strPoints+=" 50,-25,-65 65,-25,-50 65,25,-50";
//**** WING STRUTS *****/
// LEFT
strPoints+=" -65,10,10 -150,10,10 -150,10,-10";
strPoints+=" -65,10,10 -150,10,-10 -65,10,-10";
strPoints+=" -65,-10,10 -150,-10,-10 -150,-10,10";
strPoints+=" -65,-10,10 -65,-10,-10 -150,-10,-10";
strPoints+=" -65,10,10 -65,-10,10 -150,10,10";
strPoints+=" -150,-10,10 -150,10,10 -65,-10,10";
strPoints+=" -65,10,-10 -150,10,-10 -65,-10,-10";
strPoints+=" -150,-10,-10 -65,-10,-10 -150,10,-10";
// RIGHT
strPoints+=" 65,10,10 150,10,-10 150,10,10";
strPoints+=" 65,10,10 65,10,-10 150,10,-10";
strPoints+=" 65,-10,10 150,-10,10 150,-10,-10";
strPoints+=" 65,-10,10 150,-10,-10 65,-10,-10";
strPoints+=" 65,10,10 150,10,10 65,-10,10";
strPoints+=" 150,-10,10 65,-10,10 150,10,10";
strPoints+=" 65,10,-10 65,-10,-10 150,10,-10";
strPoints+=" 150,-10,-10 150,10,-10 65,-10,-10";
//******WINGS*******/
strPoints+=" 150,-50,-65 150,50,65 150,-50,65"; //right
strPoints+=" 150,50,-65 150,50,65 150,-50,-65"; //right
strPoints+=" 150,50,-65 150,50,65 125,65,-65"; //right
strPoints+=" 150,50,65 125,65,65 125,65,-65"; //right
strPoints+=" 150,-50,-65 125,-65,-65 150,-50,65"; //right
strPoints+=" 150,-50,65 125,-65,-65 125,-65,65"; //right
strPoints+=" 152,-50,-65 152,-50,65 152,50,65"; //right
strPoints+=" 152,50,-65 152,-50,-65 152,50,65"; //right
strPoints+=" 152,50,-65 127,65,-65 152,50,65"; //right
strPoints+=" 152,50,65 127,65,-65 127,65,65"; //right
strPoints+=" 152,-50,-65 152,-50,65 127,-65,-65"; //right
strPoints+=" 152,-50,65 127,-65,65 127,-65,-65"; //right
strPoints+=" -150,-50,-65 -150,-50,65 -150,50,65"; //left
strPoints+=" -150,50,-65 -150,-50,-65 -150,50,65"; //left
strPoints+=" -150,50,-65 -125,65,-65 -150,50,65"; //left
strPoints+=" -150,50,65 -125,65,-65 -125,65,65"; //left
strPoints+=" -150,-50,-65 -150,-50,65 -125,-65,-65"; //left
strPoints+=" -150,-50,65 -125,-65,65 -125,-65,-65"; //left
strPoints+=" -152,-50,-65 -152,50,65 -152,-50,65"; //left
strPoints+=" -152,50,-65 -152,50,65 -152,-50,-65"; //left
strPoints+=" -152,50,-65 -152,50,65 -127,65,-65"; //left
strPoints+=" -152,50,65 -127,65,65 -127,65,-65"; //left
strPoints+=" -152,-50,-65 -127,-65,-65 -152,-50,65"; //left
strPoints+=" -152,-50,65 -127,-65,-65 -127,-65,65"; //left
nGon.init(strPoints);
nGon.rotate(0,0,0);
randomRotate();
}
function randomRotate() {
var tDate=new Date();
var ms=Math.random()*100;
var xrot=0;
var yrot=0;
nGon.rotate(1,1,1);
iTimeoutId=setTimeout("randomRotate()",2);
}
function getWinSize()
{
winX = document.body.clientWidth - 10;
winY = document.body.clientHeight - 10;
nGon.centerX=winX/2;
nGon.centerY=winY/2-100;
}
function nGonPointObj(tid,x,y,z) {
this.id=tid;
this.x=x;
this.y=y;
this.z=z;
this.cartX;
this.cartY;
this.scrX=x;
this.scrY=y;
/*
var newObj=newObj=document.createElement("div");
newObj.id="nGonPoint"+tid;
newObj.innerHTML="0";
newObj.style.position="absolute";
newObj.style.display="none";
newObj.style.textAlign="center";
newObj.style.fontWeight="bold";
newObj.style.fontSize="16px";
newObj.style.fontFamily="arial";
newObj.style.width="20px";
newObj.style.cursor="hand";
newObj.style.top=this.y+"px";
newObj.style.left=this.x+"px";
var divObj=document.body.appendChild(newObj);
this.divObj=divObj;
this.redraw=NGON_POINT_redraw;
*/
return this;
}
function nGonFaceObj(p1,p2,p3) {
var objShape=document.body.appendChild(document.createElement("v:shape"));
objShape.style.position="absolute";
objShape.style.display="inline";
objShape.style.zindex="-1";
objShape.fillcolor="silver";
objShape.coordorigin="0,0";
this.shape=objShape;
this.myname="shape";
this.p1=p1;
this.p2=p2;
this.p3=p3;
return this;
}
function nGonObj() {
this.centerX=0;
this.centerY=0;
this.centerZ=0;
this.parallel=false;
this.name="NGonObject";
this.nPoints=new Array();
this.nFaces=new Array();
this.angleDivisor=0;
this.angle=0;
this.rotate=NGON_rotate;
this.clear=NGON_clear;
this.addPoint=NGON_addPoint;
this.addFace=NGON_addFace;
this.init=NGON_init;
this.drawFaces=NGON_drawFaces;
this.hasPoint=NGON_hasPoint;
return this;
}
function NGON_POINT_redraw() {
this.divObj.style.left=this.scrX+"px";
this.divObj.style.top=this.scrY+"px";
}
function NGON_hasPoint(strPointId) {
var k=0;
for (k=0;i<this.nPoints.length;k++) {
if (this.nPoints[k].id==strPointId) return true;
}
return false;
}
function NGON_init(strPoints) {
// passed an array of points: "200,200,0 300,200,0 250,200,0"
// create n pointObjs
// create n faceObjs
var pointArray=strPoints.split(" ");
for (i=0;i<pointArray.length;i++) {
var coorArray=pointArray[i].split(",");
var objPointId=coorArray[0]+"_"+coorArray[1]+"_"+coorArray[2];
if (!this.hasPoint(objPointId)) {
var objPoint= new nGonPointObj(objPointId,this.centerX+parseInt(coorArray[0]),this.centerY+parseInt(coorArray[1]),parseInt(coorArray[2]));
objPoint.cartX=parseInt(coorArray[0]);
objPoint.cartY=parseInt(coorArray[1]);
this.addPoint(objPoint);
}
if ((i+1)%3==0) {
var objFace = new nGonFaceObj(this.nPoints[i],this.nPoints[i-1],this.nPoints[i-2]);
this.addFace(objFace);
}
}
}
function NGON_clear() {
for (i=this.nPoints.length-1;i>=0;i--) {
//this.nPoints[i].divObj.style.display="none";
//this.nPoints[i].divObj=null;
this.nPoints.pop(i);
}
while (this.nPoints.length>0) this.nPoints.pop(0);
for (i=this.nFaces.length-1;i>=0;i--) {
this.nFaces[i].shape.style.display="none";
this.nFaces[i].shape=null;
this.nFaces[i]=null;
this.nFaces.pop(i);
}
while (this.nFaces.length>0) this.nFaces.pop(0);
}
function NGON_addFace(obj) {
this.nFaces[this.nFaces.length]=obj;
}
function NGON_addPoint(obj) {
this.nPoints[this.nPoints.length]=obj;
}
function showFace(objFace,strPath) {
objFace.shape.style.display="inline";
objFace.shape.style.width=winX;
objFace.shape.style.height=winY;
objFace.shape.coordsize=winX+","+winY;
objFace.shape.path.v=strPath;
}
function NGON_drawFaces () {
var strPath="";
var dprod=0;
for (i=0;i<this.nFaces.length;i++) {
var faceObj=this.nFaces[i];
strPath="m";
strPath+=parseInt(faceObj.p1.scrX)+","+parseInt(faceObj.p1.scrY-iLineYDiff);
strPath+=" l ";
strPath+=parseInt(faceObj.p2.scrX)+","+parseInt(faceObj.p2.scrY-iLineYDiff)+" ";
strPath+=parseInt(faceObj.p3.scrX)+","+parseInt(faceObj.p3.scrY-iLineYDiff);
strPath+="xe";
dprod=faceNormal(faceObj);
if (dprod>0) {
showFace(faceObj,strPath);
faceObj.shape.style.zIndex=faceObj.p1.z+faceObj.p2.z+faceObj.p3.z;
}
else faceObj.shape.style.display="none";
}
}
function faceNormal(faceObj) {
var point1,point2,point3,point4;
point1=faceObj.p1;
point2=faceObj.p2;
point3=faceObj.p2;
point4=faceObj.p3;
var sx1=point1.cartX-point2.cartX;
var sy1=point1.cartY-point2.cartY;
var sz1=point1.z-point2.z;
var sx2=point3.cartX-point4.cartX;
var sy2=point3.cartY-point4.cartY;
var sz2=point3.z-point4.z;
var dpx = sy1 * sz2 - sy2 * sz1;
var dpy = sx1 * sz2 - sx2 * sz1;
var dpz = sx1 * sy2 - sx2 * sy1;
var dprod = 0 * dpx + 0 * dpy + iFov*(dpz/iDistance);
return dprod;
}
function NGON_rotate(xRotate,yRotate,zRotate) {
var xRotateAngle=(-yRotate)*(iRotationAngle*Math.PI)/180;
var yRotateAngle=(-xRotate)*(iRotationAngle*Math.PI)/180;
var zRotateAngle=(-zRotate)*(iRotationAngle*Math.PI)/180;
for (i=0;i<this.nPoints.length;i++) {
var pointObj=this.nPoints[i];
var x=pointObj.x-this.centerX;
var y=this.centerY-pointObj.y;
var z=pointObj.z;
var newy=y;
var newx=x;
var newz=z;
newy=(y*Math.cos(xRotateAngle))-(z*Math.sin(xRotateAngle)); // X axis
newz=(z*Math.cos(xRotateAngle))+(y*Math.sin(xRotateAngle));
y=newy;
z=newz;
newz=(z*Math.cos(yRotateAngle))-(x*Math.sin(yRotateAngle)); // Y axis
newx=(x*Math.cos(yRotateAngle))+(z*Math.sin(yRotateAngle));
z=newz;
x=newx;
newx=(x*Math.cos(zRotateAngle))-(y*Math.sin(zRotateAngle)); // Z axis
newy=(y*Math.cos(zRotateAngle))+(x*Math.sin(zRotateAngle));
x=newx;
y=newy;
// PARALLEL PROJECTION //
pointObj.x=newx+this.centerX;
pointObj.y=this.centerY-newy;
// PERSPECTIVE PROJECTION //
iDistance+=iZoom;
if (!this.parallel) {
pointObj.scrX = parseInt(((iFov*newx) / (iDistance-(newz+this.centerZ))) + (this.centerX));
pointObj.scrY = parseInt((this.centerY) - ((iFov*newy) / (iDistance-(newz+this.centerZ))));
pointObj.cartX=pointObj.scrX-this.centerX;
pointObj.cartY=pointObj.scrY-this.centerX;
pointObj.cartY=-newy;
}
else {
pointObj.scrX=pointObj.x;
pointObj.scrY=pointObj.y;
pointObj.cartX=newx;
pointObj.cartY=-newy;
}
pointObj.z=newz;
// pointObj.redraw();
}
this.drawFaces();
}
function MoveHandler(e) {
moveX = window.event.x + document.body.scrollLeft;
moveY = window.event.y;
if (moveX<oldMoveX) xRotate=1;
else if(moveX>oldMoveX) xRotate=-1;
else xRotate=0;
if (moveY<oldMoveY) yRotate=1;
else if(moveY>oldMoveY) yRotate=-1;
else yRotate=0;
oldMoveX=moveX;
oldMoveY=moveY;
if (bRightMouseDown && (xRotate!=0 || yRotate!=0)) {
nGon.rotate(xRotate,yRotate,0);
}
else if (nGon) {
for (i=1;i<nGon.nPoints.length;i++) {
var objPoint=nGon.nPoints[i];
if (moveX>objPoint.scrX-10 &&
moveX<objPoint.scrX+10 &&
moveY>objPoint.scrY-10 &&
moveY<objPoint.scrY+10 &&
bLeftMouseDown) {
objPoint.x=moveX;
objPoint.y=moveY;
objPoint.scrX=moveX;
objPoint.scrY=moveY;
objPoint.redraw();
}
}
}
}
function KeyHandler(e) {
switch(window.event.keyCode) {
case 51:
nGon.clear();
nGon.init();
break;
case 16: // strafe left;
nGon.centerX-=10;
nGon.rotate(0,0,0);
break;
case 67: // strafe right;
nGon.centerX+=10;
nGon.rotate(0,0,0);
break;
case 80: // toggle parallel
nGon.parallel=!nGon.parallel;
projection.innerHTML=(nGon.parallel)?"parallel projection":"perspective projection";
nGon.rotate(0,0,0);
break;
case 37:
nGon.rotate(1,0,0);
break;
case 38:
nGon.rotate(0,1,0);
break;
case 39:
nGon.rotate(-1,0,0);
break;
case 40:
nGon.rotate(0,-1,0);
break;
case 83:
iZoom=-1;
nGon.rotate(0,0,0);
iZoom=0;
break;
case 90:
iZoom=1;
nGon.rotate(0,0,0);
iZoom=0;
break;
default:
break;
}
}
function PressHandler(e) {
mousegrabber.setCapture(true);
if (window.event.button==1) {
bLeftMouseDown=true;
clearTimeout(iTimeoutId);
}
else if (window.event.button==2) {bRightMouseDown=true;}
return false;
}
function UpHandler(e) {
mousegrabber.releaseCapture();
bLeftMouseDown=false;
bRightMouseDown=false;
return false;
}
window.onresize = getWinSize;
document.onmouseup=UpHandler;
document.onkeydown = KeyHandler;
document.onmousemove = MoveHandler;
document.onmousedown = PressHandler;
document.oncontextmenu=new Function("return false;");
</script>
<LINK rel="stylesheet" type="text/css" href="style.css">
</head>
<body onload="drawPolygon();">
<div onmouseup="this.releaseCapture();" id="mousegrabber" style="border:none;font-family:arial;">
<table width=100% cellpadding=0 cellspacing=0>
<tr>
<td>3D Engine - Faces</td>
<td align=right><font style="font-size:12px;">
<script src="nav.js"></script>
</td>
</tr>
</table>
<table><tr>
<td><font style="font-size:10px;"><b>left-click</b> </td><td><font style="font-size:10px;">- stop rotation </td></tr><tr>
<td><font style="font-size:10px;"><b>right-click & drag</b> </td><td><font style="font-size:10px;">- manual rotate</td></tr>
<td><font style="font-size:10px;"><b>s</b> </td><td><font style="font-size:10px;">- zoom in</td></tr>
<td><font style="font-size:10px;"><b>z</b> </td><td><font style="font-size:10px;">- zoom out</td></tr>
<td><font style="font-size:10px;"><b>p</b> </td><td><font style="font-size:10px;">- toggle parallel / perspective projection</td></tr>
<td colspan=2><font style="font-size:10px;">*note: window resize shifts center and may cause funky orbit</td></tr>
</tr>
</td>
</table>
<br>
<div id="projection" style="font-size:10px;font-weight:bold;color:#990000;">perspective projection</div>
<font style="font-size:10px;">
</font>
</div>
</body>