forked from: レンズフレア
レンズフレアのテスト(修正版)
*
* レンズフレアエフェクトは複数のリングを画面の中心点を通る直線上に並べています。
* 太陽の2D画面上での位置はシーン内のダミーのDisplayObject3Dのscreenプロパティでわかります。
* 太陽が遮蔽物に隠れたかどうかの判定は
* BitmapViewport3D.bitmapDataの太陽座標のピクセルのアルファ値の割合で判別しています。
* (※空用のポリゴンモデルを同じシーン内に配置してしまうとこの方法が使えなくなる)
*
* (変更点)
* ・カメラをドラッグで動かせるモードをつけました
* ・遮蔽物に半分隠れた太陽の強さを変化させる処理にぼかしフィルタを使わないで
* 太陽の周辺ピクセルを調べてアルファの平均値を求める方法に変更しました
* ・地面のPlaneは削除して地平線の高さに合わせて背景をスライドさせるようにしました
* ・コードを少しだけ整理
/**
* Copyright hgsk ( http://wonderfl.net/user/hgsk )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/7QOg
*/
// forked from tencho's レンズフレア
/**
* レンズフレアのテスト(修正版)
*
* レンズフレアエフェクトは複数のリングを画面の中心点を通る直線上に並べています。
* 太陽の2D画面上での位置はシーン内のダミーのDisplayObject3Dのscreenプロパティでわかります。
* 太陽が遮蔽物に隠れたかどうかの判定は
* BitmapViewport3D.bitmapDataの太陽座標のピクセルのアルファ値の割合で判別しています。
* (※空用のポリゴンモデルを同じシーン内に配置してしまうとこの方法が使えなくなる)
*
* (変更点)
* ・カメラをドラッグで動かせるモードをつけました
* ・遮蔽物に半分隠れた太陽の強さを変化させる処理にぼかしフィルタを使わないで
* 太陽の周辺ピクセルを調べてアルファの平均値を求める方法に変更しました
* ・地面のPlaneは削除して地平線の高さに合わせて背景をスライドさせるようにしました
* ・コードを少しだけ整理
*/
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.filters.GlowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import flash.utils.getTimer;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.core.geom.Lines3D;
import org.papervision3d.core.geom.renderables.Line3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.materials.special.LineMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.Papervision3D;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.BitmapViewport3D;
import org.papervision3d.view.stats.StatsView;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
public class Lensflare extends Sprite {
private var bgColor:uint = 0x4DA7FA;
private var horizonColor:uint = 0x9CC8F5;
private var PI:Number;
private var center:Point;
private var display:Rectangle;
private var sun:Sprite;
private var sunSprite:Sprite;
private var sunRing:Sprite;
private var sunStar:Sprite;
private var sunGlow:Sprite;
private var sunGlow2:Sprite;
private var sunLine:Sprite;
private var sunFlash1:Sprite;
private var sunFlash2:Sprite;
private var sky:Sprite;
private var ghosts:Array;
private var ratios:Dictionary;
private var render:BasicRenderEngine;
private var scene:Scene3D;
private var viewport:BitmapViewport3D;
private var camera:Camera3D;
private var sunDummy:DisplayObject3D;
private var cameraTarget:DisplayObject3D;
private var horizonTarget:DisplayObject3D;
private var planeColor:ColorMaterial;
private var sideColor:ColorMaterial;
private var gradientColor:BitmapMaterial;
private var cameraMode:Boolean = true;
//マウス関係
private var isDrag:Boolean = false;
private var clickPosition:Point;
private var saveRotation:Number;
private var saveAngle:Number;
private var camRotation:Number = 120;
private var camAngle:Number = 1;
//コンストラクタ
public function Lensflare() {
stage.frameRate = 60;
stage.quality = "low";
PI = Math.PI / 180;
display = new Rectangle(0, 0, 465, 465);
center = new Point(display.width / 2, display.height / 2);
//マウス操作用
clickPosition = new Point();
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMsUp);
stage.addEventListener(Event.MOUSE_LEAVE, onMsUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
//PV3D用
Papervision3D.PAPERLOGGER.unregisterLogger(Papervision3D.PAPERLOGGER.traceLogger);
render = new BasicRenderEngine();
camera = new Camera3D();
scene = new Scene3D();
viewport = new BitmapViewport3D(display.width, display.height, false, true, 0x000000);
//マテリアル生成
sideColor = new ColorMaterial(0x000000);
planeColor = new ColorMaterial(0x000000);
planeColor.doubleSided = true;
var gradientBmp:BitmapData = new BitmapData(50, 50, true, 0x00000000);
gradientBmp.draw(createGradientRect(50, 50, [0x000000, 0x000000, 0x000000], [1, 1, 0], -90));
gradientColor = new BitmapMaterial(gradientBmp);
gradientColor.doubleSided = true;
//背景イメージ
addChild(createGradientRect(display.width, display.height, [bgColor, bgColor], [1, 1]));
sky = addChild(new Sprite()) as Sprite;
var skySprite:Sprite = sky.addChild(createGradientRect(display.width, display.height, [bgColor, horizonColor], [1, 1], 90)) as Sprite;
skySprite.y = -display.height;
sky.addChild(createGradientRect(display.width, display.height, [0x000000, 0x444444], [1, 1], 90));
addChild(viewport);
addChild(new StatsView(render));
//ダミーの太陽
sunDummy = scene.addChild(new DisplayObject3D());
sunDummy.position = new Number3D(20000, 10500, -20000);
sunDummy.autoCalcScreenCoords = true;
horizonTarget = scene.addChild(new DisplayObject3D());
horizonTarget.autoCalcScreenCoords = true;
cameraTarget = scene.addChild(new DisplayObject3D());
//街モデル生成
scene.addChild(createStage());
//車配置
for (var m:int = 0; m < 8; m ++) scene.addChild(new Car(m * 60, m % 2 == 0));
//レンズフレアスプライト
sunSprite = addChild(new Sprite()) as Sprite;
sun = sunSprite.addChild(createSun()) as Sprite;
ghosts = new Array();
ratios = new Dictionary();
//リングを2つ生成
var ringSizes:Array = [60, 120];
var ringRatios:Array = [-0.3, -0.7];
for (var i:int = 0; i < ringSizes.length; i++) {
var ring:Sprite = sunSprite.addChild(createFlare(ringSizes[i])) as Sprite;
ghosts.push(ring);
ratios[ring] = ringRatios[i];
}
//オーブをいくつか生成
var orbSizes:Array = [15, 5, 10, 6, 60];
for (var n:int = 0; n < orbSizes.length; n++) {
var orb:Sprite = sunSprite.addChild(createOrb(orbSizes[n], 0xC7C677)) as Sprite;
ghosts.push(orb);
ratios[orb] = 0.7 - n * 0.4;
}
//カメラ切り替えボタン
var btn:SwitchButton = addChild(new SwitchButton()) as SwitchButton;
btn.x = 360;
btn.y = 5;
btn.refreshLabel(cameraMode);
btn.addEventListener(MouseEvent.CLICK, function():void {
cameraMode = !cameraMode;
btn.refreshLabel(cameraMode);
});
//レンダリング開始
onEnter();
addEventListener(Event.ENTER_FRAME, onEnter);
}
private function onMsDown(e:MouseEvent):void {
isDrag = true;
clickPosition = new Point(stage.mouseX, stage.mouseY);
saveAngle = camAngle;
saveRotation = camRotation;
}
private function onMsUp(...arg):void {
isDrag = false;
}
private function onMsMove(e:MouseEvent):void {
if (!isDrag || cameraMode) return;
camRotation = saveRotation - (stage.mouseX - clickPosition.x) / 10;
camAngle = saveAngle + (stage.mouseY - clickPosition.y) / 3;
camAngle = Math.max(1, Math.min(250, camAngle));
}
//毎フレーム処理
private function onEnter(...arg):void {
var time:int = getTimer() + 88400;
var cameraRotation:Number = Math.atan2(cameraTarget.z - camera.z, cameraTarget.x - camera.x);
horizonTarget.position = new Number3D(camera.x + Math.cos(cameraRotation) * 10000, 0, camera.z + Math.sin(cameraRotation) * 10000);
//カメラ移動
if(cameraMode){
cameraTarget.x = 0;
cameraTarget.z = 100;
cameraTarget.y = 100;
camera.position = new Number3D(Math.cos(PI * (time / 40)) * 350, 15, Math.cos(PI * (time / 50)) * 350);
} else {
cameraTarget.position = new Number3D(0, 100, 0);
camera.position = new Number3D(Math.cos(PI * camRotation) * 370, camAngle, Math.sin(PI * camRotation) * 370);
}
camera.lookAt(cameraTarget);
render.renderScene(scene, camera, viewport);
//地平線の高さに背景を合わせる
sky.y = horizonTarget.screen.y + center.y;
//光源とオーブの位置を更新
var sunPosition:Point = new Point(sunDummy.screen.x + center.x, sunDummy.screen.y + center.y);
var fromCenter:Point = sunPosition.subtract(center);
//太陽が遮蔽物にどのくらいの割合で隠れているかチェック
var blurArea:int = 3;
var per:Number = 0;
for (var px:int = sunPosition.x - blurArea; px <= sunPosition.x + blurArea; px++) {
for (var py:int = sunPosition.y - blurArea; py <= sunPosition.y + blurArea; py++) {
per += 1 - (viewport.bitmapData.getPixel32(px, py) >>> 24) / 255;
}
}
per /= (blurArea * 2 + 1) * (blurArea * 2 + 1);
var power:Number = (sunDummy.screen.z <= 0 )? 0 : Math.max(0, 1 - fromCenter.length / 600) * per;
if (power) {
sun.x = int(sunPosition.x);
sun.y = int(sunPosition.y);
sunRing.alpha = 0.1 * power;
sunFlash1.rotation = -time/200;
sunFlash2.rotation = time/200;
sunStar.scaleX = sunStar.scaleY = power;
sunFlash1.alpha = 0.2 * power;
sunFlash2.alpha = 0.1 * power;
sunLine.width = 500 * power*2;
sunLine.height = 15 * power*2;
sunLine.alpha = 0.2 * power;
sunGlow.scaleX = sunGlow.scaleY = power;
sunGlow2.alpha = power * 0.5 - 0.2;
for each(var orb:Sprite in ghosts) {
orb.x = int(center.x + fromCenter.x * ratios[orb]);
orb.y = int(center.y + fromCenter.y * ratios[orb]);
orb.alpha = power;
if (orb.numChildren >= 2) {
var ringRay:Sprite = orb.getChildAt(1) as Sprite;
ringRay.x = int(fromCenter.x / 5);
ringRay.y = int(fromCenter.y / 5);
}
}
sunSprite.visible = true;
} else {
sunSprite.visible = false;
}
}
//ステージモデルを生成
private function createStage():DisplayObject3D {
var d:DisplayObject3D = new DisplayObject3D();
//螺旋状の道路
var road:TriangleMesh3D = d.addChild(new TriangleMesh3D(planeColor, [], [])) as TriangleMesh3D;
var roadWidth:Number = 30;
var poleWidth:Number = 5;
var line:Lines3D = new Lines3D(new LineMaterial(0x000000, 1));
d.addChild(line);
for (var i:int = 0; i <= 720; i += 20) {
var radius:Number = 80 + i/3;
var h:Number = i/6;
var v0:Vertex3D = new Vertex3D(Math.cos(PI * i) * radius, h, Math.sin(PI * i) * radius);
var v1:Vertex3D = new Vertex3D(Math.cos(PI * i) * (radius + roadWidth), h, Math.sin(PI * i) * (radius + roadWidth));
road.geometry.vertices.push(v0, v1);
if (i) {
var pole1:Plane = d.addChild(new Plane(planeColor, poleWidth, h, 1, 1)) as Plane;
var pole2:Plane = d.addChild(new Plane(planeColor, poleWidth, h, 1, 1)) as Plane;
pole1.position = new Number3D(Math.cos(PI * i) * (radius + poleWidth/2), h/2, Math.sin(PI * i) * (radius + poleWidth/2));
pole2.position = new Number3D(Math.cos(PI * i) * (radius + roadWidth - poleWidth/2), h/2, Math.sin(PI * i) * (radius + roadWidth - poleWidth/2));
pole1.rotationY = pole2.rotationY = -i % 360;
if (i/20 % 2) {
line.addNewLine(2, pole1.x, h, pole1.z, pole1.x, h+10, pole1.z);
line.addNewLine(2, pole2.x, h, pole2.z, pole2.x, h+10, pole2.z);
line.addNewLine(2, pole1.x, h + 10, pole1.z, pole2.x, h + 10, pole2.z);
}
}
}
for (var n:int = 0; n < road.geometry.vertices.length - 2; n += 2) {
var vt0:Vertex3D = road.geometry.vertices[n];
var vt1:Vertex3D = road.geometry.vertices[n + 1];
var vt2:Vertex3D = road.geometry.vertices[n + 2];
var vt3:Vertex3D = road.geometry.vertices[n + 3];
road.geometry.faces.push(new Triangle3D(road, [vt0, vt1, vt3]));
road.geometry.faces.push(new Triangle3D(road, [vt0, vt3, vt2]));
}
road.geometry.ready = true;
road.geometry.flipFaces();
var road2:Plane = d.addChild(new Plane(gradientColor, roadWidth, 400, 1, 4)) as Plane;
road2.position = new Number3D(320+roadWidth/2, 120, 400/2);
road2.rotationX = 90;
var road3:Plane = d.addChild(new Plane(planeColor, 250, 25, 1, 1)) as Plane;
var road4:Plane = d.addChild(new Plane(planeColor, 25, 250, 1, 1)) as Plane;
road3.z = -12.5;
road4.x = -12.5;
road3.rotationX = 90;
road4.rotationX = 90;
//高層ビル
var numX:int = 5;
var numY:int = 7;
var padding:Number = 25;
var noiseMap:BitmapData = new BitmapData(numX, numY, true, 0xFFFFFFFF);
noiseMap.noise(1234, 0, 255, 7, true);
noiseMap.applyFilter(noiseMap, noiseMap.rect, new Point(), new GlowFilter(0x000000, 1, 2, 2, 2, 2, true));
for (var m:int = 0; m < numX * numY; m++) {
var px:int = m % numX;
var py:int = int(m / numY);
var per:Number = (noiseMap.getPixel(px, py) >> 16 & 0xFF) / 255;
if (per > 0.1 && px != 2 && py != 3) {
var cubeHeight:Number = per * 400;
var building:Cube = d.addChild(new Cube(new MaterialsList( { all:sideColor } ), 20, 20, cubeHeight)) as Cube;
building.position = new Number3D((px - numX / 2) * padding, cubeHeight / 2, (py - numY / 2) * padding);
}
}
return d;
}
//太陽セットを生成
private function createSun():Sprite {
var sp:Sprite = new Sprite();
sunRing = sp.addChild(createRing(100)) as Sprite;
sunFlash1 = sp.addChild(createFlash(150, 20, 0x5977AD)) as Sprite;
sunFlash2 = sp.addChild(createFlash(120, 50, 0xFFFFFF)) as Sprite;
sunStar = sp.addChild(createStar(100, 6, 0.03, 0xFFFFFF, 4)) as Sprite;
sunStar.rotation = 15;
sunLine = sp.addChild(createGlow(500, 0xFFFFFF)) as Sprite;
sunGlow = sp.addChild(createGlow(30, 0xFFFFFF)) as Sprite;
sunGlow2 = sp.addChild(createGlow(150, 0xFFDD88)) as Sprite;
sp.blendMode = BlendMode.ADD;
return sp;
}
//リングセットを生成
private function createFlare(size:Number = 100):Sprite {
var sp:Sprite = new Sprite();
var ring:Sprite = sp.addChild(createRing(size)) as Sprite;
ring.alpha = 0.5;
var ray:Sprite = new Sprite();
ray.addChild(createStar(size*2, 100, 0.2));
ray.addChild(createStar(size*2.5, 175, 0.15));
ray.addChild(createGlow(size, 0x000000));
sp.addChild(captureSprite(ray));
sp.blendMode = BlendMode.ADD;
return sp;
}
//光の放射イメージを生成
private function createFlash(size:Number = 100, count:int = 50, color:uint = 0xffffff):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.lineStyle();
var mat:Matrix = new Matrix();
mat.createGradientBox(size * 2, size * 2, 0, -size, -size);
sp.graphics.beginGradientFill("radial", [color, color], [1, 0], [0, 255], mat);
sp.graphics.drawCircle(0, 0, size);
sp.graphics.endFill();
sp.graphics.beginFill(0x000000, 1);
var random:Array = new Array();
var total:Number = 0;
for (var n:int = 0; n < count*2; n ++) {
total += Math.random() * 50 + 10;
random.push(total);
}
for (var m:int = 0; m < random.length; m ++) {
random[m] /= total / 360;
var x:Number = Math.cos(PI * random[m]) * (size*1.2);
var y:Number = Math.sin(PI * random[m]) * (size*1.2);
if (m % 2) {
sp.graphics.moveTo(x, y);
}else {
sp.graphics.lineTo(x, y);
sp.graphics.lineTo(0, 0);
}
}
sp.graphics.endFill();
sp.filters = [new BlurFilter(2, 2, 3)];
return captureSprite(sp);
}
//オーブイメージを生成
private function createOrb(size:Number = 100, color:uint = 0xFFFFFF):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.lineStyle();
var mat:Matrix = new Matrix();
mat.createGradientBox(size * 2, size * 2, 0, -size, -size);
sp.graphics.beginGradientFill("radial", [color, color], [0.2, 1], [0, 255], mat);
sp.graphics.drawCircle(0, 0, size);
sp.graphics.endFill();
sp.filters = [new GlowFilter(color, 1, 3, 3, 1, 3, false, true), new BlurFilter(2, 2, 3)];
sp.blendMode = BlendMode.ADD;
return captureSprite(sp);
}
//グローイメージを生成
private function createGlow(size:Number = 100, color:uint = 0xFFFFFF):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.lineStyle();
var mat:Matrix = new Matrix();
mat.createGradientBox(size * 2, size * 2, 0, -size, -size);
sp.graphics.beginGradientFill("radial", [color, color], [1, 0], [0, 255], mat);
sp.graphics.drawCircle(0, 0, size);
sp.graphics.endFill();
return captureSprite(sp);
}
//虹リングイメージを生成
private function createRing(size:Number = 100):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.lineStyle();
var mat:Matrix = new Matrix();
mat.createGradientBox(size * 2, size * 2, 0, -size, -size);
sp.graphics.beginGradientFill("radial", [0xFF0000, 0xFFdd00, 0x00FF00, 0x0066FF, 0x0000FF], [0, 1, 1, 0.5, 0], [180, 205, 215, 235, 255], mat);
sp.graphics.drawCircle(0, 0, size);
sp.graphics.endFill();
return captureSprite(sp);
}
//星イメージを生成
private function createStar(size:Number = 100, count:uint = 8, per:Number = 0.05, color:uint = 0x000000, blur:Number = 0):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.lineStyle();
sp.graphics.beginFill(color, 1);
var step:Number = 360 / (count * 2);
for (var i:int = 0; i < count * 2; i ++) {
var radius:Number = (i % 2)? size * per : size;
var x:Number = Math.cos(PI * i * step) * radius;
var y:Number = Math.sin(PI * i * step) * radius;
if (i == 0) sp.graphics.moveTo(x, y);
else sp.graphics.lineTo(x, y);
}
sp.graphics.endFill();
sp.filters = (blur)? [new BlurFilter(blur, blur, 3)] : [];
return captureSprite(sp);
}
//スプライトをキャプチャしてBitmap化する
private function captureSprite(target:Sprite, smooth:Boolean = false):Sprite {
var offset:int = 8;
var rect:Rectangle = target.getBounds(target);
var sp:Sprite = new Sprite();
sp.blendMode = target.blendMode;
var bd:BitmapData = new BitmapData(target.width + offset * 2, target.height + offset * 2, true, 0x00FFFFFF);
//キャプチャする瞬間だけ最高画質にする
var saveQuality:String = stage.quality;
stage.quality = "best";
bd.draw(target, new Matrix(1, 0, 0, 1, -rect.left + offset, -rect.top + offset));
for each(var filter:* in target.filters) bd.applyFilter(bd, bd.rect, new Point(), filter);
stage.quality = saveQuality;
var bmp:Bitmap = sp.addChild(new Bitmap(bd, "auto", smooth)) as Bitmap;
bmp.x = rect.left - offset;
bmp.y = rect.top - offset;
return sp;
}
//グラデーションスプライト生成
private function createGradientRect(w:Number, h:Number, colors:Array, alphas:Array, r:Number = 0):Sprite {
var ratios:Array = new Array();
for (var n:int = 0; n < colors.length; n++) ratios.push(int(255 * n / (colors.length - 1)));
var sp:Sprite = new Sprite();
var mat:Matrix = new Matrix();
mat.createGradientBox(w, h, PI * r, 0, 0);
if (colors.length == 1 && alphas.length == 1) sp.graphics.beginFill(colors[0], alphas[0]);
else sp.graphics.beginGradientFill("linear", colors, alphas, ratios, mat);
sp.graphics.drawRect(0, 0, w, h);
sp.graphics.endFill();
return sp;
}
}
}
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.utils.getTimer;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cube;
//カメラ切り替えボタン
class SwitchButton extends Sprite {
private var txt:TextField;
public function SwitchButton() {
graphics.beginFill(0x000000, 1);
graphics.drawRoundRect(0, 0, 100, 18, 5, 5);
graphics.endFill();
txt = addChild(new TextField()) as TextField;
txt.autoSize = "left";
txt.x = 2;
buttonMode = true;
mouseChildren = false;
}
public function refreshLabel(mode:Boolean):void {
txt.text = "CAMERA: " + ["DRAG", "AUTO"][int(mode)];
txt.setTextFormat(new TextFormat("Arial", 12, 0xFFFFFF));
}
}
//道路を走る車
class Car extends Cube {
private var offset:Number;
private var speed:Number;
private var line:Number;
private var sp:Sprite;
public function Car(position:Number = 0, direction:Boolean = true) {
super(new MaterialsList({all:new ColorMaterial(0x000000)}), 4, 8, 4, 1, 1, 1);
offset = position;
speed = (direction)? 0.1 : -0.1;
line = (direction)? 13 : 23;
rotationX = -4;
sp = new Sprite();
sp.addEventListener(Event.ENTER_FRAME, onEnter);
onEnter();
}
private function onEnter(...arg):void {
var rot:Number = ((getTimer() * 0.05 * speed + offset) % 720 + 720) % 720;
var radius:Number = 80 + rot/3;
position = new Number3D(Math.cos(Math.PI / 180 * rot) * (radius + line), rot / 6 + 2, Math.sin(Math.PI / 180 * rot) * (radius + line));
rotationY = -rot;
}
}