/**
* Copyright makc3d ( http://wonderfl.net/user/makc3d )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/3LEn
*/
package {
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.core.Sorting;
import alternativ7.engine3d.core.View;
import alternativ7.engine3d.materials.FillMaterial;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
/**
* Because Klein bottles are fun...
* @author makc
*/
[SWF(backgroundColor="0")]
public class AlternativaKleinBottle extends Sprite {
public var cameraLt:Camera3D;
public var cameraRt:Camera3D;
public var bottle:KleinBottleMesh;
public function AlternativaKleinBottle () {
stage.quality = "low";
cameraLt = new Camera3D;
cameraLt.view = new View (465, 465); addChild (cameraLt.view);
cameraLt.view.transform.colorTransform = new ColorTransform (1, 0, 0);
// from http://en.wikipedia.org/wiki/Anaglyph_image#Anaglyphs_containing_color_information
// "To make an anaglyph containing color information using color images, replace the red
// channel of the right-eye image with the red channel of the left-eye image... Eye sensitivity
// balance can be improved by selecting the green channel and reducing it using a linear curve
// selection (e.g. reduce to 12.5%). Select the blue channel and reduce somewhat less (e.g.
// reduce by 5%). This action compensates for the eye's lower sensitivity to red and its high
// sensitivity to green".
cameraRt = new Camera3D;
cameraRt.view = new View (465, 465); addChild (cameraRt.view);
cameraRt.view.transform.colorTransform = new ColorTransform (0, 1 - 0.125, 1 - 0.05);
cameraRt.view.blendMode = BlendMode.ADD;
cameraLt.view.hideLogo ();
cameraRt.view.hideLogo ();
cameraLt.fov = 0.6 * Math.PI;
cameraRt.fov = 0.6 * Math.PI;
Object3DContainer (new Object3DContainer).addChild (cameraLt);
cameraLt.parent.addChild (cameraRt);
var m1:FillMaterial = new FillMaterial (0xFFFFFF, 0.95);
var m2:FillMaterial = new FillMaterial (0x7F7F7F, 0.95);
bottle = new KleinBottleMesh (3);
var f:Face;
for each (f in bottle.facesEven) f.material = m1;
for each (f in bottle.facesOdd) f.material = m2;
bottle.sorting = Sorting.DYNAMIC_BSP;
cameraLt.parent.addChild (bottle);
addEventListener (Event.ENTER_FRAME, loop);
}
public var t:Number = 0;
public function loop (e:Event):void {
t += 1e-3; if (t >= 1) t -= 1;
var m:Matrix3D = interpolatePathMatrix (t, 5e-4);
m.prependRotation (-30, Vector3D.X_AXIS);
cameraLt.matrix = m;
cameraLt.render ();
m.prependTranslation (0.1, 0, 0);
cameraRt.matrix = m;
cameraRt.render ();
}
public function interpolatePathMatrix (t:Number, dt:Number):Matrix3D {
var p:Vector3D = interpolateVector3D (bottle.path, t);
var q:Vector3D = interpolateVector3D (bottle.path, t + dt);
q.decrementBy (p); q.normalize ();
var n:Vector3D = interpolateVector3D (bottle.pathNormals, t);
var m:Vector3D = q.crossProduct (n);
n = m.crossProduct (q); n.normalize ();
var c:Vector3D = q.crossProduct (n);
return new Matrix3D (Vector.<Number> ([
/* x */ c.x, c.y, c.z, 0,
/* y */ -n.x, -n.y, -n.z, 0,
/* z */ q.x, q.y, q.z, 0,
/* t */ p.x, p.y, p.z, 1
]));
}
public function interpolateVector3D (vs:Vector.<Vector3D>, t:Number, s:Number = 0.4):Vector3D {
// cardinal spline
// see http://wonderfl.net/c/vnCf/read
var i2:int = int (t * vs.length * 0.999999);
var i1:int = (i2 + vs.length - 1) % vs.length;
var i3:int = (i2 + 1) % vs.length;
var i4:int = (i2 + 2) % vs.length;
var V1:Vector3D = vs [i1];
var V2:Vector3D = vs [i2];
var V3:Vector3D = vs [i3];
var V4:Vector3D = vs [i4];
t = t * vs.length - i2;
return new Vector3D (
// x
s * ( -t * t * t + 2 * t * t - t) * V1.x +
s * ( -t * t * t + t * t) * V2.x +
(2 * t * t * t - 3 * t * t + 1) * V2.x +
s * (t * t * t - 2 * t * t + t) * V3.x +
( -2 * t * t * t + 3 * t * t) * V3.x +
s * (t * t * t - t * t) * V4.x,
// y
s * ( -t * t * t + 2 * t * t - t) * V1.y +
s * ( -t * t * t + t * t) * V2.y +
(2 * t * t * t - 3 * t * t + 1) * V2.y +
s * (t * t * t - 2 * t * t + t) * V3.y +
( -2 * t * t * t + 3 * t * t) * V3.y +
s * (t * t * t - t * t) * V4.y,
// z
s * ( -t * t * t + 2 * t * t - t) * V1.z +
s * ( -t * t * t + t * t) * V2.z +
(2 * t * t * t - 3 * t * t + 1) * V2.z +
s * (t * t * t - 2 * t * t + t) * V3.z +
( -2 * t * t * t + 3 * t * t) * V3.z +
s * (t * t * t - t * t) * V4.z
);
}
}
}
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.objects.Mesh;
import flash.geom.Vector3D;
class KleinBottleMesh extends Mesh {
public var facesEven:Vector.<Face> = new Vector.<Face>;
public var facesOdd:Vector.<Face> = new Vector.<Face>;
public var path:Vector.<Vector3D> = new Vector.<Vector3D>;
public var pathNormals:Vector.<Vector3D> = new Vector.<Vector3D>;
public function KleinBottleMesh (quality:uint = 1) {
var vs:Array = [];
var q:int = Math.max (1, 2 * quality);
var i_max:int = Math.max (4, 8 * int (q / 2) + 1);
var j_max:int = Math.max (3, 2 * q + 1);
// this code is from http://wonderfl.net/c/6RzY/read
// it could use some love too, but I don't feel like
var pi:Number = Math.PI, pi2:Number = pi*2;
var u:Number, v:Number, r:Number, vx:Number, vy:Number, vz:Number;
for (var i:int = 0; i <= i_max; i++) {
vs [i] = [];
for (var j:int = 0; j <= j_max; j++) {
u = pi2 * i / (i_max + 1); v = pi2 * j / (j_max + 1);
r = 5*(1 - Math.cos(u)/2);
if(u <= pi) {
vx = (6*Math.cos(u)*(1 + Math.sin(u)) + r*Math.cos(u)*Math.cos(v));
vy = (16*Math.sin(u) + r*Math.sin(u)*Math.cos(v));
}else if(u > pi) {
vx = (6*Math.cos(u)*(1 + Math.sin(u)) + r*Math.cos(v + Math.PI));
vy = (16*Math.sin(u));
}
vz = r*Math.sin(v);
u = i / i_max;
v = j / j_max;
vs [i][j] = addVertex (vx, vy, vz, u, v);
}
}
// now make faces
var fp:Array = [];
for (i = 0; i <= i_max; i++) {
var i1:int = (i + 1) % (i_max + 1);
for (j = 0; j <= j_max; j++) {
var j1:int = (j + 1) % (j_max + 1);
if (i < i1) {
var j0:int = j;
var j2:int = j1;
} else {
j0 = (2 * j_max - j - (q - 1)) % (j_max + 1);
j2 = (j0 + j_max) % (j_max + 1);
}
var fa:Vector.<Face> = ((i + j) % 2 == 0) ? facesEven : facesOdd;
// can't just add quad face here because it wouldn't be flat
fa.push (addTriFace (vs[i][j], vs[i][j1], vs[i1][j0]));
fa.push (addTriFace (vs[i1][j0], vs[i][j1], vs[i1][j2]));
// ironically, one-sided surface has to be double-sided mesh
fa.push (addTriFace (vs[i1][j0], vs[i][j1], vs[i][j]));
fa.push (addTriFace (vs[i1][j2], vs[i][j1], vs[i1][j0]));
if (j == (j_max - q) >> 1) {
// save some data to build the path through bottle
fp.push (fa [fa.length - 1]);
}
}
}
calculateNormals ();
// build path
path.length = fp.length * 2;
pathNormals.length = path.length;
for (j = 0; j < 2; j++) {
for (i = 0; i < fp.length; i++) {
var f:Face = Face (fp[i]);
var n:Vector3D = f.normal.clone ();
if (j > 0) n.negate ();
var k:int = j * fp.length + i;
path [k] = new Vector3D (
(f.vertices [0].x + f.vertices [1].x + f.vertices [2].x) / 3,
(f.vertices [0].y + f.vertices [1].y + f.vertices [2].y) / 3,
(f.vertices [0].z + f.vertices [1].z + f.vertices [2].z) / 3
);
path [k].incrementBy (n);
path [k].incrementBy (n);
pathNormals [k] = n;
}
}
k = 0;
}
}