3D VOCALOID RUNS!
ミクさん、リンちゃん、レンきゅんが走ります。3人のうち誰になるかはランダムで決まります。
[↑]: 前進
[←][→]: 旋回
[A][D]: カメラ回転
[W][S]: カメラ高さ調節
3d model: mqdlさん ( http://mqdl.jpn.org/ )
※もし動かない場合は一度flashエリア内をクリックしてみてください
/**
* Copyright daniwell ( http://wonderfl.net/user/daniwell )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/tkxl
*/
package
{
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Resource;
import alternativa.engine3d.core.View;
import alternativa.engine3d.lights.AmbientLight;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.primitives.Plane;
import alternativa.engine3d.resources.BitmapTextureResource;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display3D.Context3DRenderMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
public class ModelRun extends Sprite
{
// MMD でモーション付けて、Blender のプラグインで読み込んで COLLADA 書き出しするだけ
// 但しモーションに IK は使えない
private const PATH_DAEIDLE :Array = ["http://aidn.jp/_wonderfl/vr/miku_idle.dae", "http://aidn.jp/_wonderfl/vr/rin_idle.dae", "http://aidn.jp/_wonderfl/vr/len_idle.dae"];
private const PATH_DAERUN :Array = ["http://aidn.jp/_wonderfl/vr/miku_run.dae", "http://aidn.jp/_wonderfl/vr/rin_run.dae", "http://aidn.jp/_wonderfl/vr/len_run.dae"];
private const PATH_TEXTURE :Array = ["http://aidn.jp/_wonderfl/vr/miku_texture.png", "http://aidn.jp/_wonderfl/vr/rin_texture.png", "http://aidn.jp/_wonderfl/vr/len_texture.png"];
private const CAMERA_DISTANCE :Number = 600;
private const BOX_NUMS :int = 30;
private var _texture :BitmapData;
private var _daeIdle :XML;
private var _daeRun :XML;
private var _n :int = int(Math.random() * 3);
private var _stage3D :Stage3D;
private var _camera :Camera3D;
private var _scene :Object3D;
private var _model :ColladaModel;
private var _shadow :Plane;
private var _boxes :/*CustomBox*/Array = [];
private var _keyFlags :/*Boolean*/Array = [];
private var _t :Number = 0;
private var _rot :Number = 0;
private var _rotTmp :Number = -90;
private var _cameraZ :Number = 50;
private var _tfLoading :TextField;
public function ModelRun () {
stage.scaleMode = "noScale";
stage.align = "TL";
// Now Loading
_tfLoading = addChild(new TextField()) as TextField;
_tfLoading.defaultTextFormat = new TextFormat("_sans", 42, 0x999999, true);
_tfLoading.autoSize = "left"; _tfLoading.text = "NOW LOADING...";
stage.addEventListener(Event.ENTER_FRAME, _enterFrame);
// Stage3D
_stage3D = stage.stage3Ds[0];
_stage3D.addEventListener(Event.CONTEXT3D_CREATE, _contextCreate);
_stage3D.requestContext3D(Context3DRenderMode.AUTO);
}
private function _contextCreate ( evt :Event ) :void {
_stage3D.removeEventListener(Event.CONTEXT3D_CREATE, _contextCreate);
// IDLE 時データ読み込み
var ul :URLLoader = new URLLoader(); ul.addEventListener(Event.COMPLETE, _loadCompleteIdle); ul.load(new URLRequest(PATH_DAEIDLE[_n]));
}
private function _loadCompleteIdle ( evt :Event ) :void {
_daeIdle = XML(URLLoader(evt.target).data);
// RUN 時データ読み込み
var ul :URLLoader = new URLLoader(); ul.addEventListener(Event.COMPLETE, _loadCompleteRun); ul.load(new URLRequest(PATH_DAERUN[_n]));
}
private function _loadCompleteRun ( evt :Event ) :void {
_daeRun = XML(URLLoader(evt.target).data);
var l :Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, _loadCompleteTexture);
l.load(new URLRequest(PATH_TEXTURE[_n]), new LoaderContext(true));
}
private function _loadCompleteTexture ( evt :Event ) :void {
_tfLoading.visible = false;
_texture = Bitmap(evt.target.content).bitmapData;
_scene = new Object3D();
_camera = new Camera3D(1, 10000);
_camera.view = new View(stage.stageWidth, stage.stageHeight);
_camera.view.hideLogo();
addChild(_camera.view);
//// addChild(_camera.diagram);
_camera.rotationX = MathUtil.toRad(-90);
_camera.x = 0; _camera.y = 0; _camera.z = 250;
_camera.fov = 1.8;
_scene.addChild(_camera);
// Box
for (var i :int = 0; i < BOX_NUMS; i ++) _boxes.push(new CustomBox(_scene));
// Model
_model = new ColladaModel(_stage3D, _scene);
_model.init(_daeIdle, _texture, 0, 0, -300, 100);
_model.addAnimation(_daeIdle, 0);
_model.addAnimation(_daeRun, 1);
// Shadow
var bmd :BitmapData = new BitmapData(128, 128, false, 0x0);
var sp :Shape = new Shape(); sp.graphics.beginFill(0xffffff); sp.graphics.drawCircle(64, 64, 64); sp.graphics.endFill();
bmd.draw(sp);
var material :TextureMaterial = new TextureMaterial(new BitmapTextureResource(new BitmapData(128, 128, false, 0x333333)), new BitmapTextureResource(bmd));
material.alphaThreshold = 1;
_shadow = new Plane(100, 100);
_shadow.setMaterialToAllSurfaces(material);
_shadow.z = _model.z;
_shadow.y = _model.y;
_scene.addChild(_shadow);
// Light
var aLight :AmbientLight = new AmbientLight(0xffffff);
aLight.intensity = 0.5;
_scene.addChild(aLight);
var oLight :OmniLight = new OmniLight(0xffffff, 1000, 10000);
oLight.x = 100; oLight.y = 200; oLight.z = 200;
_scene.addChild(oLight);
var dLight :DirectionalLight = new DirectionalLight(0xffffff);
dLight.x = 0; dLight.y = - 100; dLight.z = 100; dLight.intensity = 0.5;
_scene.addChild(dLight);
// Resource
for each (var resource :Resource in _scene.getResources(true)) resource.upload(_stage3D.context3D);
// Event
stage.addEventListener(KeyboardEvent.KEY_DOWN, _keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, _keyUp);
stage.addEventListener(Event.RESIZE, _resize);
_resize();
}
/* RESIZE */
private function _resize ( evt :Event = null ) :void {
_camera.view.width = stage.stageWidth;
_camera.view.height = stage.stageHeight;
}
/* KEY DOWN & KEY UP */
private function _keyDown ( evt :KeyboardEvent ) :void { _keyFlags[evt.keyCode] = true; if (evt.keyCode == Keyboard.UP) _model.changeAnimation(1, 1.8); }
private function _keyUp ( evt :KeyboardEvent ) :void { _keyFlags[evt.keyCode] = false; if (evt.keyCode == Keyboard.UP) _model.changeAnimation(0, 1.4); }
/* ENTER FRAME */
private function _enterFrame ( evt :Event ) :void {
// loading
if (! _scene) {
_tfLoading.visible = ! _tfLoading.visible;
return;
}
if (_keyFlags[Keyboard.LEFT]) _model.rotationZ += MathUtil.toRad(5);
if (_keyFlags[Keyboard.RIGHT]) _model.rotationZ -= MathUtil.toRad(5);
if (_keyFlags[Keyboard.UP]) {
_model.moveFront(16);
var p :Point, n :int = 0;
var l :int = _boxes.length;
for (var i :int = 0; i < l; i ++) {
p = _boxes[i].hitTest(_model.x, _model.y, _model.prePosition.x, _model.prePosition.y);
if (p) {
_model.x = p.x;
_model.y = p.y;
i = -1; n ++; if (10 < n) break;
}
}
_shadow.x = _model.x;
_shadow.y = _model.y;
}
_model.updataAnimation();
// カメラの回転
if (_keyFlags[Keyboard.A]) _rotTmp += 8;
else if (_keyFlags[Keyboard.D]) _rotTmp -= 8;
else if (_keyFlags[Keyboard.W]) _cameraZ += 20;
else if (_keyFlags[Keyboard.S]) _cameraZ -= 20;
_updateCameraPosition();
_camera.render(_stage3D);
}
private function _updateCameraPosition ( ) :void {
_rot += (_rotTmp - _rot) / 6;
var px :Number = _model.x + CAMERA_DISTANCE * Math.cos(MathUtil.toRad(_rot));
var py :Number = _model.y + CAMERA_DISTANCE * Math.sin(MathUtil.toRad(_rot));
_camera.x += (px - _camera.x) / 6;
_camera.y += (py - _camera.y) / 6;
_camera.z += (_cameraZ - _camera.z) / 6;
_camera.x += Math.sin(_t / 210) * 11;
_camera.y += Math.sin(_t / 300) * 9;
_t += 11;
_camera.lookAt(_model.x, _model.y, _model.z);
}
}
}
import alternativa.engine3d.animation.AnimationClip;
import alternativa.engine3d.animation.AnimationController;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.loaders.ParserCollada;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.VertexLightTextureMaterial;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.primitives.Box;
import alternativa.engine3d.resources.BitmapTextureResource;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Stage3D;
import flash.geom.Point;
import mx.core.BitmapAsset;
/* -----------------------------------------------------------------
* ColladaModel
*/
class ColladaModel extends Object3D
{
private var _stage3D :Stage3D;
private var _material :TextureMaterial;
private var _animationController :AnimationController;
private var _animationClips :/*AnimationClip*/Array = [];
private var _textureResources :/*BitmapTextureResource*/Array = [];
private var _pre :Point;
public function ColladaModel ( stage3D :Stage3D, parent :Object3D = null ) {
_stage3D = stage3D;
if (parent) parent.addChild(this);
}
public function init ( dae :*, texture :*, x :Number = 0, y :Number = 0, z :Number = 0, scale :Number = 1 ) :void {
if (! (dae is XML)) dae = new XML(dae);
if (texture is Bitmap || texture is BitmapAsset) texture = texture.bitmapData;
this.x = x; this.y = y; this.z = z;
scaleX = scaleY = scaleZ = scale;
_pre = new Point(x, y);
// Parser
var parser :ParserCollada = new ParserCollada();
parser.parse(dae);
// Texture
var texResource :BitmapTextureResource = new BitmapTextureResource(texture);
texResource.upload(_stage3D.context3D);
var txtRes :BitmapTextureResource = texResource;
_material = new TextureMaterial(txtRes);
// Mesh
for each (var obj :Object3D in parser.objects) {
var mesh :Mesh = obj as Mesh;
if (mesh) { mesh.setMaterialToAllSurfaces(_material); addChild(obj); }
}
}
/* 前進 */
public function moveFront ( d :Number ) :void {
_pre.x = this.x;
_pre.y = this.y;
var radZ :Number = this.rotationZ;
this.x += d * Math.sin(radZ);
this.y -= d * Math.cos(radZ);
}
/* アニメーションのアップデート */
public function updataAnimation ( ) :void { _animationController.update(); }
/* アニメーションを追加 */
public function addAnimation ( dae :*, id :int ) :void {
if (! (dae is XML)) dae = new XML(dae);
// Animation Clip
var animationClip :AnimationClip = ParserCollada.parseAnimation(dae);
animationClip.attach(this, true);
_animationClips[id] = animationClip;
// Animation Controller
if (! _animationController) {
_animationController = new AnimationController();
_animationController.root = animationClip;
}
}
/* アニメーションの切り替え */
public function changeAnimation ( id :int, speed :Number = 1.0, loop :Boolean = true ) :void {
_animationClips[id].speed = speed;
_animationClips[id].loop = loop;
_animationController.root = _animationClips[id];
}
public function get prePosition ( ) :Point { return _pre; }
}
/* -----------------------------------------------------------------
* Box
*/
class CustomBox extends Box
{
private var _p :/*Point*/Array = [];
public function CustomBox ( parent :Object3D = null ) {
var w :int = MathUtil.rand(100, 500);
var l :int = MathUtil.rand(100, 500);
var h :int = MathUtil.rand(100, 500);
super(w, l, h);
const RANGE :int = 3000;
if (Math.random() < 0.5) {
x = MathUtil.rand(-RANGE, RANGE);
y = (Math.random() < 0.5) ? MathUtil.rand(-RANGE, -l/2) : MathUtil.rand(l/2, RANGE);
} else {
x = (Math.random() < 0.5) ? MathUtil.rand(-RANGE, -w/2) : MathUtil.rand(w/2, RANGE);
y = MathUtil.rand(-RANGE, RANGE);
}
z = - 300 + h /2;
setMaterialToAllSurfaces(new VertexLightTextureMaterial(new BitmapTextureResource(new BitmapData(8, 8, false, 0xffffff))));
if (parent) parent.addChild(this);
var rad :Number = rotationZ = MathUtil.toRad(MathUtil.rand(0, 45));
var cw :Number = (w + 90) / 2;
var cl :Number = (l + 90) / 2;
_p[0] = new Point(x + (-cw * Math.cos(rad) + cl * Math.sin(rad)), y + (-cw * Math.sin(rad) - cl * Math.cos(rad)));
_p[1] = new Point(x + (-cw * Math.cos(rad) - cl * Math.sin(rad)), y + (-cw * Math.sin(rad) + cl * Math.cos(rad)));
_p[2] = new Point(x + ( cw * Math.cos(rad) - cl * Math.sin(rad)), y + ( cw * Math.sin(rad) + cl * Math.cos(rad)));
_p[3] = new Point(x + ( cw * Math.cos(rad) + cl * Math.sin(rad)), y + ( cw * Math.sin(rad) - cl * Math.cos(rad)));
/*
// Show HitArea
var zz :Number = z - h / 2;
var vec :Vector.<Vector3D> = new <Vector3D>[
new Vector3D(_p[0].x, _p[0].y, zz),
new Vector3D(_p[1].x, _p[1].y, zz),
new Vector3D(_p[2].x, _p[2].y, zz),
new Vector3D(_p[3].x, _p[3].y, zz),
new Vector3D(_p[0].x, _p[0].y, zz)
];
var wire :WireFrame = WireFrame.createLineStrip(vec, 0x999999, 1, 2);
parent.addChild(wire);
//*/
}
public function hitTest (nx :Number, ny :Number, px :Number, py :Number) :Point {
var f :Boolean = MathUtil.boxContainsPos(_p[0].x, _p[0].y, _p[1].x, _p[1].y, _p[2].x, _p[2].y, _p[3].x, _p[3].y, nx, ny);
if (! f) return null;
for (var i :int = 0; i < 4; i ++) {
var p1x :Number = _p[i].x;
var p1y :Number = _p[i].y;
var p2x :Number = _p[(i+1)%4].x;
var p2y :Number = _p[(i+1)%4].y;
f = MathUtil.intersectLineSegment(px, py, nx, ny, p1x, p1y, p2x, p2y);
if (f) {
var pt :Point = MathUtil.getCrossPoint(p1x, p1y, p2x, p2y, nx, ny);
return new Point(pt.x + (pt.x - nx) * 0.2, pt.y + (pt.y - ny) * 0.2);
}
}
return null;
}
}
/* -----------------------------------------------------------------
* MathUtil
*/
class MathUtil
{
/* min ~ max の範囲で乱数生成 */
public static function rand ( min :Number, max :Number ) :Number { return Math.random() * ( max - min ) + min; }
/* 角度からラジアンに変換 */
public static function toRad ( deg :Number ) :Number { return deg * Math.PI / 180; }
/* 外積 */
public static function intersect ( p1x :Number, p1y :Number, p2x :Number, p2y :Number, p3x :Number, p3y :Number, p4x :Number, p4y :Number ) :Number {
var n :Number = ((p1x - p2x) * (p3y - p1y) + (p1y - p2y) * (p1x - p3x)) * ((p1x - p2x) * (p4y - p1y) + (p1y - p2y) * (p1x - p4x));
return n;
}
/* 任意の三角形の中に任意の点が内包されているかどうか */
public static function triContainsPos ( p1x :Number, p1y :Number, p2x :Number, p2y :Number, p3x :Number, p3y :Number, psx :Number, psy :Number ) :Boolean {
if ((p1x - p3x) * (p1y - p2y) == (p1x - p2x) * (p1y - p3y)) return false;
if (intersect(p1x, p1y, p2x, p2y, psx, psy, p3x, p3y) < 0) return false;
if (intersect(p1x, p1y, p3x, p3y, psx, psy, p2x, p2y) < 0) return false;
if (intersect(p2x, p2y, p3x, p3y, psx, psy, p1x, p1y) < 0) return false;
return true;
}
/* 任意の四角形の中に任意の点が内包されているかどうか(凸のみ) */
public static function boxContainsPos ( p1x :Number, p1y :Number, p2x :Number, p2y :Number, p3x :Number, p3y :Number, p4x :Number, p4y :Number, psx :Number, psy :Number ) :Boolean {
if (triContainsPos(p1x, p1y, p2x, p2y, p3x, p3y, psx, psy)) return true;
if (triContainsPos(p1x, p1y, p3x, p3y, p4x, p4y, psx, psy)) return true;
return false;
}
/* 線分の交差判定 */
public static function intersectLineSegment ( p1x :Number, p1y :Number, p2x :Number, p2y :Number, p3x :Number, p3y :Number, p4x :Number, p4y :Number) :Boolean {
if (intersect(p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y) < 0)
if (intersect(p3x, p3y, p4x, p4y, p1x, p1y, p2x, p2y) < 0) return true;
return false;
}
/* 点から直線におろした垂線の交点の座標 */
public static function getCrossPoint ( p1x :Number, p1y :Number, p2x :Number, p2y :Number, psx :Number, psy :Number ) :Point {
var a :Number = p2y - p1y;
var b :Number = p1x - p2x;
var c :Number = (p1y - p2y) * p1x + (p2x - p1x) * p1y;
var d :Number = (a * psx + b * psy + c) / (a * a + b * b);
var x :Number = psx - d * a;
var y :Number = psy - d * b;
return new Point(x, y);
}
}