mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-26 01:49:05 -07:00
476 lines
14 KiB
Plaintext
476 lines
14 KiB
Plaintext
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;
|
||
}
|
||
|
||
|
||
}
|
||
} |