package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.sorting.FaceDistancePrimitive;
import alternativa.engine3d.sorting.FaceBSPPrimitive;
import alternativa.types.Point3D;
import alternativa.types.Set;
import alternativa.engine3d.sorting.FaceNonePrimitive;
use namespace alternativa3d;
/**
* Грань, образованная тремя или более вершинами. Грани являются составными частями полигональных объектов. Каждая грань
* содержит информацию об объекте и поверхности, которым она принадлежит. Для обеспечения возможности наложения
* текстуры на грань, первым трём её вершинам могут быть заданы UV-координаты, на основании которых расчитывается
* матрица трансформации текстуры.
*/
final public class Face {
/**
* @private
* Меш
*/
alternativa3d var _mesh:Mesh;
/**
* @private
* Поверхность
*/
alternativa3d var _surface:Surface;
/**
* @private
* Вершины грани
*/
alternativa3d var _vertices:Array = new Array();
/**
* @private
* Количество вершин
*/
alternativa3d var _verticesCount:uint;
/**
* @private
* Нормаль плоскости в пространстве
*/
alternativa3d var spacePerpendicular:Point3D = new Point3D();
/**
* @private
* Примитив грани
*/
alternativa3d var primitive:FaceNonePrimitive;
alternativa3d var pointPrimitive:FaceDistancePrimitive;
alternativa3d var polyPrimitive:FaceBSPPrimitive;
alternativa3d function calculatePerpendicular():void {
trace(this, "- calculatePerpendicular");
// Вектор AB
var vertex:Vertex = _vertices[0];
var av:Point3D = vertex.spaceCoords;
vertex = _vertices[1];
var bv:Point3D = vertex.spaceCoords;
var abx:Number = bv.x - av.x;
var aby:Number = bv.y - av.y;
var abz:Number = bv.z - av.z;
// Вектор AC
vertex = _vertices[2];
var cv:Point3D = vertex.spaceCoords;
var acx:Number = cv.x - av.x;
var acy:Number = cv.y - av.y;
var acz:Number = cv.z - av.z;
// Перпендикуляр к плоскости
spacePerpendicular.x = acz*aby - acy*abz;
spacePerpendicular.y = acx*abz - acz*abx;
spacePerpendicular.z = acy*abx - acx*aby;
//TODO: проверить на нулевой перпендикуляр
}
private function preparePointPrimitive():void {
// Расчёт центра грани
pointPrimitive.coords.x = 0;
pointPrimitive.coords.y = 0;
pointPrimitive.coords.z = 0;
for each (var vertex:Vertex in _vertices) {
pointPrimitive.coords.x += vertex.spaceCoords.x;
pointPrimitive.coords.y += vertex.spaceCoords.y;
pointPrimitive.coords.z += vertex.spaceCoords.z;
}
pointPrimitive.coords.x /= _verticesCount;
pointPrimitive.coords.y /= _verticesCount;
pointPrimitive.coords.z /= _verticesCount;
}
private function preparePolyPrimitive():void {
// Расчёт смещения плоскости
// Установка приоритета
polyPrimitive.bspLevel = _surface._bspLevel;
}
alternativa3d function createPointPrimitive():void {
trace(this, "- createPointPrimitive");
// Создание точечного примитива
pointPrimitive = FaceDistancePrimitive.create();
// Подготовка точечного примитива
preparePointPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.distancePrimitivesToAdd.push(pointPrimitive);
}
alternativa3d function createPolyPrimitive():void {
trace(this, "- createPolyPrimitive");
// Создание полигонального примитива
polyPrimitive = FaceBSPPrimitive.create();
// Подготовка полигонального примитива
preparePolyPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function updatePointPrimitive():void {
trace(this, "- updatePointPrimitive");
// Удаляем примитив
pointPrimitive.node.removePrimitive(pointPrimitive);
// Подготовка точечного примитива
preparePointPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.distancePrimitivesToAdd.push(pointPrimitive);
}
alternativa3d function updatePolyPrimitive():void {
trace(this, "- updatePolyPrimitive");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Подготовка полигонального примитива
preparePolyPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function redrawPointPrimitive():void {
trace(this, "- redrawPointPrimitive");
// Помечаем примитив на перерисовку
_surface._sortingLevel.changedPrimitives[pointPrimitive] = true;
}
alternativa3d function redrawPolyPrimitive():void {
trace(this, "- redrawPolyPrimitive");
_surface._sortingLevel.changedPrimitives[polyPrimitive] = true;
}
alternativa3d function changePolyToPointPrimitive():void {
trace(this, "- changePolyToPointPrimitive");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Смена типа примитива с полигонального на точечный
FaceBSPPrimitive.defer(polyPrimitive);
polyPrimitive = null;
pointPrimitive = FaceDistancePrimitive.create();
// Подготовка точечного примитива
preparePointPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.distancePrimitivesToAdd.push(pointPrimitive);
}
alternativa3d function changePointToPolyPrimitive():void {
trace(this, "- changePointToPolyPrimitive");
// Удаляем примитив
pointPrimitive.node.removePrimitive(pointPrimitive);
// Смена типа примитива с точечного на полигональный
FaceDistancePrimitive.defer(pointPrimitive);
pointPrimitive = null;
polyPrimitive = FaceBSPPrimitive.create();
// Подготовка полигонального примитива
preparePolyPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function destroyPointPrimitive():void {
trace(this, "- destroyPointPrimitive");
// Удаляем примитив
pointPrimitive.node.removePrimitive(pointPrimitive);
// Удаляем примитив
FaceDistancePrimitive.defer(pointPrimitive);
pointPrimitive = null;
}
alternativa3d function destroyPolyPrimitive():void {
trace(this, "- destroyPolyPrimitive");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Разрушаем примитив
FaceBSPPrimitive.defer(polyPrimitive);
polyPrimitive = null;
}
alternativa3d function updatePolyPrimitiveBSPLevel():void {
trace(this, "- updatePolyPrimitiveBSPLevel");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Обновляем приоритет примитива
polyPrimitive.bspLevel = _surface._bspLevel;
// Отправляем примитив на добавление в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function changeSurface():void {
trace(this, "changeSurface");
// Если есть поверхность и материал
if (_surface != null && _surface._material != null) {
// Если BSP-сортировка
if (_surface._sortingMode == 2) {
// Если есть полигональный примитив
if (polyPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Обновление полигонального примитива
updatePolyPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
} else {
// Если изменилась мобильность
if (polyPrimitive.bspLevel != _surface._bspLevel) {
// Обновляем мобильность примитива
updatePolyPrimitiveBSPLevel();
} else {
// Отправить полигональный примитив на перерисовку
redrawPolyPrimitive();
}
}
} else {
// Если есть точечный примитив
if (pointPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
// Смена типа примитива с точечного на полигональный
changePointToPolyPrimitive();
} else {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Создание полигонального примитива
createPolyPrimitive();
}
}
} else {
// Если точечная сортировка
if (_surface._sortingMode == 1) {
// Если есть точечный примитив
if (pointPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Обновление точечного примитива
updatePointPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
} else {
// Отправить точечный примитив на перерисовку
redrawPointPrimitive();
}
} else {
// Если есть полигональный примитив
if (polyPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
// Смена типа примитива с полигонального на точечный
changePolyToPointPrimitive();
} else {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Создание точечного примитива
createPointPrimitive();
}
}
} else {
// Если нет сортировки
}
}
} else {
// Если был точечный примитив
if (pointPrimitive != null) {
// Удаляем точечный примитив
destroyPointPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
} else {
// Если был полигональный примитив
if (polyPrimitive != null) {
// Удаляем полигональный примитив
destroyPolyPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
}
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем пометку на смену поверхности
delete _mesh._scene.facesToChangeSurface[this];
}
// Вызывается только при изменении координат хотя бы одной из вершин
alternativa3d function transform():void {
trace(this, "transform");
// Расчитываем перпендикуляр
calculatePerpendicular();
if (pointPrimitive != null) {
updatePointPrimitive();
} else {
updatePolyPrimitive();
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
/**
* Массив вершин грани, представленных объектами класса alternativa.engine3d.core.Vertex.
*
* @see Vertex
*/
public function get vertices():Array {
return new Array().concat(_vertices);
}
/**
* Количество вершин грани.
*/
public function get verticesCount():uint {
return _verticesCount;
}
/**
* Полигональный объект, которому принадлежит грань.
*/
public function get mesh():Mesh {
return _mesh;
}
/**
* Поверхность, которой принадлежит грань.
*/
public function get surface():Surface {
return _surface;
}
/**
* Идентификатор грани в полигональном объекте. В случае, если грань не принадлежит ни одному объекту, идентификатор
* имеет значение null.
*/
public function get id():Object {
return (_mesh != null) ? _mesh.getFaceId(this) : null;
}
/**
* Нормаль в локальной системе координат.
*/
public function get normal():Point3D {
var res:Point3D = new Point3D();
var vertex:Vertex = _vertices[0];
var av:Point3D = vertex.coords;
vertex = _vertices[1];
var bv:Point3D = vertex.coords;
var abx:Number = bv.x - av.x;
var aby:Number = bv.y - av.y;
var abz:Number = bv.z - av.z;
vertex = _vertices[2];
var cv:Point3D = vertex.coords;
var acx:Number = cv.x - av.x;
var acy:Number = cv.y - av.y;
var acz:Number = cv.z - av.z;
res.x = acz*aby - acy*abz;
res.y = acx*abz - acz*abx;
res.z = acy*abx - acx*aby;
if (res.x != 0 || res.y != 0 || res.z != 0) {
var k:Number = Math.sqrt(res.x*res.x + res.y*res.y + res.z*res.z);
res.x /= k;
res.y /= k;
res.z /= k;
}
return res;
}
/**
* Множество граней, имеющих общие рёбра с текущей гранью.
*/
public function get edgeJoinedFaces():Set {
var res:Set = new Set(true);
// Перебираем точки грани
for (var i:uint = 0; i < _verticesCount; i++) {
var a:Vertex = _vertices[i];
var b:Vertex = _vertices[(i < _verticesCount - 1) ? (i + 1) : 0];
// Перебираем грани текущей точки
for (var key:* in a._faces) {
var face:Face = key;
// Если это другая грань и у неё также есть следующая точка
if (face != this && face._vertices.indexOf(b) >= 0) {
// Значит у граней общее ребро
res[face] = true;
}
}
}
return res;
}
/**
* Строковое представление грани.
*
* @return строковое представление грани
*/
public function toString():String {
var res:String = "[Face ID:" + id + ((_verticesCount > 0) ? " vertices:" : "");
for (var i:uint = 0; i < _verticesCount; i++) {
var vertex:Vertex = _vertices[i];
res += vertex.id + ((i < _verticesCount - 1) ? ", " : "");
}
res += "]";
return res;
}
}
}