mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-26 09:49:07 -07:00
708 lines
23 KiB
ActionScript
708 lines
23 KiB
ActionScript
package alternativa.engine3d.core {
|
||
import alternativa.engine3d.*;
|
||
import alternativa.engine3d.errors.Object3DHierarchyError;
|
||
import alternativa.engine3d.errors.Object3DNotFoundError;
|
||
import alternativa.types.Matrix3D;
|
||
import alternativa.types.Point3D;
|
||
import alternativa.types.Set;
|
||
import alternativa.utils.ObjectUtils;
|
||
|
||
use namespace alternativa3d;
|
||
|
||
/**
|
||
* Базовый класс для объектов, находящихся в сцене. Класс реализует иерархию объектов сцены, а также содержит сведения
|
||
* о трансформации объекта как единого целого.
|
||
*
|
||
* <p> Масштабирование, ориентация и положение объекта задаются в родительской системе координат. Результирующая
|
||
* локальная трансформация является композицией операций масштабирования, поворотов объекта относительно осей
|
||
* <code>X</code>, <code>Y</code>, <code>Z</code> и параллельного переноса центра объекта из начала координат.
|
||
* Операции применяются в порядке их перечисления.
|
||
*
|
||
* <p> Глобальная трансформация (в системе координат корневого объекта сцены) является композицией трансформаций
|
||
* самого объекта и всех его предков по иерархии объектов сцены.
|
||
*/
|
||
public class Object3D {
|
||
// Операции
|
||
/**
|
||
* @private
|
||
* Поворот или масштабирование
|
||
*/
|
||
alternativa3d var changeRotationOrScaleOperation:Operation = new Operation("changeRotationOrScale", this);
|
||
/**
|
||
* @private
|
||
* Перемещение
|
||
*/
|
||
alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this);
|
||
/**
|
||
* @private
|
||
* Расчёт матрицы трансформации
|
||
*/
|
||
alternativa3d var calculateTransformationOperation:Operation = new Operation("calculateTransformation", this, calculateTransformation, Operation.OBJECT_CALCULATE_TRANSFORMATION);
|
||
/**
|
||
* @private
|
||
* Изменение уровеня мобильности
|
||
*/
|
||
alternativa3d var calculateMobilityOperation:Operation = new Operation("calculateMobility", this, calculateMobility, Operation.OBJECT_CALCULATE_MOBILITY);
|
||
|
||
// Инкремент количества объектов
|
||
private static var counter:uint = 0;
|
||
|
||
/**
|
||
* @private
|
||
* Наименование
|
||
*/
|
||
alternativa3d var _name:String;
|
||
/**
|
||
* @private
|
||
* Сцена
|
||
*/
|
||
alternativa3d var _scene:Scene3D;
|
||
/**
|
||
* @private
|
||
* Родительский объект
|
||
*/
|
||
alternativa3d var _parent:Object3D;
|
||
/**
|
||
* @private
|
||
* Дочерние объекты
|
||
*/
|
||
alternativa3d var _children:Set = new Set();
|
||
/**
|
||
* @private
|
||
* Уровень мобильности
|
||
*/
|
||
alternativa3d var _mobility:int = 0;
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var inheritedMobility:int;
|
||
/**
|
||
* @private
|
||
* Координаты объекта относительно родителя
|
||
*/
|
||
alternativa3d var _coords:Point3D = new Point3D();
|
||
/**
|
||
* @private
|
||
* Поворот объекта по оси X относительно родителя. Угол измеряется в радианах.
|
||
*/
|
||
alternativa3d var _rotationX:Number = 0;
|
||
/**
|
||
* @private
|
||
* Поворот объекта по оси Y относительно родителя. Угол измеряется в радианах.
|
||
*/
|
||
alternativa3d var _rotationY:Number = 0;
|
||
/**
|
||
* @private
|
||
* Поворот объекта по оси Z относительно родителя. Угол измеряется в радианах.
|
||
*/
|
||
alternativa3d var _rotationZ:Number = 0;
|
||
/**
|
||
* @private
|
||
* Мастшаб объекта по оси X относительно родителя
|
||
*/
|
||
alternativa3d var _scaleX:Number = 1;
|
||
/**
|
||
* @private
|
||
* Мастшаб объекта по оси Y относительно родителя
|
||
*/
|
||
alternativa3d var _scaleY:Number = 1;
|
||
/**
|
||
* @private
|
||
* Мастшаб объекта по оси Z относительно родителя
|
||
*/
|
||
alternativa3d var _scaleZ:Number = 1;
|
||
/**
|
||
* @private
|
||
* Полная матрица трансформации, переводящая координаты из локальной системы координат объекта в систему координат сцены
|
||
*/
|
||
alternativa3d var transformation:Matrix3D = new Matrix3D();
|
||
/**
|
||
* @private
|
||
* Координаты в сцене
|
||
*/
|
||
alternativa3d var globalCoords:Point3D = new Point3D();
|
||
|
||
/**
|
||
* Создание экземпляра класса.
|
||
*
|
||
* @param name имя экземпляра
|
||
*/
|
||
public function Object3D(name:String = null) {
|
||
// Имя по-умолчанию
|
||
_name = (name != null) ? name : defaultName();
|
||
|
||
// Последствия операций
|
||
changeRotationOrScaleOperation.addSequel(calculateTransformationOperation);
|
||
changeCoordsOperation.addSequel(calculateTransformationOperation);
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Расчёт трансформации
|
||
*/
|
||
private function calculateTransformation():void {
|
||
if (changeRotationOrScaleOperation.queued) {
|
||
// Если полная трансформация
|
||
transformation.toTransform(_coords.x, _coords.y, _coords.z, _rotationX, _rotationY, _rotationZ, _scaleX, _scaleY, _scaleZ);
|
||
if (_parent != null) {
|
||
transformation.combine(_parent.transformation);
|
||
}
|
||
// Сохраняем глобальные координаты объекта
|
||
globalCoords.x = transformation.d;
|
||
globalCoords.y = transformation.h;
|
||
globalCoords.z = transformation.l;
|
||
} else {
|
||
// Если только перемещение
|
||
globalCoords.copy(_coords);
|
||
if (_parent != null) {
|
||
globalCoords.transform(_parent.transformation);
|
||
}
|
||
transformation.offset(globalCoords.x, globalCoords.y, globalCoords.z);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Расчёт общей мобильности
|
||
*/
|
||
private function calculateMobility():void {
|
||
inheritedMobility = ((_parent != null) ? _parent.inheritedMobility : 0) + _mobility;
|
||
}
|
||
|
||
/**
|
||
* Добавление дочернего объекта. Добавляемый объект удаляется из списка детей предыдущего родителя.
|
||
* Новой сценой дочернего объекта становится сцена родителя.
|
||
*
|
||
* @param child добавляемый объект
|
||
*
|
||
* @throws alternativa.engine3d.errors.Object3DHierarchyError нарушение иерархии объектов сцены
|
||
*/
|
||
public function addChild(child:Object3D):void {
|
||
|
||
// Проверка на null
|
||
if (child == null) {
|
||
throw new Object3DHierarchyError(null, this);
|
||
}
|
||
|
||
// Проверка на наличие
|
||
if (child._parent == this) {
|
||
return;
|
||
}
|
||
|
||
// Проверка на добавление к самому себе
|
||
if (child == this) {
|
||
throw new Object3DHierarchyError(this, this);
|
||
}
|
||
|
||
// Проверка на добавление родительского объекта
|
||
if (child._scene == _scene) {
|
||
// Если объект был в той же сцене, либо оба не были в сцене
|
||
var parentObject:Object3D = _parent;
|
||
while (parentObject != null) {
|
||
if (child == parentObject) {
|
||
throw new Object3DHierarchyError(child, this);
|
||
return;
|
||
}
|
||
parentObject = parentObject._parent;
|
||
}
|
||
}
|
||
|
||
// Если объект был в другом объекте
|
||
if (child._parent != null) {
|
||
// Удалить его оттуда
|
||
child._parent._children.remove(child);
|
||
} else {
|
||
// Если объект был корневым в сцене
|
||
if (child._scene != null && child._scene._root == child) {
|
||
child._scene.root = null;
|
||
}
|
||
}
|
||
|
||
// Добавляем в список
|
||
_children.add(child);
|
||
// Указываем себя как родителя
|
||
child.setParent(this);
|
||
// Устанавливаем уровни
|
||
child.setLevel((calculateTransformationOperation.priority & 0xFFFFFF) + 1);
|
||
// Указываем сцену
|
||
child.setScene(_scene);
|
||
}
|
||
|
||
/**
|
||
* Удаление дочернего объекта.
|
||
*
|
||
* @param child удаляемый дочерний объект
|
||
*
|
||
* @throws alternativa.engine3d.errors.Object3DNotFoundError указанный объект не содержится в списке детей текущего объекта
|
||
*/
|
||
public function removeChild(child:Object3D):void {
|
||
// Проверка на null
|
||
if (child == null) {
|
||
throw new Object3DNotFoundError(null, this);
|
||
}
|
||
// Проверка на наличие
|
||
if (child._parent != this) {
|
||
throw new Object3DNotFoundError(child, this);
|
||
}
|
||
// Убираем из списка
|
||
_children.remove(child);
|
||
// Удаляем ссылку на родителя
|
||
child.setParent(null);
|
||
// Удаляем ссылку на сцену
|
||
child.setScene(null);
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Установка родительского объекта.
|
||
*
|
||
* @param value родительский объект
|
||
*/
|
||
alternativa3d function setParent(value:Object3D):void {
|
||
// Отписываемся от сигналов старого родителя
|
||
if (_parent != null) {
|
||
removeParentSequels();
|
||
}
|
||
// Сохранить родителя
|
||
_parent = value;
|
||
// Если устанавливаем родителя
|
||
if (value != null) {
|
||
// Подписка на сигналы родителя
|
||
addParentSequels();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Установка новой сцены для объекта.
|
||
*
|
||
* @param value сцена
|
||
*/
|
||
alternativa3d function setScene(value:Scene3D):void {
|
||
if (_scene != value) {
|
||
// Если была сцена
|
||
if (_scene != null) {
|
||
// Удалиться из сцены
|
||
removeFromScene(_scene);
|
||
}
|
||
// Если новая сцена
|
||
if (value != null) {
|
||
// Добавиться на сцену
|
||
addToScene(value);
|
||
}
|
||
// Сохранить сцену
|
||
_scene = value;
|
||
} else {
|
||
// Посылаем операцию трансформации
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
// Посылаем операцию пересчёта мобильности
|
||
addOperationToScene(calculateMobilityOperation);
|
||
}
|
||
// Установить эту сцену у дочерних объектов
|
||
for (var key:* in _children) {
|
||
var object:Object3D = key;
|
||
object.setScene(_scene);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Установка уровня операции трансформации.
|
||
*
|
||
* @param value уровень операции трансформации
|
||
*/
|
||
alternativa3d function setLevel(value:uint):void {
|
||
// Установить уровень операции трансформации и расчёта мобильности
|
||
calculateTransformationOperation.priority = (calculateTransformationOperation.priority & 0xFF000000) | value;
|
||
calculateMobilityOperation.priority = (calculateMobilityOperation.priority & 0xFF000000) | value;
|
||
// Установить уровни у дочерних объектов
|
||
for (var key:* in _children) {
|
||
var object:Object3D = key;
|
||
object.setLevel(value + 1);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Подписка на сигналы родителя.
|
||
*/
|
||
private function addParentSequels():void {
|
||
_parent.changeCoordsOperation.addSequel(changeCoordsOperation);
|
||
_parent.changeRotationOrScaleOperation.addSequel(changeRotationOrScaleOperation);
|
||
_parent.calculateMobilityOperation.addSequel(calculateMobilityOperation);
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Удаление подписки на сигналы родителя.
|
||
*/
|
||
private function removeParentSequels():void {
|
||
_parent.changeCoordsOperation.removeSequel(changeCoordsOperation);
|
||
_parent.changeRotationOrScaleOperation.removeSequel(changeRotationOrScaleOperation);
|
||
_parent.calculateMobilityOperation.removeSequel(calculateMobilityOperation);
|
||
}
|
||
|
||
/**
|
||
* Метод вызывается при добавлении объекта на сцену. Наследники могут переопределять метод для выполнения
|
||
* специфических действий.
|
||
*
|
||
* @param scene сцена, в которую добавляется объект
|
||
*/
|
||
protected function addToScene(scene:Scene3D):void {
|
||
// При добавлении на сцену полная трансформация и расчёт мобильности
|
||
scene.addOperation(changeRotationOrScaleOperation);
|
||
scene.addOperation(calculateMobilityOperation);
|
||
}
|
||
|
||
/**
|
||
* Метод вызывается при удалении объекта со сцены. Наследники могут переопределять метод для выполнения
|
||
* специфических действий.
|
||
*
|
||
* @param scene сцена, из которой удаляется объект
|
||
*/
|
||
protected function removeFromScene(scene:Scene3D):void {
|
||
// Удаляем все операции из очереди
|
||
scene.removeOperation(changeRotationOrScaleOperation);
|
||
scene.removeOperation(changeCoordsOperation);
|
||
scene.removeOperation(calculateMobilityOperation);
|
||
}
|
||
|
||
/**
|
||
* Имя объекта.
|
||
*/
|
||
public function get name():String {
|
||
return _name;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set name(value:String):void {
|
||
_name = value;
|
||
}
|
||
|
||
/**
|
||
* Сцена, которой принадлежит объект.
|
||
*/
|
||
public function get scene():Scene3D {
|
||
return _scene;
|
||
}
|
||
|
||
/**
|
||
* Родительский объект.
|
||
*/
|
||
public function get parent():Object3D {
|
||
return _parent;
|
||
}
|
||
|
||
/**
|
||
* Набор дочерних объектов.
|
||
*/
|
||
public function get children():Set {
|
||
return _children.clone();
|
||
}
|
||
|
||
/**
|
||
* Уровень мобильности. Результирующая мобильность объекта является суммой мобильностей объекта и всех его предков
|
||
* по иерархии объектов в сцене. Результирующая мобильность влияет на положение объекта в BSP-дереве. Менее мобильные
|
||
* объекты находятся ближе к корню дерева, чем более мобильные.
|
||
*/
|
||
public function get mobility():int {
|
||
return _mobility;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set mobility(value:int):void {
|
||
if (_mobility != value) {
|
||
_mobility = value;
|
||
addOperationToScene(calculateMobilityOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Координата X.
|
||
*/
|
||
public function get x():Number {
|
||
return _coords.x;
|
||
}
|
||
|
||
/**
|
||
* Координата Y.
|
||
*/
|
||
public function get y():Number {
|
||
return _coords.y;
|
||
}
|
||
|
||
/**
|
||
* Координата Z.
|
||
*/
|
||
public function get z():Number {
|
||
return _coords.z;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set x(value:Number):void {
|
||
if (_coords.x != value) {
|
||
_coords.x = value;
|
||
addOperationToScene(changeCoordsOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set y(value:Number):void {
|
||
if (_coords.y != value) {
|
||
_coords.y = value;
|
||
addOperationToScene(changeCoordsOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set z(value:Number):void {
|
||
if (_coords.z != value) {
|
||
_coords.z = value;
|
||
addOperationToScene(changeCoordsOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Координаты объекта.
|
||
*/
|
||
public function get coords():Point3D {
|
||
return _coords.clone();
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set coords(value:Point3D):void {
|
||
if (!_coords.equals(value)) {
|
||
_coords.copy(value);
|
||
addOperationToScene(changeCoordsOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Угол поворота вокруг оси X, заданный в радианах.
|
||
*/
|
||
public function get rotationX():Number {
|
||
return _rotationX;
|
||
}
|
||
|
||
/**
|
||
* Угол поворота вокруг оси Y, заданный в радианах.
|
||
*/
|
||
public function get rotationY():Number {
|
||
return _rotationY;
|
||
}
|
||
|
||
/**
|
||
* Угол поворота вокруг оси Z, заданный в радианах.
|
||
*/
|
||
public function get rotationZ():Number {
|
||
return _rotationZ;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set rotationX(value:Number):void {
|
||
if (_rotationX != value) {
|
||
_rotationX = value;
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set rotationY(value:Number):void {
|
||
if (_rotationY != value) {
|
||
_rotationY = value;
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set rotationZ(value:Number):void {
|
||
if (_rotationZ != value) {
|
||
_rotationZ = value;
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Коэффициент масштабирования вдоль оси X.
|
||
*/
|
||
public function get scaleX():Number {
|
||
return _scaleX;
|
||
}
|
||
|
||
/**
|
||
* Коэффициент масштабирования вдоль оси Y.
|
||
*/
|
||
public function get scaleY():Number {
|
||
return _scaleY;
|
||
}
|
||
|
||
/**
|
||
* Коэффициент масштабирования вдоль оси Z.
|
||
*/
|
||
public function get scaleZ():Number {
|
||
return _scaleZ;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set scaleX(value:Number):void {
|
||
if (_scaleX != value) {
|
||
_scaleX = value;
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set scaleY(value:Number):void {
|
||
if (_scaleY != value) {
|
||
_scaleY = value;
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set scaleZ(value:Number):void {
|
||
if (_scaleZ != value) {
|
||
_scaleZ = value;
|
||
addOperationToScene(changeRotationOrScaleOperation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Строковое представление объекта.
|
||
*
|
||
* @return строковое представление объекта
|
||
*/
|
||
public function toString():String {
|
||
return "[" + ObjectUtils.getClassName(this) + " " + _name + "]";
|
||
}
|
||
|
||
/**
|
||
* Имя объекта по умолчанию.
|
||
*
|
||
* @return имя объекта по умолчанию
|
||
*/
|
||
protected function defaultName():String {
|
||
return "object" + ++counter;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Добавление операции в очередь.
|
||
*
|
||
* @param operation добавляемая операция
|
||
*/
|
||
alternativa3d function addOperationToScene(operation:Operation):void {
|
||
if (_scene != null) {
|
||
_scene.addOperation(operation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
* Удаление операции из очереди.
|
||
*
|
||
* @param operation удаляемая операция
|
||
*/
|
||
alternativa3d function removeOperationFromScene(operation:Operation):void {
|
||
if (_scene != null) {
|
||
_scene.removeOperation(operation);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Создание пустого объекта без какой-либо внутренней структуры. Например, если некоторый геометрический примитив при
|
||
* своём создании формирует набор вершин, граней и поверхностей, то этот метод не должен создавать вершины, грани и
|
||
* поверхности. Данный метод используется в методе clone() и должен быть переопределён в потомках для получения
|
||
* правильного объекта.
|
||
*
|
||
* @return новый пустой объект
|
||
*/
|
||
protected function createEmptyObject():Object3D {
|
||
return new Object3D();
|
||
}
|
||
|
||
/**
|
||
* Копирование свойств объекта-источника. Данный метод используется в методе clone() и должен быть переопределён в
|
||
* потомках для получения правильного объекта. Каждый потомок должен в переопределённом методе копировать только те
|
||
* свойства, которые добавлены к базовому классу именно в нём. Копирование унаследованных свойств выполняется
|
||
* вызовом super.clonePropertiesFrom(source).
|
||
*
|
||
* @param source объект, свойства которого копируются
|
||
*/
|
||
protected function clonePropertiesFrom(source:Object3D):void {
|
||
_name = source._name;
|
||
_mobility = source._mobility;
|
||
_coords.x = source._coords.x;
|
||
_coords.y = source._coords.y;
|
||
_coords.z = source._coords.z;
|
||
_rotationX = source._rotationX;
|
||
_rotationY = source._rotationY;
|
||
_rotationZ = source._rotationZ;
|
||
_scaleX = source._scaleX;
|
||
_scaleY = source._scaleY;
|
||
_scaleZ = source._scaleZ;
|
||
}
|
||
|
||
/**
|
||
* Клонирование объекта. Для реализации собственного клонирования наследники должны переопределять методы
|
||
* <code>createEmptyObject()</code> и <code>clonePropertiesFrom()</code>.
|
||
*
|
||
* @return клонированный экземпляр объекта
|
||
*
|
||
* @see #createEmptyObject()
|
||
* @see #clonePropertiesFrom()
|
||
*/
|
||
public function clone():Object3D {
|
||
var copy:Object3D = createEmptyObject();
|
||
copy.clonePropertiesFrom(this);
|
||
|
||
// Клонирование детей
|
||
for (var key:* in _children) {
|
||
var child:Object3D = key;
|
||
copy.addChild(child.clone());
|
||
}
|
||
|
||
return copy;
|
||
}
|
||
|
||
/**
|
||
* Получение дочернего объекта с заданным именем.
|
||
*
|
||
* @param name имя дочернего объекта
|
||
* @return любой дочерний объект с заданным именем или <code>null</code> в случае отсутствия таких объектов
|
||
*/
|
||
public function getChildByName(name:String):Object3D {
|
||
for (var key:* in _children) {
|
||
var child:Object3D = key;
|
||
if (child._name == name) {
|
||
return child;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
} |