package com.alternativagame.engine3d.object { import com.alternativagame.engine3d.Math3D; import com.alternativagame.engine3d.Matrix3D; import com.alternativagame.engine3d.View3D; import com.alternativagame.engine3d.engine3d; import com.alternativagame.engine3d.object.light.Light3D; import com.alternativagame.type.Set; import com.alternativagame.type.Vector; import flash.events.EventDispatcher; import flash.geom.Matrix; import flash.utils.Dictionary; use namespace engine3d; public class Object3D extends EventDispatcher { use namespace engine3d; // Название private var _name:String; // Инкремент количества объектов private static var num:uint = 0; // Флаг сплошного объекта private var _solid:Boolean = false; // Все объекты внутри солида engine3d var solidObjects:Set; // Все источники света внутри солида engine3d var solidLights:Set; // Вершина солида engine3d var solidParent:Object3D; // Ссылка на родителя engine3d var parent:Object3D = null; // Ссылка на камеру engine3d var view:View3D = null; // Флаг интерактивности private var _interactive:Boolean = false; // Смещение объекта относительно родителя private var _x:Number = 0; private var _y:Number = 0; private var _z:Number = 0; // Поворот объекта относительно родителя private var _rotX:Number = 0; private var _rotY:Number = 0; private var _rotZ:Number = 0; // Мастшаб объекта относительно родителя private var _scaleX:Number = 1; private var _scaleY:Number = 1; private var _scaleZ:Number = 1; // Списки дочерних объектов private var _objects:Set; // Глобальная трансформация engine3d var transform:Matrix3D; // Изменилась позиция объекта engine3d var positionChanged:Boolean = true; // Изменилась геометрия объекта (поворот, масштаб, параметры) engine3d var geometryChanged:Boolean = true; // Изменилось освещение объекта engine3d var lightChanged:Set = new Set(); public function Object3D() { _objects = new Set(); transform = new Matrix3D(); solidObjects = new Set(); solidLights = new Set(); solidParent = this; solidObjects.add(this); num++; _name = "Object" + num; } // Пересчитать трансформацию объекта и его детей engine3d function calculateTransform():void { if (geometryChanged || positionChanged) { // Если позиция или геометрия изменилась var topTransform:Matrix3D = (this === view.object) ? view.transformation : parent.transform; // Если изменилась геометрия - Пересчитать трансформацию if (geometryChanged) { transform = Math3D.combineMatrix(topTransform, new Matrix3D(x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ)); // Если изменилась только позиция - Скорректировать трансформацию } else { transform.d = topTransform.a*x + topTransform.b*y + topTransform.c*z + topTransform.d; transform.h = topTransform.e*x + topTransform.f*y + topTransform.g*z + topTransform.h; transform.l = topTransform.i*x + topTransform.j*y + topTransform.k*z + topTransform.l; } // Обновить скины updateTransform(); // Сбрасываем флаги изменений positionChanged = false; geometryChanged = false; } // Расчитать трансформацию у дочерних объектов for each (var object:Object3D in objects) { object.calculateTransform(); } } // Пересчитать освещение объекта и его детей engine3d function calculateLight():void { // Обновить освещение updateLight(); // Удалить источники света clearLightChanged(); // Расчитать освещение у дочерних объектов for each (var object:Object3D in objects) { object.calculateLight(); } } // Обновиться после трансформации protected function updateTransform():void {} // Обновиться после освещения protected function updateLight():void {} // Добавить дочерний объект public function attach(object:Object3D):void { // Если объект был в другом объекте if (object.parent != null) { // Удалить его оттуда object.parent.detach(object); } // Добавляем в список objects.add(object); // Указываем себя как родителя object.setParent(this); // Указываем камеру object.setView(view); } // Удалить дочерний объект public function detach(object:Object3D):void { // Проверяем, есть ли у нас этот объект if (objects.has(object)) { // Убираем из списка objects.remove(object); // Удаляем ссылку на родителя object.setParent(null); // Удаляем ссылку на камеру object.setView(null); } } // Получить дочерний объект по имени public function getObjectByName(name:String):Object3D { var res:Object3D = null; for each (var object:Object3D in objects) { if (object.name == name) { res = object; break; } } return res; } // Проверить освещение protected function updateLightChanged():void { // Собрать все источники внутри солида for each (var light:Light3D in solidParent.solidLights) { addLightChanged(light); } } public function set name(value:String):void { _name = value; } public function get name():String { return _name; } public function get x():Number { return _x; } public function get y():Number { return _y; } public function get z():Number { return _z; } public function get rotX():Number { return _rotX; } public function get rotY():Number { return _rotY; } public function get rotZ():Number { return _rotZ; } public function get scaleX():Number { return _scaleX; } public function get scaleY():Number { return _scaleY; } public function get scaleZ():Number { return _scaleZ; } public function set x(value:Number):void { _x = value; setPositionChanged(); } public function set y(value:Number):void { _y = value; setPositionChanged(); } public function set z(value:Number):void { _z = value; setPositionChanged(); } public function set rotX(value:Number):void { _rotX = value; setGeometryChanged(); } public function set rotY(value:Number):void { _rotY = value; setGeometryChanged(); } public function set rotZ(value:Number):void { _rotZ = value; setGeometryChanged(); } public function set scaleX(value:Number):void { _scaleX = value; setGeometryChanged(); } public function set scaleY(value:Number):void { _scaleY = value; setGeometryChanged(); } public function set scaleZ(value:Number):void { _scaleZ = value; setGeometryChanged(); } public function get objects():Set { return _objects; } public function set solid(value:Boolean):void { // Сохранить солид _solid = value; var childSolid:Object3D; if (value) { // Если меня установили солидом, то разослать детям себя childSolid = this; } else { // Если я теперь не солид, то разослать детям своего солидПарента childSolid = solidParent; } // Рассылаем детям нового солидПарента for each (var object:Object3D in objects) { object.setSolidParent(childSolid); } // Пересчитать свет clearLightChanged(); updateLightChanged(); } public function get solid():Boolean { return _solid; } engine3d function setSolidParent(value:Object3D):void { // Забрали себя от старого solidParent solidParent.solidObjects.remove(this); // Добавили себя к новому solidParent value.solidObjects.add(this); // Если я не солид - установить этот солидПарент у дочерних объектов if (!solid) { for each (var object:Object3D in objects) { object.setSolidParent(value); } } solidParent = value; } // Установка новой камеры для объекта engine3d function setView(value:View3D):void { view = value; // При снятии камеры сбросить флаги и очистить источники if (value == null) { geometryChanged = false; positionChanged = false; clearLightChanged(); // При назначении камеры установить флаги изменения и проверить свет } else { geometryChanged = true; positionChanged = true; updateLightChanged(); } // Установить эту камеру у дочерних объектов for each (var object:Object3D in objects) { object.setView(value); } } engine3d function setParent(value:Object3D):void { // Если отцепили, то сам себе солидПарент if (value == null) { setSolidParent(this); // Взять парентСолид у парента } else { setSolidParent(value.solid ? value : value.solidParent); } parent = value; } // Флаги геометрии engine3d function setGeometryChanged():void { if (!geometryChanged) { updateLightChanged(); for each (var object:Object3D in objects) { object.setGeometryChanged(); } geometryChanged = true; } } // Флаги позиции engine3d function setPositionChanged():void { if (!positionChanged) { updateLightChanged(); for each (var object:Object3D in objects) { object.setPositionChanged(); } positionChanged = true; } } // Добавить источник света engine3d function addLightChanged(value:Light3D):void { lightChanged.add(value); } // Убрать источники света engine3d function clearLightChanged():void { lightChanged = new Set(); } // Флаг интерактивности public function set interactive(value:Boolean):void { _interactive = value; } public function get interactive():Boolean { return _interactive; } // Координаты объекта в системе координат камеры engine3d function get canvasCoords():Vector { return (view != null) ? new Vector(transform.d, transform.h, transform.l) : null; } // Клон public function clone():Object3D { var res:Object3D = new Object3D(); cloneParams(res); return res; } // Клонировать параметры protected function cloneParams(object:*):void { var obj:Object3D = Object3D(object); obj.x = x; obj.y = y; obj.z = z; obj.rotX = rotX; obj.rotY = rotY; obj.rotZ = rotZ; obj.scaleX = scaleX; obj.scaleY = scaleY; obj.scaleZ = scaleZ; obj.solid = solid; obj.name = name; obj.interactive = interactive; for each (var child:Object3D in objects) { obj.attach(child.clone()); } } // Получить ветку объектов от текущего до корневого engine3d function getBranch():Array { var res:Array = new Array(); var object:Object3D = this; while (object != null) { res.push(object); object = object.parent; } return res; } // Получить локальные координаты внутри объекта из координат камеры engine3d function canvasToLocal(coords:Vector):Vector { var res:Vector = null; if (view != null) { // Формируем ветку объектов var objectList:Array = getBranch(); // Перевести точку в мировые координаты res = Math3D.vectorTransform(coords, view.inverseTransformation); var object:Object3D; var objectMatrix:Matrix3D; // Перебираем список объектов с конца (с корневого объекта) var i:int; for (i = objectList.length - 1; i >= 0; i--) { object = objectList[i]; // Трансформируем точку через матрицу в локальные координаты текущего объекта res = Math3D.vectorTransform(res, object.inverseTransform); } } return res; } // Расчёт обратной локальной трансформации текущего объекта engine3d function get inverseTransform():Matrix3D { var res:Matrix3D = new Matrix3D(); Math3D.translateMatrix(res, -_x, -_y, -_z); Math3D.rotateZMatrix(res, -_rotZ); Math3D.rotateYMatrix(res, -_rotY); Math3D.rotateXMatrix(res, -_rotX); Math3D.scaleMatrix(res, 1/_scaleX, 1/_scaleY, 1/_scaleZ); return res; } } }