Files
alternativa3d-archive/Alternativa3D5/5.0.0/alternativa/engine3d/loaders/.svn/text-base/Loader3DS.as.svn-base
2024-10-05 12:11:16 +01:00

974 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package alternativa.engine3d.loaders {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Mesh;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Surface;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.TextureMaterialPrecision;
import alternativa.engine3d.materials.WireMaterial;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.types.Texture;
import alternativa.utils.ColorUtils;
import alternativa.utils.MathUtils;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Loader;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import flash.utils.Endian;
use namespace alternativa3d;
/**
* Загрузчик моделей в формате 3DS.
*/
public class Loader3DS extends EventDispatcher {
/**
* Дюймы
*/
public static const INCHES:Number = 1;
/**
* Футы
*/
public static const FEET:Number = 0.0833333334;
/**
* Мили
*/
public static const MILES:Number = 0.0000157828;
/**
* Миллиметры
*/
public static const MILLIMETERS:Number = 25.4000000259;
/**
* Сантиметры
*/
public static const CENTIMETERS:Number = 2.5400000025;
/**
* Метры
*/
public static const METERS:Number = 0.0254;
/**
* Километры
*/
public static const KILOMETERS:Number = 0.0000254;
private static var stub:BitmapData;
private var _content:Object3D;
private var version:uint;
private var objectDatas:Array;
private var animationDatas:Array;
private var materialDatas:Array;
private var bitmaps:Array;
private var urlLoader:URLLoader;
private var data:ByteArray;
private var counter:uint;
private var sequence:Array;
private var loader:Loader;
private var context:LoaderContext;
private var path:String;
/**
* Повтор текстуры при заливке
*/
public var repeat:Boolean = true;
/**
* Сглаживание текстур при масштабировании
*/
public var smooth:Boolean = false;
/**
* Режим наложения цветов
*/
public var blendMode:String = BlendMode.NORMAL;
/**
* Точность перспективной коррекции
*/
public var precision:Number = TextureMaterialPrecision.MEDIUM;
/**
* Определяет в какие единицы измерения перевести координаты модели.
* Может быть любым значением из констант Loader3DS
* <table>
* <tr><td><code>INCHES</code></td><td>переводить в дюймы</td></tr>
* <tr><td><code>FEET</code></td><td>переводить в футы</td></tr>
* <tr><td><code>MILES</code></td><td>переводить в мили</td></tr>
* <tr><td><code>MILLIMETERS</code></td><td>переводить в миллиметры</td></tr>
* <tr><td><code>CENTIMETERS</code></td><td>переводить в сантиметры</td></tr>
* <tr><td><code>METERS</code></td><td>переводить в метры</td></tr>
* <tr><td><code>KILOMETERS</code></td><td>переводить в километры</td></tr>
* </table>
*/
public var units:Number = INCHES;
/**
* Уровень мобильности
*/
public var mobility:int = 0;
/**
* Загрузка модели.
*
* @param url адрес, откуда загружается модель
* @param context
*/
public function load(url:String, context:LoaderContext = null):void {
path = url.substring(0, url.lastIndexOf("/") + 1);
this.context = context;
urlLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(new URLRequest(url));
urlLoader.addEventListener(Event.COMPLETE, on3DSLoad);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, on3DSError);
urlLoader.addEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
urlLoader.addEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
// Очистка
_content = null;
version = 0;
objectDatas = null;
animationDatas = null;
materialDatas = null;
bitmaps = null;
sequence = null;
if (loader != null) {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadNextBitmap);
loader.close();
loader = null;
}
}
private function on3DSLoad(e:Event):void {
data = urlLoader.data;
data.endian = Endian.LITTLE_ENDIAN;
parse3DSChunk(0, data.bytesAvailable);
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
urlLoader = null;
}
private function on3DSError(e:Event):void {
_content = null;
throw new IOError(IOErrorEvent(e).text);
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
}
private function loadBitmaps():void {
if (bitmaps != null) {
counter = 0;
sequence = new Array();
for (var filename:String in bitmaps) {
sequence.push(filename);
}
loader = new Loader();
loader.load(new URLRequest(path + sequence[counter]), context);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNextBitmap);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNextBitmap);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNextBitmap);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNextBitmap);
} else {
buildContent();
}
}
private function loadNextBitmap(e:Event = null):void {
if (!(e is IOErrorEvent)) {
bitmaps[sequence[counter]] = Bitmap(loader.content).bitmapData;
} else {
if (stub == null) {
var size:uint = 10;
stub = new BitmapData(size, size, false, 0);
for (var i:uint = 0; i < size; i++) {
for (var j:uint = 0; j < size; j+=2) {
stub.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
}
}
}
bitmaps[sequence[counter]] = stub;
}
counter++;
if (counter < sequence.length) {
loader.load(new URLRequest(path + sequence[counter]), context);
} else {
buildContent();
}
if (e is IOErrorEvent) {
throw new IOError(IOErrorEvent(e).text);
}
}
private function buildContent():void {
var i:uint;
var length:uint;
// Формируем связи объектов
_content = new Object3D();
// Создаём материалы
var materialData:MaterialData;
for (var materialName:String in materialDatas) {
materialData = materialDatas[materialName];
var mapData:MapData = materialData.diffuseMap;
var materialMatrix:Matrix = new Matrix();
if (mapData != null) {
var rot:Number = MathUtils.toRadian(mapData.rotation);
var rotSin:Number = Math.sin(rot);
var rotCos:Number = Math.cos(rot);
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
materialMatrix.translate(-0.5, -0.5);
materialMatrix.rotate(-rot);
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
materialMatrix.translate(0.5, 0.5);
}
materialData.matrix = materialMatrix;
}
// Если есть данные об анимации и иерархии объектов
var objectName:String;
var objectData:ObjectData;
var mesh:Mesh;
if (animationDatas != null) {
if (objectDatas != null) {
length = animationDatas.length;
for (i = 0; i < length; i++) {
var animationData:AnimationData = animationDatas[i];
objectName = animationData.objectName;
objectData = objectDatas[objectName];
if (objectData != null && objectData.vertices != null) {
// Меш
mesh = new Mesh(objectName);
animationData.object = mesh;
buildObject(animationData);
buildMesh(mesh, objectData, animationData);
} else {
var object:Object3D = new Object3D(objectName);
animationData.object = object;
buildObject(animationData);
}
}
buildHierarchy(_content, 0, length - 1);
}
} else {
for (objectName in objectDatas) {
objectData = objectDatas[objectName];
if (objectData.vertices != null) {
// Меш
mesh = new Mesh(objectName);
buildMesh(mesh, objectData, null);
_content.addChild(mesh);
}
}
}
// Рассылаем событие о завершении
dispatchEvent(new Event(Event.COMPLETE));
}
private function buildObject(animationData:AnimationData):void {
var object:Object3D = animationData.object;
if (animationData.position != null) {
object.x = animationData.position.x;
object.y = animationData.position.y;
object.z = animationData.position.z;
}
if (animationData.rotation != null) {
object.rotationX = animationData.rotation.x;
object.rotationY = animationData.rotation.y;
object.rotationZ = animationData.rotation.z;
}
if (animationData.scale != null) {
object.scaleX = animationData.scale.x;
object.scaleY = animationData.scale.y;
object.scaleZ = animationData.scale.z;
}
object.mobility = mobility;
}
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
// Добавляем вершины
var i:uint;
var length:uint = objectData.vertices.length;
for (i = 0; i < length; i++) {
var vertexData:Point3D = objectData.vertices[i];
objectData.vertices[i] = mesh.addVertex(vertexData.x, vertexData.y, vertexData.z, i);
}
// Коррекция вершин
if (animationData != null) {
// Инвертируем матрицу
objectData.matrix.invert();
// Вычитаем точку привязки из смещения матрицы
objectData.matrix.d -= animationData.pivot.x;
objectData.matrix.h -= animationData.pivot.y;
objectData.matrix.l -= animationData.pivot.z;
// Трансформируем вершины
for (var key:* in mesh._vertices) {
var vertex:Vertex = mesh._vertices[key];
vertex._coords.transform(objectData.matrix);
vertex._coords.multiply(units);
}
}
// Добавляем грани
length = objectData.faces.length;
for (i = 0; i < length; i++) {
var faceData:FaceData = objectData.faces[i];
mesh.addFace([objectData.vertices[faceData.a], objectData.vertices[faceData.b], objectData.vertices[faceData.c]], i);
}
// Добавляем поверхности
if (objectData.surfaces != null) {
for (var surfaceId:String in objectData.surfaces) {
var materialData:MaterialData = materialDatas[surfaceId];
var surfaceData:SurfaceData = objectData.surfaces[surfaceId];
var surface:Surface = mesh.addSurface(surfaceData.faces, surfaceId);
if (materialData.diffuseMap != null || materialData.normalMap != null) {
surface.material = new TextureMaterial(new Texture(bitmaps[materialData.diffuseMap.filename], materialData.diffuseMap.filename), 1 - materialData.transparency/100, repeat, smooth, blendMode, precision);
length = surfaceData.faces.length;
if (objectData.uvs != null) {
for (i = 0; i < length; i++) {
var id:uint = surfaceData.faces[i];
var fd:FaceData = objectData.faces[id];
var face:Face = mesh.getFaceById(id);
var aUV:Point = objectData.uvs[fd.a];
var bUV:Point = objectData.uvs[fd.b];
var cUV:Point = objectData.uvs[fd.c];
if (aUV != null && bUV != null && cUV != null && ((bUV.x - aUV.x)*(cUV.y - aUV.y) - (bUV.y - aUV.y)*(cUV.x - aUV.x) != 0)) {
face.aUV = materialData.matrix.transformPoint(aUV);
face.bUV = materialData.matrix.transformPoint(bUV);
face.cUV = materialData.matrix.transformPoint(cUV);
}
}
}
} else {
surface.material = new FillMaterial(materialDatas[surfaceId].color, 1 - materialData.transparency/100);
}
}
} else {
// Поверхность по умолчанию
var defaultSurface:Surface = mesh.addSurface();
// Добавляем грани
for (var faceId:String in mesh._faces) {
defaultSurface.addFace(mesh._faces[faceId]);
}
defaultSurface.material = new WireMaterial(0);
}
}
private function buildHierarchy(parent:Object3D, begin:uint, end:uint):void {
if (begin <= end) {
var animation:AnimationData = animationDatas[begin];
var object:Object3D = animation.object;
parent.addChild(object);
var parentIndex:uint = animation.parentIndex;
for (var i:uint = begin + 1; i <= end; i++) {
animation = animationDatas[i];
if (parentIndex == animation.parentIndex) {
buildHierarchy(object, begin + 1, i - 1);
buildHierarchy(parent, i, end);
return;
}
}
buildHierarchy(object, begin + 1, end);
}
}
private function parse3DSChunk(index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Главный
case 0x4D4D:
parseMainChunk(dataIndex, dataLength);
break;
}
parse3DSChunk(index + chunkLength, length - chunkLength);
} else {
// Загрузка битмап
loadBitmaps();
}
}
private function parseMainChunk(index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Версия
case 0x0002:
parseVersion(dataIndex);
break;
// 3D-сцена
case 0x3D3D:
parse3DChunk(dataIndex, dataLength);
break;
// Анимация
case 0xB000:
parseAnimationChunk(dataIndex, dataLength);
break;
}
parseMainChunk(index + chunkLength, length - chunkLength);
}
}
private function parseVersion(index:uint):void {
data.position = index;
version = data.readUnsignedInt();
}
private function parse3DChunk(index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Материал
case 0xAFFF:
// Парсим материал
var material:MaterialData = new MaterialData();
parseMaterialChunk(material, dataIndex, dataLength);
break;
// Объект
case 0x4000:
// Создаём данные объекта
var object:ObjectData = new ObjectData();
var objectLength:uint = parseObject(object, dataIndex);
// Парсим объект
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
break;
}
parse3DChunk(index + chunkLength, length - chunkLength);
}
}
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Имя материала
case 0xA000:
parseMaterialName(material, dataIndex);
break;
// Ambient color
case 0xA010:
break;
// Diffuse color
case 0xA020:
data.position = dataIndex + 6;
material.color = ColorUtils.rgb(data.readUnsignedByte(), data.readUnsignedByte(), data.readUnsignedByte());
break;
// Specular color
case 0xA030:
break;
// Shininess percent
case 0xA040:
data.position = dataIndex + 6;
material.glossiness = data.readUnsignedShort();
break;
// Shininess strength percent
case 0xA041:
data.position = dataIndex + 6;
material.specular = data.readUnsignedShort();
break;
// Transperensy
case 0xA050:
data.position = dataIndex + 6;
material.transparency = data.readUnsignedShort();
break;
// Texture map 1
case 0xA200:
material.diffuseMap = new MapData();
parseMapChunk(material.diffuseMap, dataIndex, dataLength);
break;
// Texture map 2
case 0xA33A:
break;
// Opacity map
case 0xA210:
break;
// Bump map
case 0xA230:
material.normalMap = new MapData();
parseMapChunk(material.normalMap, dataIndex, dataLength);
break;
// Shininess map
case 0xA33C:
break;
// Specular map
case 0xA204:
break;
// Self-illumination map
case 0xA33D:
break;
// Reflection map
case 0xA220:
break;
}
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
}
}
private function parseMaterialName(material:MaterialData, index:uint):void {
// Создаём список материалов, если надо
if (materialDatas == null) {
materialDatas = new Array();
}
// Получаем название материала
material.name = getString(index);
// Помещаем данные материала в список
materialDatas[material.name] = material;
}
private function parseMapChunk(map:MapData, index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Имя файла
case 0xA300:
map.filename = getString(dataIndex).toLowerCase();
if (bitmaps == null) {
bitmaps = new Array();
}
bitmaps[map.filename] = null;
break;
// Масштаб по U
case 0xA354:
data.position = dataIndex;
map.scaleU = data.readFloat();
break;
// Масштаб по V
case 0xA356:
data.position = dataIndex;
map.scaleV = data.readFloat();
break;
// Смещение по U
case 0xA358:
data.position = dataIndex;
map.offsetU = data.readFloat();
break;
// Смещение по V
case 0xA35A:
data.position = dataIndex;
map.offsetV = data.readFloat();
break;
// Угол поворота
case 0xA35C:
data.position = dataIndex;
map.rotation = data.readFloat();
break;
}
parseMapChunk(map, index + chunkLength, length - chunkLength);
}
}
private function parseObject(object:ObjectData, index:uint):uint {
// Создаём список объектов, если надо
if (objectDatas == null) {
objectDatas = new Array();
}
// Получаем название объекта
object.name = getString(index);
// Помещаем данные объекта в список
objectDatas[object.name] = object;
return object.name.length + 1;
}
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Меш
case 0x4100:
parseMeshChunk(object, dataIndex, dataLength);
break;
// Источник света
case 0x4600:
break;
// Камера
case 0x4700:
break;
}
parseObjectChunk(object, index + chunkLength, length - chunkLength);
}
}
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Вершины
case 0x4110:
parseVertices(object, dataIndex);
break;
// UV
case 0x4140:
parseUVs(object, dataIndex);
break;
// Трансформация
case 0x4160:
parseMatrix(object, dataIndex);
break;
// Грани
case 0x4120:
var facesLength:uint = parseFaces(object, dataIndex);
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
break;
}
parseMeshChunk(object, index + chunkLength, length - chunkLength);
}
}
private function parseVertices(object:ObjectData, index:uint):void {
data.position = index;
var num:uint = data.readUnsignedShort();
object.vertices = new Array();
for (var i:uint = 0; i < num; i++) {
object.vertices.push(new Point3D(data.readFloat(), data.readFloat(), data.readFloat()));
}
}
private function parseUVs(object:ObjectData, index:uint):void {
data.position = index;
var num:uint = data.readUnsignedShort();
object.uvs = new Array();
for (var i:uint = 0; i < num; i++) {
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
}
}
private function parseMatrix(object:ObjectData, index:uint):void {
data.position = index;
object.matrix = new Matrix3D();
object.matrix.a = data.readFloat();
object.matrix.e = data.readFloat();
object.matrix.i = data.readFloat();
object.matrix.b = data.readFloat();
object.matrix.f = data.readFloat();
object.matrix.j = data.readFloat();
object.matrix.c = data.readFloat();
object.matrix.g = data.readFloat();
object.matrix.k = data.readFloat();
object.matrix.d = data.readFloat();
object.matrix.h = data.readFloat();
object.matrix.l = data.readFloat();
}
private function parseFaces(object:ObjectData, index:uint):uint {
data.position = index;
var num:uint = data.readUnsignedShort();
object.faces = new Array();
for (var i:uint = 0; i < num; i++) {
var face:FaceData = new FaceData();
face.a = data.readUnsignedShort();
face.b = data.readUnsignedShort();
face.c = data.readUnsignedShort();
object.faces.push(face);
data.position += 2; // Пропускаем флаг
}
return 2 + num*8;
}
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Поверхности
case 0x4130:
parseSurface(object, dataIndex);
break;
// Группы сглаживания
case 0x4150:
break;
}
parseFacesChunk(object, index + chunkLength, length - chunkLength);
}
}
private function parseSurface(object:ObjectData, index:uint):void {
// Создаём данные поверхности
var surface:SurfaceData = new SurfaceData();
// Создаём список поверхностей, если надо
if (object.surfaces == null) {
object.surfaces = new Array();
}
// Получаем название материала
surface.materialName = getString(index);
// Помещаем данные поверхности в список
object.surfaces[surface.materialName] = surface;
// Получаем грани поверхности
data.position = index + surface.materialName.length + 1;
var num:uint = data.readUnsignedShort();
surface.faces = new Array();
for (var i:uint = 0; i < num; i++) {
surface.faces.push(data.readUnsignedShort());
}
}
private function parseAnimationChunk(index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Анимация объекта
case 0xB001:
case 0xB002:
case 0xB003:
case 0xB004:
case 0xB005:
case 0xB006:
case 0xB007:
var animation:AnimationData = new AnimationData();
if (animationDatas == null) {
animationDatas = new Array();
}
animationDatas.push(animation);
parseObjectAnimationChunk(animation, dataIndex, dataLength);
break;
// Таймлайн
case 0xB008:
break;
}
parseAnimationChunk(index + chunkLength, length - chunkLength);
}
}
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
if (length > 6) {
data.position = index;
var chunkId:uint = data.readUnsignedShort();
var chunkLength:uint = data.readUnsignedInt();
var dataIndex:uint = index + 6;
var dataLength:uint = chunkLength - 6;
switch (chunkId) {
// Идентификация объекта и его связь
case 0xB010:
parseObjectAnimationInfo(animation, dataIndex);
break;
// Точка привязки объекта (pivot)
case 0xB013:
parseObjectAnimationPivot(animation, dataIndex);
break;
// Смещение объекта относительно родителя
case 0xB020:
parseObjectAnimationPosition(animation, dataIndex);
break;
// Поворот объекта относительно родителя (angle-axis)
case 0xB021:
parseObjectAnimationRotation(animation, dataIndex);
break;
// Масштабирование объекта относительно родителя
case 0xB022:
parseObjectAnimationScale(animation, dataIndex);
break;
}
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
}
}
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
var name:String = getString(index);
data.position = index + name.length + 1 + 4;
animation.objectName = name;
animation.parentIndex = data.readUnsignedShort();
}
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
data.position = index;
animation.pivot = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
}
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
data.position = index + 20;
animation.position = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
}
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
data.position = index + 20;
animation.rotation = getRotationFrom3DSAngleAxis(data.readFloat(), data.readFloat(), data.readFloat(), data.readFloat());
}
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
data.position = index + 20;
animation.scale = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
}
public function get content():Object3D {
return _content;
}
// Считываем строку заканчивающуюся на нулевой байт
public function getString(index:uint):String {
data.position = index;
var charCode:uint = data.readByte();
var res:String = "";
while (charCode != 0) {
res += String.fromCharCode(charCode);
charCode = data.readByte();
}
return res;
}
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Point3D {
var res:Point3D = new Point3D();
var s:Number = Math.sin(angle);
var c:Number = Math.cos(angle);
var t:Number = 1 - c;
var k:Number = x*y*t + z*s;
var half:Number;
if (k > 0.998) {
half = angle/2;
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
res.y = -Math.PI/2;
res.x = 0;
return res;
}
if (k < -0.998) {
half = angle/2;
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
res.y = Math.PI/2;
res.x = 0;
return res;
}
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
res.y = -Math.asin(x*y*t + z*s);
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
return res;
}
}
}
import alternativa.engine3d.core.Object3D;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import flash.geom.Matrix;
class MaterialData {
public var name:String;
public var color:uint;
public var specular:uint;
public var glossiness:uint;
public var transparency:uint;
public var diffuseMap:MapData;
public var normalMap:MapData;
public var matrix:Matrix;
}
class MapData {
public var filename:String;
public var scaleU:Number = 1;
public var scaleV:Number = 1;
public var offsetU:Number = 0;
public var offsetV:Number = 0;
public var rotation:Number = 0;
}
class ObjectData {
public var name:String;
public var vertices:Array;
public var uvs:Array;
public var matrix:Matrix3D;
public var faces:Array;
public var surfaces:Array;
}
class FaceData {
public var a:uint;
public var b:uint;
public var c:uint;
}
class SurfaceData {
public var materialName:String;
public var faces:Array;
}
class AnimationData {
public var objectName:String;
public var object:Object3D;
public var parentIndex:uint;
public var pivot:Point3D;
public var position:Point3D;
public var rotation:Point3D;
public var scale:Point3D;
}