mirror of
https://github.com/MapMakersAndProgrammers/Alternativa3D.git
synced 2025-10-26 09:59:10 -07:00
Test commit
This commit is contained in:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
.idea/*
|
||||
.idea/**/*
|
||||
.settings/*
|
||||
*.iml
|
||||
*.svn*
|
||||
*.DS_store*
|
||||
|
||||
# Build and Release Folders
|
||||
bin-debug/
|
||||
bin-release/
|
||||
target/
|
||||
out/
|
||||
|
||||
# Project property files
|
||||
.actionScriptProperties
|
||||
.flexProperties
|
||||
.flexLibProperties
|
||||
.settings/
|
||||
.project
|
||||
BIN
libs/A3DModelsBase-2.5.1.swc
Normal file
BIN
libs/A3DModelsBase-2.5.1.swc
Normal file
Binary file not shown.
BIN
libs/A3DModelsBase-2.5.2.swc
Normal file
BIN
libs/A3DModelsBase-2.5.2.swc
Normal file
Binary file not shown.
BIN
libs/AlternativaProtocol-2.53.0.swc
Normal file
BIN
libs/AlternativaProtocol-2.53.0.swc
Normal file
Binary file not shown.
BIN
libs/OSGIBase.swc
Normal file
BIN
libs/OSGIBase.swc
Normal file
Binary file not shown.
BIN
libs/ProtocolTypes.swc
Normal file
BIN
libs/ProtocolTypes.swc
Normal file
Binary file not shown.
BIN
libs/apparat-ersatz-1.0-RC9.swc
Normal file
BIN
libs/apparat-ersatz-1.0-RC9.swc
Normal file
Binary file not shown.
56
pom-standalone.xml
Normal file
56
pom-standalone.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>platform.clients.fp10.libraries</groupId>
|
||||
<artifactId>Alternativa3D</artifactId>
|
||||
<packaging>swc</packaging>
|
||||
<version>8.5.0.0-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>platform.clients.fp11.tools.maven</groupId>
|
||||
<artifactId>BasePom</artifactId>
|
||||
<version>2.11.1.0</version>
|
||||
</parent>
|
||||
<scm>
|
||||
<connection>
|
||||
scm:svn:https://svndev.alternativaplatform.com/platform/clients/fp11/libraries/Alternativa3D/trunk/
|
||||
</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>platform.client</groupId>
|
||||
<artifactId>A3DModelsBase</artifactId>
|
||||
<version>0.0.1.0</version>
|
||||
<type>swc</type>
|
||||
<scope>merged</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>platform.clients.fp10</groupId>
|
||||
<artifactId>OSGiBase</artifactId>
|
||||
<version>2.0.2.0</version>
|
||||
<type>swc</type>
|
||||
<scope>merged</scope>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>platform.clients.fp10.libraries</groupId>-->
|
||||
<!--<artifactId>AlternativaOSGi</artifactId>-->
|
||||
<!--<version>2.0.14.0</version>-->
|
||||
<!--<type>swc</type>-->
|
||||
<!--<scope>merged</scope>-->
|
||||
<!--</dependency>-->
|
||||
<dependency>
|
||||
<groupId>platform.clients.fp10.libraries</groupId>
|
||||
<artifactId>AlternativaProtocol</artifactId>
|
||||
<version>2.0.15.0</version>
|
||||
<type>swc</type>
|
||||
<scope>merged</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>platform.clients.fp10.libraries</groupId>
|
||||
<artifactId>ProtocolTypes</artifactId>
|
||||
<version>1.0.1.0</version>
|
||||
<type>swc</type>
|
||||
<scope>merged</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
57
pom.xml
Normal file
57
pom.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>platform.clients.fp11.libraries</groupId>
|
||||
<artifactId>Alternativa3D</artifactId>
|
||||
<packaging>swc</packaging>
|
||||
<version>8.28.0-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>platform.clients.fp11.tools.maven</groupId>
|
||||
<artifactId>BasePom</artifactId>
|
||||
<version>2.58.0</version>
|
||||
</parent>
|
||||
<scm>
|
||||
<connection>
|
||||
scm:svn:https://svndev.alternativaplatform.com/platform/clients/fp11/libraries/Alternativa3D/trunk/
|
||||
</connection>
|
||||
</scm>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>platform.client.formats</groupId>
|
||||
<artifactId>A3DModelsBase</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<type>swc</type>
|
||||
<scope>external</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>platform.clients.fp10.libraries</groupId>
|
||||
<artifactId>AlternativaProtocol</artifactId>
|
||||
<version>2.53.0</version>
|
||||
<type>swc</type>
|
||||
<scope>external</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>platform.client.formats</groupId>
|
||||
<artifactId>A3DModelsBase</artifactId>
|
||||
<type>swc</type>
|
||||
<scope>external</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>platform.clients.fp10</groupId>
|
||||
<artifactId>OSGiBase</artifactId>
|
||||
<type>swc</type>
|
||||
<scope>external</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>platform.clients.fp10.libraries</groupId>
|
||||
<artifactId>AlternativaProtocol</artifactId>
|
||||
<type>swc</type>
|
||||
<scope>external</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
24
src/alternativa/Alternativa3D.as
Normal file
24
src/alternativa/Alternativa3D.as
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa {
|
||||
|
||||
/**
|
||||
* Class contains information about version of a library.
|
||||
* Also used for integration of a library to development tool of Adobe Flash.
|
||||
*/
|
||||
public class Alternativa3D {
|
||||
|
||||
/**
|
||||
* Library version in the format: generation.feature-version.fix-version.
|
||||
*/
|
||||
public static const version:String = "8.27.0";
|
||||
}
|
||||
}
|
||||
13
src/alternativa/engine3d/alternativa3d.as
Normal file
13
src/alternativa/engine3d/alternativa3d.as
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
|
||||
}
|
||||
495
src/alternativa/engine3d/animation/AnimationClip.as
Normal file
495
src/alternativa/engine3d/animation/AnimationClip.as
Normal file
@@ -0,0 +1,495 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.keys.Track;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
*
|
||||
* Plays complex animation which consists of a set of animation tracks.
|
||||
* Every animated property of every model's element presented by separated track.
|
||||
* Track is somewhat similar to separate animated layer in Flash, but the layer stores animation of all properties for some element at once.
|
||||
* In opposite, track stores animation for every property (for an example, separate track for a scale and separate track for a coordinates).
|
||||
* Track also can contain keyframes in arbitrary positions. Frames, contained between keyframes, are linearly interpolated
|
||||
* (i.e. behave themselves like a timeline frames in flash, for which motion twin was created ).
|
||||
* Animation clip connects each track with a specific object.
|
||||
* Animation clip stores information about animation for the whole model, i.e. for any element state at any time moment.
|
||||
* Animation works handled by <code>AnimationController</code>.
|
||||
*
|
||||
* @see alternativa.engine3d.animation.keys.Track
|
||||
* @see alternativa.engine3d.animation.keys.TransformTrack
|
||||
* @see alternativa.engine3d.animation.keys.NumberTrack
|
||||
* @see alternativa.engine3d.animation.AnimationController
|
||||
*/
|
||||
public class AnimationClip extends AnimationNode {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _objects:Array;
|
||||
|
||||
/**
|
||||
* Name of the animation clip.
|
||||
*/
|
||||
public var name:String;
|
||||
|
||||
/**
|
||||
* Defines if animation should be repeated.
|
||||
*/
|
||||
public var loop:Boolean = true;
|
||||
|
||||
/**
|
||||
* Length of animation in seconds. If length of any animation track is changed, updateLength()
|
||||
* method should be called to recalculate the length of the clip.
|
||||
*
|
||||
* @see #updateLength()
|
||||
*/
|
||||
public var length:Number = 0;
|
||||
/**
|
||||
* Handles the active animation execution. Plays animation if value is true.
|
||||
*
|
||||
* @see AnimationNode#isActive
|
||||
*/
|
||||
public var animated:Boolean = true;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Current value of time.
|
||||
*/
|
||||
private var _time:Number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _numTracks:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _tracks:Vector.<Track> = new Vector.<Track>();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _notifiersList:AnimationNotify;
|
||||
|
||||
/**
|
||||
* Creates a AnimationClip object.
|
||||
*
|
||||
* @param name name of the clip
|
||||
*/
|
||||
public function AnimationClip(name:String = null) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of animated objects. Animation tracks are bound to the objects by object names.
|
||||
*
|
||||
* @see Track#object
|
||||
*/
|
||||
public function get objects():Array {
|
||||
return (_objects == null) ? null : [].concat(_objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set objects(value:Array):void {
|
||||
updateObjects(_objects, controller, value, controller);
|
||||
_objects = (value == null) ? null : [].concat(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function setController(value:AnimationController):void {
|
||||
updateObjects(_objects, controller, _objects, value);
|
||||
this.controller = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function addObject(object:Object):void {
|
||||
if (_objects == null) {
|
||||
_objects = [object];
|
||||
} else {
|
||||
_objects.push(object);
|
||||
}
|
||||
if (controller != null) {
|
||||
controller.addObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function updateObjects(oldObjects:Array, oldController:AnimationController, newObjects:Array, newController:AnimationController):void {
|
||||
var i:int, count:int;
|
||||
if (oldController != null && oldObjects != null) {
|
||||
for (i = 0, count = _objects.length; i < count; i++) {
|
||||
oldController.removeObject(oldObjects[i]);
|
||||
}
|
||||
}
|
||||
if (newController != null && newObjects != null) {
|
||||
for (i = 0, count = newObjects.length; i < count; i++) {
|
||||
newController.addObject(newObjects[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the length of the clip in order to match with length of longest track.
|
||||
* Should be called after track was changed.
|
||||
*/
|
||||
public function updateLength():void {
|
||||
for (var i:int = 0; i < _numTracks; i++) {
|
||||
var track:Track = _tracks[i];
|
||||
var len:Number = track.length;
|
||||
if (len > length) {
|
||||
length = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new track to the animation clip.
|
||||
* The total length of the clip is recalculated automatically.
|
||||
*
|
||||
* @param track track which should be added.
|
||||
* @return added track.
|
||||
*
|
||||
* @see #length
|
||||
*/
|
||||
public function addTrack(track:Track):Track {
|
||||
if (track == null) {
|
||||
throw new Error("Track can not be null");
|
||||
}
|
||||
_tracks[_numTracks++] = track;
|
||||
if (track.length > length) {
|
||||
length = track.length;
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified track from the clip. The clip length is automatically recalculated.
|
||||
*
|
||||
* @param track track which should be removed.
|
||||
* @return removed track.
|
||||
*
|
||||
* @see #length
|
||||
* @throw Error if the AnimationClip does not include the track.
|
||||
*/
|
||||
public function removeTrack(track:Track):Track {
|
||||
var index:int = _tracks.indexOf(track);
|
||||
if (index < 0) throw new ArgumentError("Track not found");
|
||||
_numTracks--;
|
||||
var j:int = index + 1;
|
||||
while (index < _numTracks) {
|
||||
_tracks[index] = _tracks[j];
|
||||
index++;
|
||||
j++;
|
||||
}
|
||||
_tracks.length = _numTracks;
|
||||
length = 0;
|
||||
for (var i:int = 0; i < _numTracks; i++) {
|
||||
var t:Track = _tracks[i];
|
||||
if (t.length > length) {
|
||||
length = t.length;
|
||||
}
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the track object instance that exists at the specified index.
|
||||
*
|
||||
* @param index index.
|
||||
* @return the track object instance that exists at the specified index.
|
||||
*/
|
||||
public function getTrackAt(index:int):Track {
|
||||
return _tracks[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of tracks in the AnimationClip.
|
||||
*/
|
||||
public function get numTracks():int {
|
||||
return _numTracks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function update(interval:Number, weight:Number):void {
|
||||
var oldTime:Number = _time;
|
||||
if (animated) {
|
||||
_time += interval*speed;
|
||||
if (loop) {
|
||||
if (_time < 0) {
|
||||
// _position = (length <= 0) ? 0 : _position % length;
|
||||
_time = 0;
|
||||
} else {
|
||||
if (_time >= length) {
|
||||
collectNotifiers(oldTime, length);
|
||||
_time = (length <= 0) ? 0 : _time % length;
|
||||
collectNotifiers(0, _time);
|
||||
} else {
|
||||
collectNotifiers(oldTime, _time);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_time < 0) {
|
||||
_time = 0;
|
||||
} else if (_time >= length) {
|
||||
_time = length;
|
||||
}
|
||||
collectNotifiers(oldTime, _time);
|
||||
}
|
||||
}
|
||||
if (weight > 0) {
|
||||
for (var i:int = 0; i < _numTracks; i++) {
|
||||
var track:Track = _tracks[i];
|
||||
if (track.object != null) {
|
||||
var state:AnimationState = controller.getState(track.object);
|
||||
if (state != null) {
|
||||
track.blend(_time, weight, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Current time of animation.
|
||||
*/
|
||||
public function get time():Number {
|
||||
return _time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set time(value:Number):void {
|
||||
_time = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current normalized time in the interval [0, 1].
|
||||
*/
|
||||
public function get normalizedTime():Number {
|
||||
return (length == 0) ? 0 : _time/length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set normalizedTime(value:Number):void {
|
||||
_time = value*length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function getNumChildren(object:Object):int {
|
||||
if (object is Object3D) {
|
||||
return Object3D(object).numChildren;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function getChildAt(object:Object, index:int):Object {
|
||||
if (object is Object3D) {
|
||||
return Object3D(object).getChildAt(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function addChildren(object:Object):void {
|
||||
for (var i:int = 0, numChildren:int = getNumChildren(object); i < numChildren; i++) {
|
||||
var child:Object = getChildAt(object, i);
|
||||
addObject(child);
|
||||
addChildren(child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds tracks from the animation clip to given object. Only those tracks which have object property equal to the object's name are bound.
|
||||
*
|
||||
* @param object The object to which tracks are bound.
|
||||
* @param includeDescendants If true, the whole tree of the object's children (if any) is processed.
|
||||
*
|
||||
* @see #objects
|
||||
* @see alternativa.engine3d.animation.keys.Track#object
|
||||
*/
|
||||
public function attach(object:Object, includeDescendants:Boolean):void {
|
||||
updateObjects(_objects, controller, null, controller);
|
||||
_objects = null;
|
||||
addObject(object);
|
||||
if (includeDescendants) {
|
||||
addChildren(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function collectNotifiers(start:Number, end:Number):void {
|
||||
var notify:AnimationNotify = _notifiersList;
|
||||
while (notify != null) {
|
||||
if (notify._time > start) {
|
||||
if (notify._time > end) {
|
||||
notify = notify.next;
|
||||
continue;
|
||||
}
|
||||
notify.processNext = controller.nearestNotifyers;
|
||||
controller.nearestNotifyers = notify;
|
||||
}
|
||||
notify = notify.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AnimationNotify instance which is capable of firing notification events when playback reaches the specified time on the time line.
|
||||
*
|
||||
* @param time The time in seconds to which the notification trigger will be bound.
|
||||
* @param name The name of AnimationNotify instance.
|
||||
*
|
||||
* @return A new instance of AnimationNotify class bound to specified time counting from start of the time line.
|
||||
*
|
||||
* @see AnimationNotify
|
||||
*/
|
||||
public function addNotify(time:Number, name:String = null):AnimationNotify {
|
||||
time = (time <= 0) ? 0 : ((time >= length) ? length : time);
|
||||
var notify:AnimationNotify = new AnimationNotify(name);
|
||||
notify._time = time;
|
||||
if (_notifiersList == null) {
|
||||
_notifiersList = notify;
|
||||
return notify;
|
||||
} else {
|
||||
if (_notifiersList._time > time) {
|
||||
// Replaces the first key
|
||||
notify.next = _notifiersList;
|
||||
_notifiersList = notify;
|
||||
return notify;
|
||||
} else {
|
||||
// Search for appropriate place
|
||||
var n:AnimationNotify = _notifiersList;
|
||||
while (n.next != null && n.next._time <= time) {
|
||||
n = n.next;
|
||||
}
|
||||
if (n.next == null) {
|
||||
// Places at the end
|
||||
n.next = notify;
|
||||
} else {
|
||||
notify.next = n.next;
|
||||
n.next = notify;
|
||||
}
|
||||
}
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AnimationNotify instance which is capable of firing notification events when playback reaches
|
||||
* the specified time on the time line. The time is specified as an offset from the end of time line towards its start.
|
||||
*
|
||||
* @param offsetFromEnd The offset in seconds from the end of the time line towards its start, where the event object will be set in.
|
||||
* @param name The name of notification trigger.
|
||||
*
|
||||
* @return A new instance of AnimationNotify class bound to specified time.
|
||||
*
|
||||
* @see AnimationNotify
|
||||
*/
|
||||
// TODO: name of method (addNotifyAtEnd) is incomprehensible. Rename to addNotifyFromFinish (or something else).
|
||||
public function addNotifyAtEnd(offsetFromEnd:Number = 0, name:String = null):AnimationNotify {
|
||||
return addNotify(length - offsetFromEnd, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes specified notification trigger.
|
||||
*
|
||||
* @param notify The notification trigger to remove.
|
||||
* @return The removed notification trigger.
|
||||
*/
|
||||
public function removeNotify(notify:AnimationNotify):AnimationNotify {
|
||||
if (_notifiersList != null) {
|
||||
if (_notifiersList == notify) {
|
||||
_notifiersList = _notifiersList.next;
|
||||
return notify;
|
||||
}
|
||||
var n:AnimationNotify = _notifiersList;
|
||||
while (n.next != null && n.next != notify) {
|
||||
n = n.next;
|
||||
}
|
||||
if (n.next == notify) {
|
||||
// removes
|
||||
n.next = notify.next;
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
throw new Error("Notify not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of notification triggers.
|
||||
*/
|
||||
public function get notifiers():Vector.<AnimationNotify> {
|
||||
var result:Vector.<AnimationNotify> = new Vector.<AnimationNotify>();
|
||||
var i:int = 0;
|
||||
for (var notify:AnimationNotify = _notifiersList; notify != null; notify = notify.next) {
|
||||
result[i] = notify;
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fragment of the clip between specified bounds.
|
||||
*
|
||||
* @param start The start time of a fragment in seconds.
|
||||
* @param end The end time of a fragment in seconds.
|
||||
* @return The clip fragment.
|
||||
*/
|
||||
public function slice(start:Number, end:Number = Number.MAX_VALUE):AnimationClip {
|
||||
var sliced:AnimationClip = new AnimationClip(name);
|
||||
sliced._objects = (_objects == null) ? null : [].concat(_objects);
|
||||
for (var i:int = 0; i < _numTracks; i++) {
|
||||
sliced.addTrack(_tracks[i].slice(start, end));
|
||||
}
|
||||
return sliced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the clip. Both the clone and the original reference the same tracks.
|
||||
*/
|
||||
public function clone():AnimationClip {
|
||||
var cloned:AnimationClip = new AnimationClip(name);
|
||||
cloned._objects = (_objects == null) ? null : [].concat(_objects);
|
||||
for (var i:int = 0; i < _numTracks; i++) {
|
||||
cloned.addTrack(_tracks[i]);
|
||||
}
|
||||
cloned.length = length;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
220
src/alternativa/engine3d/animation/AnimationController.as
Normal file
220
src/alternativa/engine3d/animation/AnimationController.as
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.events.NotifyEvent;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
/**
|
||||
* Controls animation playback and blending. I.e. it animates model using information
|
||||
* stored in <code>AnimationClip</code>-s and generated by <code>AnimationSwitcher</code>
|
||||
* and <code>AnimationCouple</code> blenders.
|
||||
* You have to call method <code>update()</code> each frame,
|
||||
* which refreshes all child animation clips and blenders, which return
|
||||
* list of properties and values to controller after that. You can use this list
|
||||
* to set those properties. Controller sets those values and as a result
|
||||
* the animation goes on. Animation control is carried out with the
|
||||
* help of animated flag, and with <code>AnimationSwitcher</code> blender,
|
||||
* which can transfer clip from active state to passive and vice versa.
|
||||
*
|
||||
*
|
||||
* @see alternativa.engine3d.animation.AnimationClip
|
||||
* @see alternativa.engine3d.animation.AnimationCouple
|
||||
* @see alternativa.engine3d.animation.AnimationSwitcher
|
||||
*/
|
||||
public class AnimationController {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _root:AnimationNode;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _objects:Vector.<Object>;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _object3ds:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var objectsUsedCount:Dictionary = new Dictionary();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var states:Object = new Object();
|
||||
// private var datasList:BlendedData;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var lastTime:int = -1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var nearestNotifyers:AnimationNotify;
|
||||
|
||||
/**
|
||||
* Creates a AnimationController object.
|
||||
*/
|
||||
public function AnimationController() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Root of the animation tree.
|
||||
*/
|
||||
public function get root():AnimationNode {
|
||||
return _root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set root(value:AnimationNode):void {
|
||||
if (_root != value) {
|
||||
if (_root != null) {
|
||||
_root.setController(null);
|
||||
_root._isActive = false;
|
||||
}
|
||||
if (value != null) {
|
||||
value.setController(this);
|
||||
value._isActive = true;
|
||||
}
|
||||
this._root = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays animations on the time interval passed since the last <code>update()</code> call.
|
||||
* If <code>freeze()</code> method was called after the last <code>update(),</code>
|
||||
* animation will continue from that moment.
|
||||
*/
|
||||
public function update():void {
|
||||
var interval:Number;
|
||||
if (lastTime < 0) {
|
||||
lastTime = getTimer();
|
||||
interval = 0;
|
||||
} else {
|
||||
var time:int = getTimer();
|
||||
interval = 0.001*(time - lastTime);
|
||||
lastTime = time;
|
||||
}
|
||||
if (_root == null) {
|
||||
return;
|
||||
}
|
||||
var data:AnimationState;
|
||||
// Cleaning
|
||||
for each (data in states) {
|
||||
data.reset();
|
||||
}
|
||||
_root.update(interval, 1);
|
||||
// Apply the animation
|
||||
for (var i:int = 0, count:int = _object3ds.length; i < count; i++) {
|
||||
var object:Object3D = _object3ds[i];
|
||||
data = states[object.name];
|
||||
if (data != null) {
|
||||
data.apply(object);
|
||||
}
|
||||
}
|
||||
// Calls the notifications
|
||||
for (var notify:AnimationNotify = nearestNotifyers; notify != null; notify = notify.processNext) {
|
||||
if (notify.willTrigger(NotifyEvent.NOTIFY)) {
|
||||
notify.dispatchEvent(new NotifyEvent(notify));
|
||||
}
|
||||
}
|
||||
nearestNotifyers = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function addObject(object:Object):void {
|
||||
if (object in objectsUsedCount) {
|
||||
objectsUsedCount[object]++;
|
||||
} else {
|
||||
if (object is Object3D) {
|
||||
_object3ds.push(object);
|
||||
} else {
|
||||
_objects.push(object);
|
||||
}
|
||||
objectsUsedCount[object] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function removeObject(object:Object):void {
|
||||
var used:int = objectsUsedCount[object];
|
||||
used--;
|
||||
if (used <= 0) {
|
||||
var index:int;
|
||||
var j:int;
|
||||
var count:int;
|
||||
if (object is Object3D) {
|
||||
index = _object3ds.indexOf(object);
|
||||
count = _object3ds.length - 1;
|
||||
j = index + 1;
|
||||
while (index < count) {
|
||||
_object3ds[index] = _object3ds[j];
|
||||
index++;
|
||||
j++;
|
||||
}
|
||||
_object3ds.length = count;
|
||||
} else {
|
||||
index = _objects.indexOf(object);
|
||||
count = _objects.length - 1;
|
||||
j = index + 1;
|
||||
while (index < count) {
|
||||
_objects[index] = _objects[j];
|
||||
index++;
|
||||
j++;
|
||||
}
|
||||
_objects.length = count;
|
||||
}
|
||||
delete objectsUsedCount[object];
|
||||
} else {
|
||||
objectsUsedCount[object] = used;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function getState(name:String):AnimationState {
|
||||
var state:AnimationState = states[name];
|
||||
if (state == null) {
|
||||
state = new AnimationState();
|
||||
states[name] = state;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes internal time counter till the next <code>update()<code> call.
|
||||
*
|
||||
* @see #update
|
||||
*/
|
||||
public function freeze():void {
|
||||
lastTime = -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
138
src/alternativa/engine3d/animation/AnimationCouple.as
Normal file
138
src/alternativa/engine3d/animation/AnimationCouple.as
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Blends two animations according to the balance value.
|
||||
* Mixes two animations with the given percentage. Any of <code>AnimationClip</code>,
|
||||
* <code>AnimationSwitcher</code>, <code>AnimationCouple</code> classes can be blended.
|
||||
*/
|
||||
public class AnimationCouple extends AnimationNode {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _left:AnimationNode;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _right:AnimationNode;
|
||||
|
||||
/**
|
||||
* The balance is a value in [0, 1] interval which specifies weight coefficient for each animation.
|
||||
* The first (left) animation gets weight of (1 - balance) and the second (right) one gets weigth of balance.
|
||||
*/
|
||||
public var balance:Number = 0.5;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function update(elapsed:Number, weight:Number):void {
|
||||
var w:Number = (balance <= 0) ? 0 : ((balance >= 1) ? 1 : balance);
|
||||
if (_left == null) {
|
||||
_right.update(elapsed*speed, weight);
|
||||
} else if (_right == null) {
|
||||
_left.update(elapsed*speed, weight);
|
||||
} else {
|
||||
_left.update(elapsed*speed, (1 - w)*weight);
|
||||
_right.update(elapsed*speed, w*weight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function setController(value:AnimationController):void {
|
||||
this.controller = value;
|
||||
if (_left != null) {
|
||||
_left.setController(value);
|
||||
}
|
||||
if (_right != null) {
|
||||
_right.setController(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function addNode(node:AnimationNode):void {
|
||||
super.addNode(node);
|
||||
node._isActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function removeNode(node:AnimationNode):void {
|
||||
if (_left == node) {
|
||||
_left = null;
|
||||
} else {
|
||||
_right = null;
|
||||
}
|
||||
super.removeNode(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* The first animation.
|
||||
*/
|
||||
public function get left():AnimationNode {
|
||||
return _left;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set left(value:AnimationNode):void {
|
||||
if (value != _left) {
|
||||
if (value._parent == this) {
|
||||
throw new Error("Animation already exists in blender");
|
||||
}
|
||||
if (_left != null) {
|
||||
removeNode(_left);
|
||||
}
|
||||
_left = value;
|
||||
if (value != null) {
|
||||
addNode(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The second animation.
|
||||
*/
|
||||
public function get right():AnimationNode {
|
||||
return _right;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set right(value:AnimationNode):void {
|
||||
if (value != _right) {
|
||||
if (value._parent == this) {
|
||||
throw new Error("Animation already exists in blender");
|
||||
}
|
||||
if (_right != null) {
|
||||
removeNode(_right);
|
||||
}
|
||||
_right = value;
|
||||
if (value != null) {
|
||||
addNode(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
96
src/alternativa/engine3d/animation/AnimationNode.as
Normal file
96
src/alternativa/engine3d/animation/AnimationNode.as
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Animation tree node. Animation in Alternativa3D is built over the blend tree.
|
||||
* This tree is intended for combining a set of animations and keeping unambiguous status
|
||||
* of each property being animated in every frame. E.g. there can be independent animations
|
||||
* for legs and hands, that will be presented by the nodes of blend tree. With the help of blenders,
|
||||
* derived from <code>AnimationNode</code> you can change or blend nodes of blend tree. Every tree animation
|
||||
* is controlled by <code>AnimationController</code>. <code>AnimationNode</code> instance have to be a root element of the tree.
|
||||
*
|
||||
*/
|
||||
public class AnimationNode {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _isActive:Boolean = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _parent:AnimationNode;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var controller:AnimationController;
|
||||
|
||||
/**
|
||||
* Animation speed.
|
||||
*/
|
||||
public var speed:Number = 1;
|
||||
|
||||
/**
|
||||
* Determines if the animation is active.
|
||||
*/
|
||||
public function get isActive():Boolean {
|
||||
return _isActive && controller != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent of this node in animation tree hierarchy.
|
||||
*/
|
||||
public function get parent():AnimationNode {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function update(elapsed:Number, weight:Number):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function setController(value:AnimationController):void {
|
||||
this.controller = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function addNode(node:AnimationNode):void {
|
||||
if (node._parent != null) {
|
||||
node._parent.removeNode(node);
|
||||
}
|
||||
node._parent = this;
|
||||
node.setController(controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function removeNode(node:AnimationNode):void {
|
||||
node.setController(null);
|
||||
node._isActive = false;
|
||||
node._parent = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
78
src/alternativa/engine3d/animation/AnimationNotify.as
Normal file
78
src/alternativa/engine3d/animation/AnimationNotify.as
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.events.EventDispatcher;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* The notification trigger bound to certain time on an animation time line.
|
||||
* <code>AnimationNotify</code> instance subscribes to <code>NotifyEvent.<NOTIFY /code>When animation
|
||||
* playback reaches the given time, an event is dispatched by the trigger.
|
||||
*
|
||||
*<code>
|
||||
* animationClip.addNotify(30).addEventListener(NotifyEvent.NOTIFY, notifyHandler)
|
||||
* …
|
||||
* private function notifyHandler(e:NotifyEvent):void{
|
||||
* trace("Animation time is " + e.notify.time + " seconds now")
|
||||
*}</code>
|
||||
*
|
||||
*
|
||||
* @see AnimationClip#addNotify()
|
||||
* @see AnimationClip#addNotifyAtEnd()
|
||||
*/
|
||||
public class AnimationNotify extends EventDispatcher {
|
||||
|
||||
/**
|
||||
* The name of notification trigger.
|
||||
*/
|
||||
public var name:String;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _time:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var next:AnimationNotify;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var updateTime:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var processNext:AnimationNotify;
|
||||
|
||||
/**
|
||||
* A new instance should not be created directly. Instead, use <code>AnimationClip.addNotify()</code> or <code>AnimationClip.addNotifyAtEnd()</code> methods.
|
||||
*
|
||||
* @see AnimationClip#addNotify()
|
||||
* @see AnimationClip#addNotifyAtEnd()
|
||||
*/
|
||||
public function AnimationNotify(name:String) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time in seconds on the time line to which the trigger is bound.
|
||||
*/
|
||||
public function get time():Number {
|
||||
return _time;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
262
src/alternativa/engine3d/animation/AnimationState.as
Normal file
262
src/alternativa/engine3d/animation/AnimationState.as
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.keys.TransformKey;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class AnimationState {
|
||||
|
||||
public var useCount:int = 0;
|
||||
|
||||
public var transform:TransformKey = new TransformKey();
|
||||
public var transformWeightSum:Number = 0;
|
||||
|
||||
public var numbers:Object = new Object();
|
||||
public var numberWeightSums:Object = new Object();
|
||||
|
||||
|
||||
public function AnimationState() {
|
||||
}
|
||||
|
||||
public function reset():void {
|
||||
transformWeightSum = 0;
|
||||
for (var key:String in numbers) {
|
||||
delete numbers[key];
|
||||
delete numberWeightSums[key];
|
||||
}
|
||||
}
|
||||
|
||||
public function addWeightedTransform(key:TransformKey, weight:Number):void {
|
||||
transformWeightSum += weight;
|
||||
transform.interpolate(transform, key, weight/transformWeightSum);
|
||||
}
|
||||
|
||||
public function addWeightedNumber(property:String, value:Number, weight:Number):void {
|
||||
var sum:Number = numberWeightSums[property];
|
||||
if (sum == sum) {
|
||||
sum += weight;
|
||||
weight /= sum;
|
||||
var current:Number = numbers[property];
|
||||
numbers[property] = (1 - weight)*current + weight*value;
|
||||
numberWeightSums[property] = sum;
|
||||
} else {
|
||||
numbers[property] = value;
|
||||
numberWeightSums[property] = weight;
|
||||
}
|
||||
}
|
||||
|
||||
public function apply(object:Object3D):void {
|
||||
if (transformWeightSum > 0) {
|
||||
object._x = transform.x;
|
||||
object._y = transform.y;
|
||||
object._z = transform.z;
|
||||
setEulerAngles(transform.rotation, object);
|
||||
object._scaleX = transform.scaleX;
|
||||
object._scaleY = transform.scaleY;
|
||||
object._scaleZ = transform.scaleZ;
|
||||
object.transformChanged = true;
|
||||
}
|
||||
|
||||
var sum:Number, weight:Number;
|
||||
for (var key:String in numbers) {
|
||||
switch (key) {
|
||||
case 'x':
|
||||
sum = numberWeightSums['x'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.x = (1 - weight)*object.x + weight*numbers['x'];
|
||||
break;
|
||||
case 'y':
|
||||
sum = numberWeightSums['y'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.y = (1 - weight)*object.y + weight*numbers['y'];
|
||||
break;
|
||||
case 'z':
|
||||
sum = numberWeightSums['z'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.z = (1 - weight)*object.z + weight*numbers['z'];
|
||||
break;
|
||||
case 'rotationX':
|
||||
sum = numberWeightSums['rotationX'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.rotationX = (1 - weight)*object.rotationX + weight*numbers['rotationX'];
|
||||
break;
|
||||
case 'rotationY':
|
||||
sum = numberWeightSums['rotationY'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.rotationY = (1 - weight)*object.rotationY + weight*numbers['rotationY'];
|
||||
break;
|
||||
case 'rotationZ':
|
||||
sum = numberWeightSums['rotationZ'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.rotationZ = (1 - weight)*object.rotationZ + weight*numbers['rotationZ'];
|
||||
break;
|
||||
case 'scaleX':
|
||||
sum = numberWeightSums['scaleX'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.scaleX = (1 - weight)*object.scaleX + weight*numbers['scaleX'];
|
||||
break;
|
||||
case 'scaleY':
|
||||
sum = numberWeightSums['scaleY'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.scaleY = (1 - weight)*object.scaleY + weight*numbers['scaleY'];
|
||||
break;
|
||||
case 'scaleZ':
|
||||
sum = numberWeightSums['scaleZ'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.scaleZ = (1 - weight)*object.scaleZ + weight*numbers['scaleZ'];
|
||||
break;
|
||||
default :
|
||||
object[key] = numbers[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function applyObject(object:Object):void {
|
||||
if (transformWeightSum > 0) {
|
||||
object.x = transform.x;
|
||||
object.y = transform.y;
|
||||
object.z = transform.z;
|
||||
setEulerAnglesObject(transform.rotation, object);
|
||||
object.scaleX = transform.scaleX;
|
||||
object.scaleY = transform.scaleY;
|
||||
object.scaleZ = transform.scaleZ;
|
||||
}
|
||||
|
||||
var sum:Number, weight:Number;
|
||||
for (var key:String in numbers) {
|
||||
switch (key) {
|
||||
case 'x':
|
||||
sum = numberWeightSums['x'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.x = (1 - weight)*object.x + weight*numbers['x'];
|
||||
break;
|
||||
case 'y':
|
||||
sum = numberWeightSums['y'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.y = (1 - weight)*object.y + weight*numbers['y'];
|
||||
break;
|
||||
case 'z':
|
||||
sum = numberWeightSums['z'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.z = (1 - weight)*object.z + weight*numbers['z'];
|
||||
break;
|
||||
case 'rotationX':
|
||||
sum = numberWeightSums['rotationX'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.rotationX = (1 - weight)*object.rotationX + weight*numbers['rotationX'];
|
||||
break;
|
||||
case 'rotationY':
|
||||
sum = numberWeightSums['rotationY'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.rotationY = (1 - weight)*object.rotationY + weight*numbers['rotationY'];
|
||||
break;
|
||||
case 'rotationZ':
|
||||
sum = numberWeightSums['rotationZ'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.rotationZ = (1 - weight)*object.rotationZ + weight*numbers['rotationZ'];
|
||||
break;
|
||||
case 'scaleX':
|
||||
sum = numberWeightSums['scaleX'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.scaleX = (1 - weight)*object.scaleX + weight*numbers['scaleX'];
|
||||
break;
|
||||
case 'scaleY':
|
||||
sum = numberWeightSums['scaleY'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.scaleY = (1 - weight)*object.scaleY + weight*numbers['scaleY'];
|
||||
break;
|
||||
case 'scaleZ':
|
||||
sum = numberWeightSums['scaleZ'];
|
||||
weight = sum/(sum + transformWeightSum);
|
||||
object.scaleZ = (1 - weight)*object.scaleZ + weight*numbers['scaleZ'];
|
||||
break;
|
||||
default :
|
||||
object[key] = numbers[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setEulerAngles(quat:Vector3D, object:Object3D):void {
|
||||
var qi2:Number = 2*quat.x*quat.x;
|
||||
var qj2:Number = 2*quat.y*quat.y;
|
||||
var qk2:Number = 2*quat.z*quat.z;
|
||||
var qij:Number = 2*quat.x*quat.y;
|
||||
var qjk:Number = 2*quat.y*quat.z;
|
||||
var qki:Number = 2*quat.z*quat.x;
|
||||
var qri:Number = 2*quat.w*quat.x;
|
||||
var qrj:Number = 2*quat.w*quat.y;
|
||||
var qrk:Number = 2*quat.w*quat.z;
|
||||
|
||||
var aa:Number = 1 - qj2 - qk2;
|
||||
var bb:Number = qij - qrk;
|
||||
var ee:Number = qij + qrk;
|
||||
var ff:Number = 1 - qi2 - qk2;
|
||||
var ii:Number = qki - qrj;
|
||||
var jj:Number = qjk + qri;
|
||||
var kk:Number = 1 - qi2 - qj2;
|
||||
|
||||
if (-1 < ii && ii < 1) {
|
||||
object._rotationX = Math.atan2(jj, kk);
|
||||
object._rotationY = -Math.asin(ii);
|
||||
object._rotationZ = Math.atan2(ee, aa);
|
||||
} else {
|
||||
object._rotationX = 0;
|
||||
object._rotationY = (ii <= -1) ? Math.PI : -Math.PI;
|
||||
object._rotationY *= 0.5;
|
||||
object._rotationZ = Math.atan2(-bb, ff);
|
||||
}
|
||||
}
|
||||
|
||||
private function setEulerAnglesObject(quat:Vector3D, object:Object):void {
|
||||
var qi2:Number = 2*quat.x*quat.x;
|
||||
var qj2:Number = 2*quat.y*quat.y;
|
||||
var qk2:Number = 2*quat.z*quat.z;
|
||||
var qij:Number = 2*quat.x*quat.y;
|
||||
var qjk:Number = 2*quat.y*quat.z;
|
||||
var qki:Number = 2*quat.z*quat.x;
|
||||
var qri:Number = 2*quat.w*quat.x;
|
||||
var qrj:Number = 2*quat.w*quat.y;
|
||||
var qrk:Number = 2*quat.w*quat.z;
|
||||
|
||||
var aa:Number = 1 - qj2 - qk2;
|
||||
var bb:Number = qij - qrk;
|
||||
var ee:Number = qij + qrk;
|
||||
var ff:Number = 1 - qi2 - qk2;
|
||||
var ii:Number = qki - qrj;
|
||||
var jj:Number = qjk + qri;
|
||||
var kk:Number = 1 - qi2 - qj2;
|
||||
|
||||
if (-1 < ii && ii < 1) {
|
||||
object.rotationX = Math.atan2(jj, kk);
|
||||
object.rotationY = -Math.asin(ii);
|
||||
object.rotationZ = Math.atan2(ee, aa);
|
||||
} else {
|
||||
object.rotationX = 0;
|
||||
object.rotationY = (ii <= -1) ? Math.PI : -Math.PI;
|
||||
object.rotationY *= 0.5;
|
||||
object.rotationZ = Math.atan2(-bb, ff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
198
src/alternativa/engine3d/animation/AnimationSwitcher.as
Normal file
198
src/alternativa/engine3d/animation/AnimationSwitcher.as
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* The animation switcher performs animation blending and active animation switching.
|
||||
*
|
||||
*/
|
||||
public class AnimationSwitcher extends AnimationNode {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _numAnimations:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _animations:Vector.<AnimationNode> = new Vector.<AnimationNode>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _weights:Vector.<Number> = new Vector.<Number>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _active:AnimationNode;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var fadingSpeed:Number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function update(elapsed:Number, weight:Number):void {
|
||||
// TODO : make fade if it required only
|
||||
var interval:Number = speed * elapsed;
|
||||
var fade:Number = fadingSpeed * interval;
|
||||
for (var i:int = 0; i < _numAnimations; i++) {
|
||||
var animation:AnimationNode = _animations[i];
|
||||
var w:Number = _weights[i];
|
||||
if (animation == _active) {
|
||||
w += fade;
|
||||
w = (w >= 1) ? 1 : w;
|
||||
animation.update(interval, weight * w);
|
||||
_weights[i] = w;
|
||||
} else {
|
||||
w -= fade;
|
||||
if (w > 0) {
|
||||
animation.update(interval, weight * w);
|
||||
_weights[i] = w;
|
||||
} else {
|
||||
animation._isActive = false;
|
||||
_weights[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The current active animation. To change active animation use <code>activate()</code>.
|
||||
*
|
||||
* @see #activate()
|
||||
*/
|
||||
public function get active():AnimationNode {
|
||||
return _active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates specified animation during given time interval. All the rest animations fade out.
|
||||
*
|
||||
* @param animation Animation which is set as active.
|
||||
* @param time The time interval during which the animation becomes fully active (i.e. has full weight).
|
||||
*/
|
||||
public function activate(animation:AnimationNode, time:Number = 0):void {
|
||||
if (animation._parent != this) {
|
||||
throw new Error("Animation is not child of this blender");
|
||||
}
|
||||
_active = animation;
|
||||
animation._isActive = true;
|
||||
if (time <= 0) {
|
||||
for (var i:int = 0; i < _numAnimations; i++) {
|
||||
if (_animations[i] == animation) {
|
||||
_weights[i] = 1;
|
||||
} else {
|
||||
_weights[i] = 0;
|
||||
_animations[i]._isActive = false;
|
||||
}
|
||||
}
|
||||
fadingSpeed = 0;
|
||||
} else {
|
||||
fadingSpeed = 1/time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function setController(value:AnimationController):void {
|
||||
this.controller = value;
|
||||
for (var i:int = 0; i < _numAnimations; i++) {
|
||||
var animation:AnimationNode = _animations[i];
|
||||
animation.setController(controller);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function removeNode(node:AnimationNode):void {
|
||||
removeAnimation(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new animation.
|
||||
*
|
||||
* @param animation The animation node to add.
|
||||
* @return Added animation.
|
||||
*/
|
||||
public function addAnimation(animation:AnimationNode):AnimationNode {
|
||||
if (animation == null) {
|
||||
throw new Error("Animation cannot be null");
|
||||
}
|
||||
if (animation._parent == this) {
|
||||
throw new Error("Animation already exist in blender");
|
||||
}
|
||||
_animations[_numAnimations] = animation;
|
||||
if (_numAnimations == 0) {
|
||||
_active = animation;
|
||||
animation._isActive = true;
|
||||
_weights[_numAnimations] = 1;
|
||||
} else {
|
||||
_weights[_numAnimations] = 0;
|
||||
}
|
||||
_numAnimations++;
|
||||
addNode(animation);
|
||||
return animation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes child animation node.
|
||||
*
|
||||
* @param animation Animation node to remove.
|
||||
*/
|
||||
public function removeAnimation(animation:AnimationNode):AnimationNode {
|
||||
var index:int = _animations.indexOf(animation);
|
||||
if (index < 0) throw new ArgumentError("Animation not found");
|
||||
_numAnimations--;
|
||||
var j:int = index + 1;
|
||||
while (index < _numAnimations) {
|
||||
_animations[index] = _animations[j];
|
||||
index++;
|
||||
j++;
|
||||
}
|
||||
_animations.length = _numAnimations;
|
||||
_weights.length = _numAnimations;
|
||||
if (_active == animation) {
|
||||
if (_numAnimations > 0) {
|
||||
_active = _animations[int(_numAnimations - 1)];
|
||||
_weights[int(_numAnimations - 1)] = 1;
|
||||
} else {
|
||||
_active = null;
|
||||
}
|
||||
}
|
||||
super.removeNode(animation);
|
||||
return animation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child animation that exists at the specified index.
|
||||
*
|
||||
* @param index The index position of the child object.
|
||||
*/
|
||||
public function getAnimationAt(index:int):AnimationNode {
|
||||
return _animations[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of animations.
|
||||
*/
|
||||
public function numAnimations():int {
|
||||
return _numAnimations;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
44
src/alternativa/engine3d/animation/events/NotifyEvent.as
Normal file
44
src/alternativa/engine3d/animation/events/NotifyEvent.as
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.events {
|
||||
|
||||
import alternativa.engine3d.animation.AnimationNotify;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* This event is fired by an AnimationNotify instance when certain point of AnimationClip time line is reached.
|
||||
*
|
||||
* @see alternativa.engine3d.animation.AnimationNotify
|
||||
*/
|
||||
public class NotifyEvent extends Event {
|
||||
|
||||
/**
|
||||
*NotifyEvent.NOTIFY is specified as the type property for transfer to the addEventListener alert for the event.
|
||||
*/
|
||||
public static const NOTIFY:String = "notify";
|
||||
|
||||
/**
|
||||
* The source of the event. Actually this property returns AnimationNotify(target) value.
|
||||
*/
|
||||
public function get notify():AnimationNotify {
|
||||
return AnimationNotify(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Notyfy object.
|
||||
*/
|
||||
public function NotifyEvent(notify:AnimationNotify) {
|
||||
super(NOTIFY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
84
src/alternativa/engine3d/animation/keys/Keyframe.as
Normal file
84
src/alternativa/engine3d/animation/keys/Keyframe.as
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.keys {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Keyframe of the animation. Sets object property at given time.
|
||||
* Keyframe animation can be defined with NumberTrack and TransformTrack classes.
|
||||
*
|
||||
* @see TransformTrack
|
||||
* @see NumberTrack
|
||||
*/
|
||||
public class Keyframe {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Key frame time in seconds.
|
||||
*/
|
||||
alternativa3d var _time:Number = 0;
|
||||
|
||||
/**
|
||||
* Creates a new Keyframe instance.
|
||||
*/
|
||||
public function Keyframe() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Key frame time in seconds.
|
||||
*/
|
||||
public function get time():Number {
|
||||
return _time;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of animated property kept by the keyframe.
|
||||
* Can be <code>Number</code> or <code>Matrix3D</code> depends on
|
||||
* <code>NumberTrack</code> or <code>TransformTrack</code> belongs to.
|
||||
*
|
||||
* @see NumberTrack
|
||||
* @see TransformTrack
|
||||
*/
|
||||
public function get value():Object {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set value(v:Object):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function get nextKeyFrame():Keyframe {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function set nextKeyFrame(value:Keyframe):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string representation of the object.
|
||||
*/
|
||||
public function toString():String {
|
||||
return '[Keyframe time = ' + _time.toFixed(2) + ' value = ' + value + ']';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
73
src/alternativa/engine3d/animation/keys/NumberKey.as
Normal file
73
src/alternativa/engine3d/animation/keys/NumberKey.as
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.keys {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class NumberKey extends Keyframe {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _value:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var next:NumberKey;
|
||||
|
||||
/**
|
||||
* Creates a NumberKey object.
|
||||
*/
|
||||
public function NumberKey() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interpolated value.
|
||||
*/
|
||||
public function interpolate(a:NumberKey, b:NumberKey, c:Number):void {
|
||||
_value = (1 - c)*a._value + c*b._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function get value():Object {
|
||||
return _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function set value(v:Object):void {
|
||||
_value = Number(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override alternativa3d function get nextKeyFrame():Keyframe {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override alternativa3d function set nextKeyFrame(value:Keyframe):void {
|
||||
next = NumberKey(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
160
src/alternativa/engine3d/animation/keys/NumberTrack.as
Normal file
160
src/alternativa/engine3d/animation/keys/NumberTrack.as
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.keys {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.AnimationState;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
*
|
||||
* Keyframe track for animating numeric properties. Each keyframe keeps its own value of the property.
|
||||
* The value interpolates for in between keyframes.
|
||||
*/
|
||||
public class NumberTrack extends Track {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Head of keyframe list.
|
||||
*/
|
||||
alternativa3d var keyList:NumberKey;
|
||||
|
||||
private var lastKey:NumberKey;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get keyFramesList():Keyframe {
|
||||
return keyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function set keyFramesList(value:Keyframe):void {
|
||||
keyList = NumberKey(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get lastKey():Keyframe {
|
||||
return lastKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function set lastKey(value:Keyframe):void {
|
||||
lastKey = NumberKey(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the name of object property which will be animated.
|
||||
*/
|
||||
public var property:String;
|
||||
|
||||
/**
|
||||
* Creates a NumberTrack object.
|
||||
*
|
||||
* @param object name of animating object.
|
||||
* @param property name of animating property.
|
||||
*/
|
||||
public function NumberTrack(object:String, property:String) {
|
||||
this.property = property;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new keyframe. Keyframes stores ordered by its time property.
|
||||
*
|
||||
* @param time time of the new keyframe.
|
||||
* @param value value of property for the new keyframe.
|
||||
* @return added keyframe.
|
||||
*/
|
||||
public function addKey(time:Number, value:Number = 0):Keyframe {
|
||||
var key:NumberKey = new NumberKey();
|
||||
key._time = time;
|
||||
key.value = value;
|
||||
addKeyToList(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var temp:NumberKey = new NumberKey();
|
||||
|
||||
private var recentKey:NumberKey = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function blend(time:Number, weight:Number, state:AnimationState):void {
|
||||
if (property == null) {
|
||||
return;
|
||||
}
|
||||
var prev:NumberKey;
|
||||
var next:NumberKey;
|
||||
|
||||
if (recentKey != null && recentKey.time < time) {
|
||||
prev = recentKey;
|
||||
next = recentKey.next;
|
||||
} else {
|
||||
next = keyList;
|
||||
}
|
||||
while (next != null && next._time < time) {
|
||||
prev = next;
|
||||
next = next.next;
|
||||
}
|
||||
if (prev != null) {
|
||||
if (next != null) {
|
||||
temp.interpolate(prev, next, (time - prev._time)/(next._time - prev._time));
|
||||
state.addWeightedNumber(property, temp._value, weight);
|
||||
} else {
|
||||
state.addWeightedNumber(property, prev._value, weight);
|
||||
}
|
||||
recentKey = prev;
|
||||
} else {
|
||||
if (next != null) {
|
||||
state.addWeightedNumber(property, next._value, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function createKeyFrame():Keyframe {
|
||||
return new NumberKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function interpolateKeyFrame(dest:Keyframe, a:Keyframe, b:Keyframe, value:Number):void {
|
||||
NumberKey(dest).interpolate(NumberKey(a), NumberKey(b), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function slice(start:Number, end:Number = Number.MAX_VALUE):Track {
|
||||
var track:NumberTrack = new NumberTrack(object, property);
|
||||
sliceImplementation(track, start, end);
|
||||
return track;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
261
src/alternativa/engine3d/animation/keys/Track.as
Normal file
261
src/alternativa/engine3d/animation/keys/Track.as
Normal file
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.keys {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.AnimationState;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Keyframe track baseclass.
|
||||
*
|
||||
* @see alternativa.engine3d.animation.AnimationClip
|
||||
*/
|
||||
public class Track {
|
||||
|
||||
/**
|
||||
* Name of the object which is animated.
|
||||
*/
|
||||
public var object:String;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _length:Number = 0;
|
||||
|
||||
/**
|
||||
* Creates a Track object.
|
||||
*/
|
||||
public function Track() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of animation in seconds..
|
||||
*/
|
||||
public function get length():Number {
|
||||
return _length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function get keyFramesList():Keyframe {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function set keyFramesList(value:Keyframe):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function get lastKey():Keyframe {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function set lastKey(value:Keyframe):void {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function addKeyToList(key:Keyframe):void {
|
||||
var time:Number = key._time;
|
||||
if (keyFramesList == null) {
|
||||
keyFramesList = key;
|
||||
lastKey = key;
|
||||
_length = (time <= 0) ? 0 : time;
|
||||
return;
|
||||
} else {
|
||||
if (keyFramesList._time > time) {
|
||||
// replace head of the keyframe list
|
||||
key.nextKeyFrame = keyFramesList;
|
||||
keyFramesList = key;
|
||||
return;
|
||||
} else {
|
||||
// adds to the end of list
|
||||
if (lastKey._time < time) {
|
||||
lastKey.nextKeyFrame = key;
|
||||
lastKey = key;
|
||||
_length = (time <= 0) ? 0 : time;
|
||||
} else {
|
||||
// search for appropriate place
|
||||
var k:Keyframe = keyFramesList;
|
||||
while (k.nextKeyFrame != null && k.nextKeyFrame._time <= time) {
|
||||
k = k.nextKeyFrame;
|
||||
}
|
||||
if (k.nextKeyFrame == null) {
|
||||
// adds to the end
|
||||
k.nextKeyFrame = key;
|
||||
_length = (time <= 0) ? 0 : time;
|
||||
} else {
|
||||
key.nextKeyFrame = k.nextKeyFrame;
|
||||
k.nextKeyFrame = key;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the supplied key frame.
|
||||
*
|
||||
* @param key the key frame to remove.
|
||||
* @return removed key frame.
|
||||
*/
|
||||
public function removeKey(key:Keyframe):Keyframe {
|
||||
if (keyFramesList != null) {
|
||||
if (keyFramesList == key) {
|
||||
keyFramesList = keyFramesList.nextKeyFrame;
|
||||
if (keyFramesList == null) {
|
||||
lastKey = null;
|
||||
_length = 0;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
var k:Keyframe = keyFramesList;
|
||||
while (k.nextKeyFrame != null && k.nextKeyFrame != key) {
|
||||
k = k.nextKeyFrame;
|
||||
}
|
||||
if (k.nextKeyFrame == key) {
|
||||
// Remove
|
||||
if (key.nextKeyFrame == null) {
|
||||
lastKey = k;
|
||||
// Last item
|
||||
_length = (k._time <= 0) ? 0 : k._time;
|
||||
}
|
||||
k.nextKeyFrame = key.nextKeyFrame;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
throw new Error("Key not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Time-sorted list of key frames.
|
||||
*/
|
||||
public function get keys():Vector.<Keyframe> {
|
||||
var result:Vector.<Keyframe> = new Vector.<Keyframe>();
|
||||
var i:int = 0;
|
||||
for (var key:Keyframe = keyFramesList; key != null; key = key.nextKeyFrame) {
|
||||
result[i] = key;
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function blend(time:Number, weight:Number, state:AnimationState):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fragment of animation track between start and end time.
|
||||
*
|
||||
* @param start Fragment's start time.
|
||||
* @param end Fragment's end time.
|
||||
* @return Track fragment.
|
||||
*/
|
||||
public function slice(start:Number, end:Number = Number.MAX_VALUE):Track {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function createKeyFrame():Keyframe {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function interpolateKeyFrame(dest:Keyframe, a:Keyframe, b:Keyframe, value:Number):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function sliceImplementation(dest:Track, start:Number, end:Number):void {
|
||||
var shiftTime:Number = (start > 0) ? start : 0;
|
||||
var prev:Keyframe;
|
||||
var next:Keyframe = keyFramesList;
|
||||
// the first keyframe
|
||||
var key:Keyframe = createKeyFrame();
|
||||
var nextKey:Keyframe;
|
||||
while (next != null && next._time <= start) {
|
||||
prev = next;
|
||||
next = next.nextKeyFrame;
|
||||
}
|
||||
if (prev != null) {
|
||||
if (next != null) {
|
||||
interpolateKeyFrame(key, prev, next, (start - prev._time)/(next._time - prev._time));
|
||||
key._time = start - shiftTime;
|
||||
} else {
|
||||
// last keyframe
|
||||
interpolateKeyFrame(key, key, prev, 1);
|
||||
}
|
||||
} else {
|
||||
if (next != null) {
|
||||
// time before the start of animation
|
||||
interpolateKeyFrame(key, key, next, 1);
|
||||
key._time = next._time - shiftTime;
|
||||
prev = next;
|
||||
next = next.nextKeyFrame;
|
||||
} else {
|
||||
// empty track
|
||||
return;
|
||||
}
|
||||
}
|
||||
dest.keyFramesList = key;
|
||||
if (next == null || end <= start) {
|
||||
// one key frame
|
||||
dest._length = (key._time <= 0) ? 0 : key._time;
|
||||
return;
|
||||
}
|
||||
// copies intermediate keys
|
||||
while (next != null && next._time <= end) {
|
||||
nextKey = createKeyFrame();
|
||||
interpolateKeyFrame(nextKey, nextKey, next, 1);
|
||||
nextKey._time = next._time - shiftTime;
|
||||
key.nextKeyFrame = nextKey;
|
||||
key = nextKey;
|
||||
prev = next;
|
||||
next = next.nextKeyFrame;
|
||||
}
|
||||
// move last key
|
||||
if (next != null) {
|
||||
// time to end of the track
|
||||
nextKey = createKeyFrame();
|
||||
interpolateKeyFrame(nextKey, prev, next, (end - prev._time)/(next._time - prev._time));
|
||||
nextKey._time = end - shiftTime;
|
||||
key.nextKeyFrame = nextKey;
|
||||
} else {
|
||||
// time after current key
|
||||
}
|
||||
if (nextKey != null) {
|
||||
dest._length = (nextKey._time <= 0) ? 0 : nextKey._time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
164
src/alternativa/engine3d/animation/keys/TransformKey.as
Normal file
164
src/alternativa/engine3d/animation/keys/TransformKey.as
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.keys {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class TransformKey extends Keyframe {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var x:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var y:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var z:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var rotation:Vector3D = new Vector3D(0, 0, 0, 1);
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var scaleX:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var scaleY:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var scaleZ:Number = 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var next:TransformKey;
|
||||
|
||||
/**
|
||||
* Creates a TransformKey object.
|
||||
*/
|
||||
public function TransformKey() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function get value():Object {
|
||||
var m:Matrix3D = new Matrix3D();
|
||||
m.recompose(Vector.<Vector3D>([new Vector3D(x, y, z), rotation, new Vector3D(scaleX, scaleY, scaleZ)]), Orientation3D.QUATERNION);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function set value(v:Object):void {
|
||||
var m:Matrix3D = Matrix3D(v);
|
||||
var components:Vector.<Vector3D> = m.decompose(Orientation3D.QUATERNION);
|
||||
x = components[0].x;
|
||||
y = components[0].y;
|
||||
z = components[0].z;
|
||||
rotation = components[1];
|
||||
scaleX = components[2].x;
|
||||
scaleY = components[2].y;
|
||||
scaleZ = components[2].z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interpolated value.
|
||||
*/
|
||||
public function interpolate(a:TransformKey, b:TransformKey, c:Number):void {
|
||||
var c2:Number = 1 - c;
|
||||
x = c2*a.x + c*b.x;
|
||||
y = c2*a.y + c*b.y;
|
||||
z = c2*a.z + c*b.z;
|
||||
slerp(a.rotation, b.rotation, c, rotation);
|
||||
scaleX = c2*a.scaleX + c*b.scaleX;
|
||||
scaleY = c2*a.scaleY + c*b.scaleY;
|
||||
scaleZ = c2*a.scaleZ + c*b.scaleZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Performs spherical interpolation between two given quaternions by min distance
|
||||
*
|
||||
* @param a first quaternion.
|
||||
* @param b second quaternion.
|
||||
* @param t interpolation parameter, usually defines in [0, 1] range.
|
||||
* @return this
|
||||
*/
|
||||
private function slerp(a:Vector3D, b:Vector3D, t:Number, result:Vector3D):void {
|
||||
var flip:Number = 1;
|
||||
// Since one orientation represents by two values q and -q, we should invert the sign of one of quaternions
|
||||
// in case of negative value of the dot product. Otherwise the interpolation results by max distance.
|
||||
var cosine:Number = a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
if (cosine < 0) {
|
||||
cosine = -cosine;
|
||||
flip = -1;
|
||||
}
|
||||
if ((1 - cosine) < 0.001) {
|
||||
// Linear interpolation used near zero
|
||||
var k1:Number = 1 - t;
|
||||
var k2:Number = t*flip;
|
||||
result.w = a.w*k1 + b.w*k2;
|
||||
result.x = a.x*k1 + b.x*k2;
|
||||
result.y = a.y*k1 + b.y*k2;
|
||||
result.z = a.z*k1 + b.z*k2;
|
||||
var d:Number = result.w*result.w + result.x*result.x + result.y*result.y + result.z*result.z;
|
||||
if (d == 0) {
|
||||
result.w = 1;
|
||||
} else {
|
||||
result.scaleBy(1/Math.sqrt(d));
|
||||
}
|
||||
} else {
|
||||
var theta:Number = Math.acos(cosine);
|
||||
var sine:Number = Math.sin(theta);
|
||||
var beta:Number = Math.sin((1 - t)*theta)/sine;
|
||||
var alpha:Number = Math.sin(t*theta)/sine*flip;
|
||||
result.w = a.w*beta + b.w*alpha;
|
||||
result.x = a.x*beta + b.x*alpha;
|
||||
result.y = a.y*beta + b.y*alpha;
|
||||
result.z = a.z*beta + b.z*alpha;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override alternativa3d function get nextKeyFrame():Keyframe {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override alternativa3d function set nextKeyFrame(value:Keyframe):void {
|
||||
next = TransformKey(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
240
src/alternativa/engine3d/animation/keys/TransformTrack.as
Normal file
240
src/alternativa/engine3d/animation/keys/TransformTrack.as
Normal file
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.animation.keys {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.AnimationState;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* A track which animates object transformation.
|
||||
*/
|
||||
public class TransformTrack extends Track {
|
||||
|
||||
private var keyList:TransformKey;
|
||||
|
||||
private var lastKey:TransformKey;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get keyFramesList():Keyframe {
|
||||
return keyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function set keyFramesList(value:Keyframe):void {
|
||||
keyList = TransformKey(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get lastKey():Keyframe {
|
||||
return lastKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function set lastKey(value:Keyframe):void {
|
||||
lastKey = TransformKey(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TransformTrack object.
|
||||
*/
|
||||
public function TransformTrack(object:String) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new keyframe. Keyframes stores ordered by its time property.
|
||||
*
|
||||
* @param time time of the new keyframe.
|
||||
* @param matrix value of property for the new keyframe.
|
||||
* @return added keyframe.
|
||||
*/
|
||||
public function addKey(time:Number, matrix:Matrix3D):TransformKey {
|
||||
var key:TransformKey = new TransformKey();
|
||||
key._time = time;
|
||||
var components:Vector.<Vector3D> = matrix.decompose(Orientation3D.QUATERNION);
|
||||
key.x = components[0].x;
|
||||
key.y = components[0].y;
|
||||
key.z = components[0].z;
|
||||
key.rotation = components[1];
|
||||
key.scaleX = components[2].x;
|
||||
key.scaleY = components[2].y;
|
||||
key.scaleZ = components[2].z;
|
||||
addKeyToList(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new keyframe and initialize it by transformation components.
|
||||
* Keyframes stores ordered by its time property.
|
||||
*
|
||||
* @param time time of the new keyframe.
|
||||
* @return added keyframe.
|
||||
*/
|
||||
public function addKeyComponents(time:Number, x:Number = 0, y:Number = 0, z:Number = 0, rotationX:Number = 0, rotationY:Number = 0, rotationZ:Number = 0, scaleX:Number = 1, scaleY:Number = 1, scaleZ:Number = 1):TransformKey {
|
||||
var key:TransformKey = new TransformKey();
|
||||
key._time = time;
|
||||
key.x = x;
|
||||
key.y = y;
|
||||
key.z = z;
|
||||
key.rotation = createQuatFromEuler(rotationX, rotationY, rotationZ);
|
||||
key.scaleX = scaleX;
|
||||
key.scaleY = scaleY;
|
||||
key.scaleZ = scaleZ;
|
||||
addKeyToList(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Multiplies quat by additive from right: quat = quat * additive.
|
||||
*
|
||||
*/
|
||||
private function appendQuat(quat:Vector3D, additive:Vector3D):void {
|
||||
var ww:Number = additive.w*quat.w - additive.x*quat.x - additive.y*quat.y - additive.z*quat.z;
|
||||
var xx:Number = additive.w*quat.x + additive.x*quat.w + additive.y*quat.z - additive.z*quat.y;
|
||||
var yy:Number = additive.w*quat.y + additive.y*quat.w + additive.z*quat.x - additive.x*quat.z;
|
||||
var zz:Number = additive.w*quat.z + additive.z*quat.w + additive.x*quat.y - additive.y*quat.x;
|
||||
quat.w = ww;
|
||||
quat.x = xx;
|
||||
quat.y = yy;
|
||||
quat.z = zz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function normalizeQuat(quat:Vector3D):void {
|
||||
var d:Number = quat.w*quat.w + quat.x*quat.x + quat.y*quat.y + quat.z*quat.z;
|
||||
if (d == 0) {
|
||||
quat.w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
quat.w *= d;
|
||||
quat.x *= d;
|
||||
quat.y *= d;
|
||||
quat.z *= d;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function setQuatFromAxisAngle(quat:Vector3D, x:Number, y:Number, z:Number, angle:Number):void {
|
||||
quat.w = Math.cos(0.5*angle);
|
||||
var k:Number = Math.sin(0.5*angle)/Math.sqrt(x*x + y*y + z*z);
|
||||
quat.x = x*k;
|
||||
quat.y = y*k;
|
||||
quat.z = z*k;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var tempQuat:Vector3D = new Vector3D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function createQuatFromEuler(x:Number, y:Number, z:Number):Vector3D {
|
||||
var result:Vector3D = new Vector3D();
|
||||
setQuatFromAxisAngle(result, 1, 0, 0, x);
|
||||
|
||||
setQuatFromAxisAngle(tempQuat, 0, 1, 0, y);
|
||||
appendQuat(result, tempQuat);
|
||||
normalizeQuat(result);
|
||||
|
||||
setQuatFromAxisAngle(tempQuat, 0, 0, 1, z);
|
||||
appendQuat(result, tempQuat);
|
||||
normalizeQuat(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var temp:TransformKey = new TransformKey();
|
||||
|
||||
private var recentKey:TransformKey = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function blend(time:Number, weight:Number, state:AnimationState):void {
|
||||
var prev:TransformKey;
|
||||
var next:TransformKey;
|
||||
|
||||
if (recentKey != null && recentKey.time < time) {
|
||||
prev = recentKey;
|
||||
next = recentKey.next;
|
||||
} else {
|
||||
next = keyList;
|
||||
}
|
||||
while (next != null && next._time < time) {
|
||||
prev = next;
|
||||
next = next.next;
|
||||
}
|
||||
|
||||
if (prev != null) {
|
||||
if (next != null) {
|
||||
temp.interpolate(prev, next, (time - prev._time)/(next._time - prev._time));
|
||||
state.addWeightedTransform(temp, weight);
|
||||
} else {
|
||||
state.addWeightedTransform(prev, weight);
|
||||
}
|
||||
recentKey = prev;
|
||||
} else {
|
||||
if (next != null) {
|
||||
state.addWeightedTransform(next, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function createKeyFrame():Keyframe {
|
||||
return new TransformKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function interpolateKeyFrame(dest:Keyframe, a:Keyframe, b:Keyframe, value:Number):void {
|
||||
TransformKey(dest).interpolate(TransformKey(a), TransformKey(b), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function slice(start:Number, end:Number = Number.MAX_VALUE):Track {
|
||||
var track:TransformTrack = new TransformTrack(object);
|
||||
sliceImplementation(track, start, end);
|
||||
return track;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
576
src/alternativa/engine3d/collisions/EllipsoidCollider.as
Normal file
576
src/alternativa/engine3d/collisions/EllipsoidCollider.as
Normal file
@@ -0,0 +1,576 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.collisions {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.*;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* The class implements the algorithm of the continuous collision of an ellipsoid with the faces.
|
||||
*/
|
||||
public class EllipsoidCollider {
|
||||
|
||||
/**
|
||||
* Ellipsoid radius along X axis.
|
||||
*/
|
||||
public var radiusX:Number;
|
||||
|
||||
/**
|
||||
* Ellipsoid radius along Y axis.
|
||||
*/
|
||||
public var radiusY:Number;
|
||||
|
||||
/**
|
||||
* Ellipsoid radius along Z axis.
|
||||
*/
|
||||
public var radiusZ:Number;
|
||||
|
||||
/**
|
||||
* Geometric error. Minimum absolute difference between two values
|
||||
* when they are considered to be different. Default value is 0.001.
|
||||
*/
|
||||
public var threshold:Number = 0.001;
|
||||
|
||||
private var matrix:Transform3D = new Transform3D();
|
||||
private var inverseMatrix:Transform3D = new Transform3D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var geometries:Vector.<Geometry> = new Vector.<Geometry>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var transforms:Vector.<Transform3D> = new Vector.<Transform3D>();
|
||||
|
||||
private var vertices:Vector.<Number> = new Vector.<Number>();
|
||||
private var normals:Vector.<Number> = new Vector.<Number>();
|
||||
private var indices:Vector.<int> = new Vector.<int>();
|
||||
private var numTriangles:int;
|
||||
|
||||
private var radius:Number;
|
||||
private var src:Vector3D = new Vector3D();
|
||||
private var displ:Vector3D = new Vector3D();
|
||||
private var dest:Vector3D = new Vector3D();
|
||||
|
||||
private var collisionPoint:Vector3D = new Vector3D();
|
||||
private var collisionPlane:Vector3D = new Vector3D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var sphere:Vector3D = new Vector3D();
|
||||
private var cornerA:Vector3D = new Vector3D();
|
||||
private var cornerB:Vector3D = new Vector3D();
|
||||
private var cornerC:Vector3D = new Vector3D();
|
||||
private var cornerD:Vector3D = new Vector3D();
|
||||
|
||||
/**
|
||||
* Creates a EllipsoidCollider object.
|
||||
*
|
||||
* @param radiusX Ellipsoid radius along X axis.
|
||||
* @param radiusY Ellipsoid radius along Y axis.
|
||||
* @param radiusZ Ellipsoid radius along Z axis.
|
||||
*/
|
||||
public function EllipsoidCollider(radiusX:Number, radiusY:Number, radiusZ:Number) {
|
||||
this.radiusX = radiusX;
|
||||
this.radiusY = radiusY;
|
||||
this.radiusZ = radiusZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function calculateSphere(transform:Transform3D):void {
|
||||
sphere.x = transform.d;
|
||||
sphere.y = transform.h;
|
||||
sphere.z = transform.l;
|
||||
var sax:Number = transform.a*cornerA.x + transform.b*cornerA.y + transform.c*cornerA.z + transform.d;
|
||||
var say:Number = transform.e*cornerA.x + transform.f*cornerA.y + transform.g*cornerA.z + transform.h;
|
||||
var saz:Number = transform.i*cornerA.x + transform.j*cornerA.y + transform.k*cornerA.z + transform.l;
|
||||
var sbx:Number = transform.a*cornerB.x + transform.b*cornerB.y + transform.c*cornerB.z + transform.d;
|
||||
var sby:Number = transform.e*cornerB.x + transform.f*cornerB.y + transform.g*cornerB.z + transform.h;
|
||||
var sbz:Number = transform.i*cornerB.x + transform.j*cornerB.y + transform.k*cornerB.z + transform.l;
|
||||
var scx:Number = transform.a*cornerC.x + transform.b*cornerC.y + transform.c*cornerC.z + transform.d;
|
||||
var scy:Number = transform.e*cornerC.x + transform.f*cornerC.y + transform.g*cornerC.z + transform.h;
|
||||
var scz:Number = transform.i*cornerC.x + transform.j*cornerC.y + transform.k*cornerC.z + transform.l;
|
||||
var sdx:Number = transform.a*cornerD.x + transform.b*cornerD.y + transform.c*cornerD.z + transform.d;
|
||||
var sdy:Number = transform.e*cornerD.x + transform.f*cornerD.y + transform.g*cornerD.z + transform.h;
|
||||
var sdz:Number = transform.i*cornerD.x + transform.j*cornerD.y + transform.k*cornerD.z + transform.l;
|
||||
var dx:Number = sax - sphere.x;
|
||||
var dy:Number = say - sphere.y;
|
||||
var dz:Number = saz - sphere.z;
|
||||
sphere.w = dx*dx + dy*dy + dz*dz;
|
||||
dx = sbx - sphere.x;
|
||||
dy = sby - sphere.y;
|
||||
dz = sbz - sphere.z;
|
||||
var dxyz:Number = dx*dx + dy*dy + dz*dz;
|
||||
if (dxyz > sphere.w) sphere.w = dxyz;
|
||||
dx = scx - sphere.x;
|
||||
dy = scy - sphere.y;
|
||||
dz = scz - sphere.z;
|
||||
dxyz = dx*dx + dy*dy + dz*dz;
|
||||
if (dxyz > sphere.w) sphere.w = dxyz;
|
||||
dx = sdx - sphere.x;
|
||||
dy = sdy - sphere.y;
|
||||
dz = sdz - sphere.z;
|
||||
dxyz = dx*dx + dy*dy + dz*dz;
|
||||
if (dxyz > sphere.w) sphere.w = dxyz;
|
||||
sphere.w = Math.sqrt(sphere.w);
|
||||
}
|
||||
|
||||
private function prepare(source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary):void {
|
||||
|
||||
// Radius of the sphere
|
||||
radius = radiusX;
|
||||
if (radiusY > radius) radius = radiusY;
|
||||
if (radiusZ > radius) radius = radiusZ;
|
||||
|
||||
// The matrix of the collider
|
||||
matrix.compose(source.x, source.y, source.z, 0, 0, 0, radiusX/radius, radiusY/radius, radiusZ/radius);
|
||||
inverseMatrix.copy(matrix);
|
||||
inverseMatrix.invert();
|
||||
|
||||
// Local coordinates
|
||||
src.x = 0;
|
||||
src.y = 0;
|
||||
src.z = 0;
|
||||
// Local offset
|
||||
displ.x = inverseMatrix.a*displacement.x + inverseMatrix.b*displacement.y + inverseMatrix.c*displacement.z;
|
||||
displ.y = inverseMatrix.e*displacement.x + inverseMatrix.f*displacement.y + inverseMatrix.g*displacement.z;
|
||||
displ.z = inverseMatrix.i*displacement.x + inverseMatrix.j*displacement.y + inverseMatrix.k*displacement.z;
|
||||
// Local destination point
|
||||
dest.x = src.x + displ.x;
|
||||
dest.y = src.y + displ.y;
|
||||
dest.z = src.z + displ.z;
|
||||
|
||||
// Bound defined by movement of the sphere
|
||||
var rad:Number = radius + displ.length;
|
||||
cornerA.x = -rad;
|
||||
cornerA.y = -rad;
|
||||
cornerA.z = -rad;
|
||||
cornerB.x = rad;
|
||||
cornerB.y = -rad;
|
||||
cornerB.z = -rad;
|
||||
cornerC.x = rad;
|
||||
cornerC.y = rad;
|
||||
cornerC.z = -rad;
|
||||
cornerD.x = -rad;
|
||||
cornerD.y = rad;
|
||||
cornerD.z = -rad;
|
||||
|
||||
// Gathering the faces which with collision can occur
|
||||
if (excludedObjects == null || !excludedObjects[object]) {
|
||||
if (object.transformChanged) object.composeTransforms();
|
||||
object.globalToLocalTransform.combine(object.inverseTransform, matrix);
|
||||
// Check collision with the bound
|
||||
var intersects:Boolean = true;
|
||||
if (object.boundBox != null) {
|
||||
calculateSphere(object.globalToLocalTransform);
|
||||
intersects = object.boundBox.checkSphere(sphere);
|
||||
}
|
||||
if (intersects) {
|
||||
object.localToGlobalTransform.combine(inverseMatrix, object.transform);
|
||||
object.collectGeometry(this, excludedObjects);
|
||||
}
|
||||
// Check children
|
||||
if (object.childrenList != null) object.collectChildrenGeometry(this, excludedObjects);
|
||||
}
|
||||
|
||||
numTriangles = 0;
|
||||
var indicesLength:int = 0;
|
||||
var normalsLength:int = 0;
|
||||
|
||||
// Loop geometries
|
||||
var j:int;
|
||||
var mapOffset:int = 0;
|
||||
var verticesLength:int = 0;
|
||||
var geometriesLength:int = geometries.length;
|
||||
for (var i:int = 0; i < geometriesLength; i++) {
|
||||
var geometry:Geometry = geometries[i];
|
||||
var transform:Transform3D = transforms[i];
|
||||
var geometryIndicesLength:int = geometry._indices.length;
|
||||
if (geometry._numVertices == 0 || geometryIndicesLength == 0) continue;
|
||||
// Transform vertices
|
||||
var vBuffer:VertexStream = (VertexAttributes.POSITION < geometry._attributesStreams.length) ? geometry._attributesStreams[VertexAttributes.POSITION] : null;
|
||||
if (vBuffer != null) {
|
||||
var attributesOffset:int = geometry._attributesOffsets[VertexAttributes.POSITION];
|
||||
var numMappings:int = vBuffer.attributes.length;
|
||||
var data:ByteArray = vBuffer.data;
|
||||
for (j = 0; j < geometry._numVertices; j++) {
|
||||
data.position = 4*(numMappings*j + attributesOffset);
|
||||
var vx:Number = data.readFloat();
|
||||
var vy:Number = data.readFloat();
|
||||
var vz:Number = data.readFloat();
|
||||
vertices[verticesLength] = transform.a*vx + transform.b*vy + transform.c*vz + transform.d; verticesLength++;
|
||||
vertices[verticesLength] = transform.e*vx + transform.f*vy + transform.g*vz + transform.h; verticesLength++;
|
||||
vertices[verticesLength] = transform.i*vx + transform.j*vy + transform.k*vz + transform.l; verticesLength++;
|
||||
}
|
||||
}
|
||||
// Loop triangles
|
||||
var geometryIndices:Vector.<uint> = geometry._indices;
|
||||
for (j = 0; j < geometryIndicesLength;) {
|
||||
var a:int = geometryIndices[j] + mapOffset; j++;
|
||||
var index:int = a*3;
|
||||
var ax:Number = vertices[index]; index++;
|
||||
var ay:Number = vertices[index]; index++;
|
||||
var az:Number = vertices[index];
|
||||
var b:int = geometryIndices[j] + mapOffset; j++;
|
||||
index = b*3;
|
||||
var bx:Number = vertices[index]; index++;
|
||||
var by:Number = vertices[index]; index++;
|
||||
var bz:Number = vertices[index];
|
||||
var c:int = geometryIndices[j] + mapOffset; j++;
|
||||
index = c*3;
|
||||
var cx:Number = vertices[index]; index++;
|
||||
var cy:Number = vertices[index]; index++;
|
||||
var cz:Number = vertices[index];
|
||||
// Exclusion by bound
|
||||
if (ax > rad && bx > rad && cx > rad || ax < -rad && bx < -rad && cx < -rad) continue;
|
||||
if (ay > rad && by > rad && cy > rad || ay < -rad && by < -rad && cy < -rad) continue;
|
||||
if (az > rad && bz > rad && cz > rad || az < -rad && bz < -rad && cz < -rad) continue;
|
||||
// The normal
|
||||
var abx:Number = bx - ax;
|
||||
var aby:Number = by - ay;
|
||||
var abz:Number = bz - az;
|
||||
var acx:Number = cx - ax;
|
||||
var acy:Number = cy - ay;
|
||||
var acz:Number = cz - az;
|
||||
var normalX:Number = acz*aby - acy*abz;
|
||||
var normalY:Number = acx*abz - acz*abx;
|
||||
var normalZ:Number = acy*abx - acx*aby;
|
||||
var len:Number = normalX*normalX + normalY*normalY + normalZ*normalZ;
|
||||
if (len < 0.001) continue;
|
||||
len = 1/Math.sqrt(len);
|
||||
normalX *= len;
|
||||
normalY *= len;
|
||||
normalZ *= len;
|
||||
var offset:Number = ax*normalX + ay*normalY + az*normalZ;
|
||||
if (offset > rad || offset < -rad) continue;
|
||||
indices[indicesLength] = a; indicesLength++;
|
||||
indices[indicesLength] = b; indicesLength++;
|
||||
indices[indicesLength] = c; indicesLength++;
|
||||
normals[normalsLength] = normalX; normalsLength++;
|
||||
normals[normalsLength] = normalY; normalsLength++;
|
||||
normals[normalsLength] = normalZ; normalsLength++;
|
||||
normals[normalsLength] = offset; normalsLength++;
|
||||
numTriangles++;
|
||||
}
|
||||
// Offset by nomber of vertices
|
||||
mapOffset += geometry._numVertices;
|
||||
}
|
||||
geometries.length = 0;
|
||||
transforms.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates destination point from given start position and displacement vector.
|
||||
* @param source Starting point.
|
||||
* @param displacement Displacement vector.
|
||||
* @param object An object at crossing which will be checked. If this is a container, the application will participate and its child objects
|
||||
* @param excludedObjects An associative array whose keys are instances of <code>Object3D</code> and its children.
|
||||
* The objects that are keys of this dictionary will be excluded from intersection test.
|
||||
* @return Destination point.
|
||||
*/
|
||||
public function calculateDestination(source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary = null):Vector3D {
|
||||
|
||||
if (displacement.length <= threshold) return source.clone();
|
||||
|
||||
prepare(source, displacement, object, excludedObjects);
|
||||
|
||||
if (numTriangles > 0) {
|
||||
var limit:int = 50;
|
||||
for (var i:int = 0; i < limit; i++) {
|
||||
if (checkCollision()) {
|
||||
// Offset destination point from behind collision plane by radius of the sphere over plane, along the normal
|
||||
var offset:Number = radius + threshold + collisionPlane.w - dest.x*collisionPlane.x - dest.y*collisionPlane.y - dest.z*collisionPlane.z;
|
||||
dest.x += collisionPlane.x*offset;
|
||||
dest.y += collisionPlane.y*offset;
|
||||
dest.z += collisionPlane.z*offset;
|
||||
// Fixing up the current sphere coordinates for the next iteration
|
||||
src.x = collisionPoint.x + collisionPlane.x*(radius + threshold);
|
||||
src.y = collisionPoint.y + collisionPlane.y*(radius + threshold);
|
||||
src.z = collisionPoint.z + collisionPlane.z*(radius + threshold);
|
||||
// Fixing up velocity vector. The result ordered along plane of collision.
|
||||
displ.x = dest.x - src.x;
|
||||
displ.y = dest.y - src.y;
|
||||
displ.z = dest.z - src.z;
|
||||
if (displ.length < threshold) break;
|
||||
} else break;
|
||||
}
|
||||
// Setting the coordinates
|
||||
return new Vector3D(matrix.a*dest.x + matrix.b*dest.y + matrix.c*dest.z + matrix.d, matrix.e*dest.x + matrix.f*dest.y + matrix.g*dest.z + matrix.h, matrix.i*dest.x + matrix.j*dest.y + matrix.k*dest.z + matrix.l);
|
||||
} else {
|
||||
return new Vector3D(source.x + displacement.x, source.y + displacement.y, source.z + displacement.z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds first collision from given starting point aling displacement vector.
|
||||
* @param source Starting point.
|
||||
* @param displacement Displacement vector.
|
||||
* @param resCollisionPoint Collision point will be written into this variable.
|
||||
* @param resCollisionPlane Collision plane (defines by normal) parameters will be written into this variable.
|
||||
* @param object The object to use in collision detection. If a container is specified, all its children will be tested for collison with ellipsoid.
|
||||
* @param excludedObjects An associative array whose keys are instances of <code>Object3D</code> and its children.
|
||||
* @return <code>true</code> if collision detected and <code>false</code> otherwise.
|
||||
*/
|
||||
public function getCollision(source:Vector3D, displacement:Vector3D, resCollisionPoint:Vector3D, resCollisionPlane:Vector3D, object:Object3D, excludedObjects:Dictionary = null):Boolean {
|
||||
|
||||
if (displacement.length <= threshold) return false;
|
||||
|
||||
prepare(source, displacement, object, excludedObjects);
|
||||
|
||||
if (numTriangles > 0) {
|
||||
if (checkCollision()) {
|
||||
|
||||
// Transform the point to the global space
|
||||
resCollisionPoint.x = matrix.a*collisionPoint.x + matrix.b*collisionPoint.y + matrix.c*collisionPoint.z + matrix.d;
|
||||
resCollisionPoint.y = matrix.e*collisionPoint.x + matrix.f*collisionPoint.y + matrix.g*collisionPoint.z + matrix.h;
|
||||
resCollisionPoint.z = matrix.i*collisionPoint.x + matrix.j*collisionPoint.y + matrix.k*collisionPoint.z + matrix.l;
|
||||
|
||||
// Transform the plane to the global space
|
||||
var abx:Number;
|
||||
var aby:Number;
|
||||
var abz:Number;
|
||||
if (collisionPlane.x < collisionPlane.y) {
|
||||
if (collisionPlane.x < collisionPlane.z) {
|
||||
abx = 0;
|
||||
aby = -collisionPlane.z;
|
||||
abz = collisionPlane.y;
|
||||
} else {
|
||||
abx = -collisionPlane.y;
|
||||
aby = collisionPlane.x;
|
||||
abz = 0;
|
||||
}
|
||||
} else {
|
||||
if (collisionPlane.y < collisionPlane.z) {
|
||||
abx = collisionPlane.z;
|
||||
aby = 0;
|
||||
abz = -collisionPlane.x;
|
||||
} else {
|
||||
abx = -collisionPlane.y;
|
||||
aby = collisionPlane.x;
|
||||
abz = 0;
|
||||
}
|
||||
}
|
||||
var acx:Number = collisionPlane.z*aby - collisionPlane.y*abz;
|
||||
var acy:Number = collisionPlane.x*abz - collisionPlane.z*abx;
|
||||
var acz:Number = collisionPlane.y*abx - collisionPlane.x*aby;
|
||||
|
||||
var abx2:Number = matrix.a*abx + matrix.b*aby + matrix.c*abz;
|
||||
var aby2:Number = matrix.e*abx + matrix.f*aby + matrix.g*abz;
|
||||
var abz2:Number = matrix.i*abx + matrix.j*aby + matrix.k*abz;
|
||||
var acx2:Number = matrix.a*acx + matrix.b*acy + matrix.c*acz;
|
||||
var acy2:Number = matrix.e*acx + matrix.f*acy + matrix.g*acz;
|
||||
var acz2:Number = matrix.i*acx + matrix.j*acy + matrix.k*acz;
|
||||
|
||||
resCollisionPlane.x = abz2*acy2 - aby2*acz2;
|
||||
resCollisionPlane.y = abx2*acz2 - abz2*acx2;
|
||||
resCollisionPlane.z = aby2*acx2 - abx2*acy2;
|
||||
resCollisionPlane.normalize();
|
||||
resCollisionPlane.w = resCollisionPoint.x*resCollisionPlane.x + resCollisionPoint.y*resCollisionPlane.y + resCollisionPoint.z*resCollisionPlane.z;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkCollision():Boolean {
|
||||
var minTime:Number = 1;
|
||||
var displacementLength:Number = displ.length;
|
||||
// Loop triangles
|
||||
var indicesLength:int = numTriangles*3;
|
||||
for (var i:int = 0, j:int = 0; i < indicesLength;) {
|
||||
// Points
|
||||
var index:int = indices[i]*3; i++;
|
||||
var ax:Number = vertices[index]; index++;
|
||||
var ay:Number = vertices[index]; index++;
|
||||
var az:Number = vertices[index];
|
||||
index = indices[i]*3; i++;
|
||||
var bx:Number = vertices[index]; index++;
|
||||
var by:Number = vertices[index]; index++;
|
||||
var bz:Number = vertices[index];
|
||||
index = indices[i]*3; i++;
|
||||
var cx:Number = vertices[index]; index++;
|
||||
var cy:Number = vertices[index]; index++;
|
||||
var cz:Number = vertices[index];
|
||||
// Normal
|
||||
var normalX:Number = normals[j]; j++;
|
||||
var normalY:Number = normals[j]; j++;
|
||||
var normalZ:Number = normals[j]; j++;
|
||||
var offset:Number = normals[j]; j++;
|
||||
var distance:Number = src.x*normalX + src.y*normalY + src.z*normalZ - offset;
|
||||
// The intersection of plane and sphere
|
||||
var pointX:Number;
|
||||
var pointY:Number;
|
||||
var pointZ:Number;
|
||||
if (distance < radius) {
|
||||
pointX = src.x - normalX*distance;
|
||||
pointY = src.y - normalY*distance;
|
||||
pointZ = src.z - normalZ*distance;
|
||||
} else {
|
||||
var t:Number = (distance - radius)/(distance - dest.x*normalX - dest.y*normalY - dest.z*normalZ + offset);
|
||||
pointX = src.x + displ.x*t - normalX*radius;
|
||||
pointY = src.y + displ.y*t - normalY*radius;
|
||||
pointZ = src.z + displ.z*t - normalZ*radius;
|
||||
}
|
||||
// Closest polygon vertex
|
||||
var faceX:Number;
|
||||
var faceY:Number;
|
||||
var faceZ:Number;
|
||||
var min:Number = 1e+22;
|
||||
// Loop edges
|
||||
var inside:Boolean = true;
|
||||
for (var k:int = 0; k < 3; k++) {
|
||||
var p1x:Number;
|
||||
var p1y:Number;
|
||||
var p1z:Number;
|
||||
var p2x:Number;
|
||||
var p2y:Number;
|
||||
var p2z:Number;
|
||||
if (k == 0) {
|
||||
p1x = ax;
|
||||
p1y = ay;
|
||||
p1z = az;
|
||||
p2x = bx;
|
||||
p2y = by;
|
||||
p2z = bz;
|
||||
} else if (k == 1) {
|
||||
p1x = bx;
|
||||
p1y = by;
|
||||
p1z = bz;
|
||||
p2x = cx;
|
||||
p2y = cy;
|
||||
p2z = cz;
|
||||
} else {
|
||||
p1x = cx;
|
||||
p1y = cy;
|
||||
p1z = cz;
|
||||
p2x = ax;
|
||||
p2y = ay;
|
||||
p2z = az;
|
||||
}
|
||||
var abx:Number = p2x - p1x;
|
||||
var aby:Number = p2y - p1y;
|
||||
var abz:Number = p2z - p1z;
|
||||
var acx:Number = pointX - p1x;
|
||||
var acy:Number = pointY - p1y;
|
||||
var acz:Number = pointZ - p1z;
|
||||
var crx:Number = acz*aby - acy*abz;
|
||||
var cry:Number = acx*abz - acz*abx;
|
||||
var crz:Number = acy*abx - acx*aby;
|
||||
// Case of the point is outside of the polygon
|
||||
if (crx*normalX + cry*normalY + crz*normalZ < 0) {
|
||||
var edgeLength:Number = abx*abx + aby*aby + abz*abz;
|
||||
var edgeDistanceSqr:Number = (crx*crx + cry*cry + crz*crz)/edgeLength;
|
||||
if (edgeDistanceSqr < min) {
|
||||
// Edge normalization
|
||||
edgeLength = Math.sqrt(edgeLength);
|
||||
abx /= edgeLength;
|
||||
aby /= edgeLength;
|
||||
abz /= edgeLength;
|
||||
// Distance to intersecion of normal along theedge
|
||||
t = abx*acx + aby*acy + abz*acz;
|
||||
var acLen:Number;
|
||||
if (t < 0) {
|
||||
// Closest point is the first one
|
||||
acLen = acx*acx + acy*acy + acz*acz;
|
||||
if (acLen < min) {
|
||||
min = acLen;
|
||||
faceX = p1x;
|
||||
faceY = p1y;
|
||||
faceZ = p1z;
|
||||
}
|
||||
} else if (t > edgeLength) {
|
||||
// Closest point is the second one
|
||||
acx = pointX - p2x;
|
||||
acy = pointY - p2y;
|
||||
acz = pointZ - p2z;
|
||||
acLen = acx*acx + acy*acy + acz*acz;
|
||||
if (acLen < min) {
|
||||
min = acLen;
|
||||
faceX = p2x;
|
||||
faceY = p2y;
|
||||
faceZ = p2z;
|
||||
}
|
||||
} else {
|
||||
// Closest point is on edge
|
||||
min = edgeDistanceSqr;
|
||||
faceX = p1x + abx*t;
|
||||
faceY = p1y + aby*t;
|
||||
faceZ = p1z + abz*t;
|
||||
}
|
||||
}
|
||||
inside = false;
|
||||
}
|
||||
}
|
||||
// Case of point is inside polygon
|
||||
if (inside) {
|
||||
faceX = pointX;
|
||||
faceY = pointY;
|
||||
faceZ = pointZ;
|
||||
}
|
||||
// Vector pointed from closest point to the center of sphere
|
||||
var deltaX:Number = src.x - faceX;
|
||||
var deltaY:Number = src.y - faceY;
|
||||
var deltaZ:Number = src.z - faceZ;
|
||||
// If movement directed to point
|
||||
if (deltaX*displ.x + deltaY*displ.y + deltaZ*displ.z <= 0) {
|
||||
// reversed vector
|
||||
var backX:Number = -displ.x/displacementLength;
|
||||
var backY:Number = -displ.y/displacementLength;
|
||||
var backZ:Number = -displ.z/displacementLength;
|
||||
// Length of Vector pointed from closest point to the center of sphere
|
||||
var deltaLength:Number = deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ;
|
||||
// Projection Vector pointed from closest point to the center of sphere on reversed vector
|
||||
var projectionLength:Number = deltaX*backX + deltaY*backY + deltaZ*backZ;
|
||||
var projectionInsideLength:Number = radius*radius - deltaLength + projectionLength*projectionLength;
|
||||
if (projectionInsideLength > 0) {
|
||||
// Time of the intersection
|
||||
var time:Number = (projectionLength - Math.sqrt(projectionInsideLength))/displacementLength;
|
||||
// Collision with closest point occurs
|
||||
if (time < minTime) {
|
||||
minTime = time;
|
||||
collisionPoint.x = faceX;
|
||||
collisionPoint.y = faceY;
|
||||
collisionPoint.z = faceZ;
|
||||
if (inside) {
|
||||
collisionPlane.x = normalX;
|
||||
collisionPlane.y = normalY;
|
||||
collisionPlane.z = normalZ;
|
||||
collisionPlane.w = offset;
|
||||
} else {
|
||||
deltaLength = Math.sqrt(deltaLength);
|
||||
collisionPlane.x = deltaX/deltaLength;
|
||||
collisionPlane.y = deltaY/deltaLength;
|
||||
collisionPlane.z = deltaZ/deltaLength;
|
||||
collisionPlane.w = collisionPoint.x*collisionPlane.x + collisionPoint.y*collisionPlane.y + collisionPoint.z*collisionPlane.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return minTime < 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
486
src/alternativa/engine3d/controllers/SimpleObjectController.as
Normal file
486
src/alternativa/engine3d/controllers/SimpleObjectController.as
Normal file
@@ -0,0 +1,486 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.controllers {
|
||||
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.ui.Keyboard;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
/**
|
||||
* Controller for <code>Object3D</code>. Allow to handle the object with a keyboard and mouse.
|
||||
*
|
||||
* @see alternativa.engine3d.core.Object3D
|
||||
*/
|
||||
public class SimpleObjectController {
|
||||
|
||||
/**
|
||||
* Name of action for binding "forward" action.
|
||||
*/
|
||||
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
|
||||
|
||||
/**
|
||||
* Name of action for binding "back" action.
|
||||
*/
|
||||
public static const ACTION_BACK:String = "ACTION_BACK";
|
||||
|
||||
/**
|
||||
* Name of action for binding "left" action.
|
||||
*/
|
||||
public static const ACTION_LEFT:String = "ACTION_LEFT";
|
||||
|
||||
/**
|
||||
* Name of action for binding "right" action.
|
||||
*/
|
||||
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
|
||||
|
||||
/**
|
||||
* Name of action for binding "up" action.
|
||||
*/
|
||||
public static const ACTION_UP:String = "ACTION_UP";
|
||||
|
||||
/**
|
||||
* Name of action for binding "down" action.
|
||||
*/
|
||||
public static const ACTION_DOWN:String = "ACTION_DOWN";
|
||||
|
||||
/**
|
||||
* Name of action for binding "pitch up" action.
|
||||
*/
|
||||
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
|
||||
|
||||
/**
|
||||
* Name of action for binding "pitch down" action.
|
||||
*/
|
||||
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
|
||||
|
||||
/**
|
||||
* Name of action for binding "yaw left" action.
|
||||
*/
|
||||
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
|
||||
|
||||
/**
|
||||
* Name of action for binding "yaw right" action.
|
||||
*/
|
||||
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
|
||||
|
||||
/**
|
||||
* Name of action for binding "accelerate" action.
|
||||
*/
|
||||
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
|
||||
|
||||
/**
|
||||
* ИName of action for binding "mouse look" action.
|
||||
*/
|
||||
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
|
||||
|
||||
/**
|
||||
* Speed.
|
||||
*/
|
||||
public var speed:Number;
|
||||
|
||||
/**
|
||||
* Speed multiplier for acceleration mode.
|
||||
*/
|
||||
public var speedMultiplier:Number;
|
||||
|
||||
/**
|
||||
* Mouse sensitivity.
|
||||
*/
|
||||
public var mouseSensitivity:Number;
|
||||
|
||||
/**
|
||||
* The maximal slope in the vertical plane in radians.
|
||||
*/
|
||||
public var maxPitch:Number = 1e+22;
|
||||
|
||||
/**
|
||||
* The minimal slope in the vertical plane in radians.
|
||||
*/
|
||||
public var minPitch:Number = -1e+22;
|
||||
|
||||
private var eventSource:InteractiveObject;
|
||||
private var _object:Object3D;
|
||||
|
||||
private var _up:Boolean;
|
||||
private var _down:Boolean;
|
||||
private var _forward:Boolean;
|
||||
private var _back:Boolean;
|
||||
private var _left:Boolean;
|
||||
private var _right:Boolean;
|
||||
private var _accelerate:Boolean;
|
||||
|
||||
private var displacement:Vector3D = new Vector3D();
|
||||
private var mousePoint:Point = new Point();
|
||||
private var mouseLook:Boolean;
|
||||
private var objectTransform:Vector.<Vector3D>;
|
||||
|
||||
private var time:int;
|
||||
|
||||
/**
|
||||
* The hash for binding names of action and functions. The functions should be at a form are follows:
|
||||
* <code>
|
||||
* function(value:Boolean):void
|
||||
* </code>
|
||||
*
|
||||
* <code>value</code> argument defines if bound key pressed down or up.
|
||||
*/
|
||||
private var actionBindings:Object = {};
|
||||
|
||||
/**
|
||||
* The hash for binding key codes and action names.
|
||||
*/
|
||||
protected var keyBindings:Object = {};
|
||||
|
||||
/**
|
||||
* Creates a SimpleObjectController object.
|
||||
* @param eventSource Source for event listening.
|
||||
* @param speed Speed of movement.
|
||||
* @param mouseSensitivity Mouse sensitivity, i.e. number of degrees per each pixel of mouse movement.
|
||||
*/
|
||||
public function SimpleObjectController(eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) {
|
||||
this.eventSource = eventSource;
|
||||
this.object = object;
|
||||
this.speed = speed;
|
||||
this.speedMultiplier = speedMultiplier;
|
||||
this.mouseSensitivity = mouseSensitivity;
|
||||
|
||||
actionBindings[ACTION_FORWARD] = moveForward;
|
||||
actionBindings[ACTION_BACK] = moveBack;
|
||||
actionBindings[ACTION_LEFT] = moveLeft;
|
||||
actionBindings[ACTION_RIGHT] = moveRight;
|
||||
actionBindings[ACTION_UP] = moveUp;
|
||||
actionBindings[ACTION_DOWN] = moveDown;
|
||||
actionBindings[ACTION_ACCELERATE] = accelerate;
|
||||
|
||||
setDefaultBindings();
|
||||
|
||||
enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the controler.
|
||||
*/
|
||||
public function enable():void {
|
||||
eventSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
eventSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
eventSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
eventSource.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the controller.
|
||||
*/
|
||||
public function disable():void {
|
||||
eventSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
eventSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
eventSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
eventSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
stopMouseLook();
|
||||
}
|
||||
|
||||
private function onMouseDown(e:MouseEvent):void {
|
||||
startMouseLook();
|
||||
}
|
||||
|
||||
private function onMouseUp(e:MouseEvent):void {
|
||||
stopMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables mouse look mode.
|
||||
*/
|
||||
public function startMouseLook():void {
|
||||
mousePoint.x = eventSource.mouseX;
|
||||
mousePoint.y = eventSource.mouseY;
|
||||
mouseLook = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables mouse look mode.
|
||||
*/
|
||||
public function stopMouseLook():void {
|
||||
mouseLook = false;
|
||||
}
|
||||
|
||||
private function onKey(e:KeyboardEvent):void {
|
||||
var method:Function = keyBindings[e.keyCode];
|
||||
if (method != null) method.call(this, e.type == KeyboardEvent.KEY_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Target of handling.
|
||||
*/
|
||||
public function get object():Object3D {
|
||||
return _object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set object(value:Object3D):void {
|
||||
_object = value;
|
||||
updateObjectTransform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes controller state from state of handled object. Should be called if object was moved without the controller (i.e. <code>object.x = 100;</code>).
|
||||
*/
|
||||
public function updateObjectTransform():void {
|
||||
if (_object != null) objectTransform = _object.matrix.decompose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and sets new object position.
|
||||
*/
|
||||
public function update():void {
|
||||
if (_object == null) return;
|
||||
|
||||
var frameTime:Number = time;
|
||||
time = getTimer();
|
||||
frameTime = 0.001*(time - frameTime);
|
||||
if (frameTime > 0.1) frameTime = 0.1;
|
||||
|
||||
var moved:Boolean = false;
|
||||
|
||||
if (mouseLook) {
|
||||
var dx:Number = eventSource.mouseX - mousePoint.x;
|
||||
var dy:Number = eventSource.mouseY - mousePoint.y;
|
||||
mousePoint.x = eventSource.mouseX;
|
||||
mousePoint.y = eventSource.mouseY;
|
||||
var v:Vector3D = objectTransform[1];
|
||||
v.x -= dy*Math.PI/180*mouseSensitivity;
|
||||
if (v.x > maxPitch) v.x = maxPitch;
|
||||
if (v.x < minPitch) v.x = minPitch;
|
||||
v.z -= dx*Math.PI/180*mouseSensitivity;
|
||||
moved = true;
|
||||
}
|
||||
|
||||
displacement.x = _right ? 1 : (_left ? -1 : 0);
|
||||
displacement.y = _forward ? 1 : (_back ? -1 : 0);
|
||||
displacement.z = _up ? 1 : (_down ? -1 : 0);
|
||||
if (displacement.lengthSquared > 0) {
|
||||
if (_object is Camera3D) {
|
||||
var tmp:Number = displacement.z;
|
||||
displacement.z = displacement.y;
|
||||
displacement.y = -tmp;
|
||||
}
|
||||
deltaTransformVector(displacement);
|
||||
if (_accelerate) displacement.scaleBy(speedMultiplier*speed*frameTime/displacement.length);
|
||||
else displacement.scaleBy(speed*frameTime/displacement.length);
|
||||
(objectTransform[0] as Vector3D).incrementBy(displacement);
|
||||
moved = true;
|
||||
}
|
||||
|
||||
if (moved) {
|
||||
var m:Matrix3D = new Matrix3D();
|
||||
m.recompose(objectTransform);
|
||||
_object.matrix = m;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets object at given position.
|
||||
* @param pos The position.
|
||||
*/
|
||||
public function setObjectPos(pos:Vector3D):void {
|
||||
if (_object != null) {
|
||||
var v:Vector3D = objectTransform[0];
|
||||
v.x = pos.x;
|
||||
v.y = pos.y;
|
||||
v.z = pos.z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets object at given position.
|
||||
* @param x X.
|
||||
* @param y Y.
|
||||
* @param z Z.
|
||||
*/
|
||||
public function setObjectPosXYZ(x:Number, y:Number, z:Number):void {
|
||||
if (_object != null) {
|
||||
var v:Vector3D = objectTransform[0];
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.z = z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
|
||||
* @param point Point to look at.
|
||||
*/
|
||||
public function lookAt(point:Vector3D):void {
|
||||
lookAtXYZ(point.x, point.y, point.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
|
||||
* @param x X.
|
||||
* @param y Y.
|
||||
* @param z Z.
|
||||
*/
|
||||
public function lookAtXYZ(x:Number, y:Number, z:Number):void {
|
||||
if (_object == null) return;
|
||||
var v:Vector3D = objectTransform[0];
|
||||
var dx:Number = x - v.x;
|
||||
var dy:Number = y - v.y;
|
||||
var dz:Number = z - v.z;
|
||||
v = objectTransform[1];
|
||||
v.x = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy));
|
||||
if (_object is Camera3D) v.x -= 0.5*Math.PI;
|
||||
v.y = 0;
|
||||
v.z = -Math.atan2(dx, dy);
|
||||
var m:Matrix3D = _object.matrix;
|
||||
m.recompose(objectTransform);
|
||||
_object.matrix = m;
|
||||
}
|
||||
|
||||
private var _vin:Vector.<Number> = new Vector.<Number>(3);
|
||||
private var _vout:Vector.<Number> = new Vector.<Number>(3);
|
||||
|
||||
private function deltaTransformVector(v:Vector3D):void {
|
||||
_vin[0] = v.x;
|
||||
_vin[1] = v.y;
|
||||
_vin[2] = v.z;
|
||||
_object.matrix.transformVectors(_vin, _vout);
|
||||
var c:Vector3D = objectTransform[0];
|
||||
v.x = _vout[0] - c.x;
|
||||
v.y = _vout[1] - c.y;
|
||||
v.z = _vout[2] - c.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and stops move forward according to <code>true</code> or <code>false</code> was passed.
|
||||
* @param value Action switcher.
|
||||
*/
|
||||
public function moveForward(value:Boolean):void {
|
||||
_forward = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and stops move backward according to <code>true</code> or <code>false</code> was passed.
|
||||
* @param value Action switcher.
|
||||
*/
|
||||
public function moveBack(value:Boolean):void {
|
||||
_back = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and stops move to left according to <code>true</code> or <code>false</code> was passed.
|
||||
* @param value Action switcher.
|
||||
*/
|
||||
public function moveLeft(value:Boolean):void {
|
||||
_left = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and stops move to right according to <code>true</code> or <code>false</code> was passed.
|
||||
* @param value Action switcher.
|
||||
*/
|
||||
public function moveRight(value:Boolean):void {
|
||||
_right = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and stops move up according to <code>true</code> or <code>false</code> was passed.
|
||||
* @param value Action switcher.
|
||||
*/
|
||||
public function moveUp(value:Boolean):void {
|
||||
_up = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and stops move down according to <code>true</code> or <code>false</code> was passed.
|
||||
* @param value Action switcher.
|
||||
*/
|
||||
public function moveDown(value:Boolean):void {
|
||||
_down = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches acceleration mode.
|
||||
* @param value <code>true</code> turns acceleration on, <code>false</code> turns off.
|
||||
*/
|
||||
public function accelerate(value:Boolean):void {
|
||||
_accelerate = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds key and action. Only one action can be assigned to one key.
|
||||
* @param keyCode Key code.
|
||||
* @param action Action name.
|
||||
* @see #unbindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function bindKey(keyCode:uint, action:String):void {
|
||||
var method:Function = actionBindings[action];
|
||||
if (method != null) keyBindings[keyCode] = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds keys and actions. Only one action can be assigned to one key.
|
||||
* @param bindings Array which consists of sequence of couples of key code and action. An example are follows: <code> [ keyCode1, action1, keyCode2, action2 ] </code>.
|
||||
*/
|
||||
public function bindKeys(bindings:Array):void {
|
||||
for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear binding for given keyCode.
|
||||
* @param keyCode Key code.
|
||||
* @see #bindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function unbindKey(keyCode:uint):void {
|
||||
delete keyBindings[keyCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear binding of all keys.
|
||||
* @see #bindKey()
|
||||
* @see #unbindKey()
|
||||
*/
|
||||
public function unbindAll():void {
|
||||
for (var key:String in keyBindings) delete keyBindings[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default binding.
|
||||
* @see #bindKey()
|
||||
* @see #unbindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function setDefaultBindings():void {
|
||||
bindKey(87, ACTION_FORWARD);
|
||||
bindKey(83, ACTION_BACK);
|
||||
bindKey(65, ACTION_LEFT);
|
||||
bindKey(68, ACTION_RIGHT);
|
||||
bindKey(69, ACTION_UP);
|
||||
bindKey(67, ACTION_DOWN);
|
||||
bindKey(Keyboard.SHIFT, ACTION_ACCELERATE);
|
||||
|
||||
bindKey(Keyboard.UP, ACTION_FORWARD);
|
||||
bindKey(Keyboard.DOWN, ACTION_BACK);
|
||||
bindKey(Keyboard.LEFT, ACTION_LEFT);
|
||||
bindKey(Keyboard.RIGHT, ACTION_RIGHT);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
299
src/alternativa/engine3d/core/BoundBox.as
Normal file
299
src/alternativa/engine3d/core/BoundBox.as
Normal file
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Class stores object's bounding box object's local space. Generally, position of child objects isn't considered at BoundBox calculation.
|
||||
* Ray intersection always made boundBox check at first, but it's possible to check on crossing boundBox only.
|
||||
*
|
||||
*/
|
||||
public class BoundBox {
|
||||
/**
|
||||
* Left face.
|
||||
*/
|
||||
public var minX:Number = 1e+22;
|
||||
/**
|
||||
* Back face.
|
||||
*/
|
||||
public var minY:Number = 1e+22;
|
||||
/**
|
||||
* Bottom face.
|
||||
*/
|
||||
public var minZ:Number = 1e+22;
|
||||
/**
|
||||
* Right face.
|
||||
*/
|
||||
public var maxX:Number = -1e+22;
|
||||
/**
|
||||
* Ftont face.
|
||||
*/
|
||||
public var maxY:Number = -1e+22;
|
||||
/**
|
||||
* Top face.
|
||||
*/
|
||||
public var maxZ:Number = -1e+22;
|
||||
|
||||
|
||||
/**
|
||||
* Resets all bounds values to its initial state.
|
||||
*/
|
||||
public function reset():void {
|
||||
minX = 1e+22;
|
||||
minY = 1e+22;
|
||||
minZ = 1e+22;
|
||||
maxX = -1e+22;
|
||||
maxY = -1e+22;
|
||||
maxZ = -1e+22;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function checkFrustumCulling(frustum:CullingPlane, culling:int):int {
|
||||
var side:int = 1;
|
||||
for (var plane:CullingPlane = frustum; plane != null; plane = plane.next) {
|
||||
if (culling & side) {
|
||||
if (plane.x >= 0) if (plane.y >= 0) if (plane.z >= 0) {
|
||||
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
|
||||
if (minX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else {
|
||||
if (maxX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
|
||||
if (minX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else if (plane.z >= 0) {
|
||||
if (maxX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
|
||||
if (minX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else {
|
||||
if (maxX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
|
||||
if (minX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else if (plane.y >= 0) if (plane.z >= 0) {
|
||||
if (minX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
|
||||
if (maxX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else {
|
||||
if (minX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
|
||||
if (maxX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else if (plane.z >= 0) {
|
||||
if (minX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
|
||||
if (maxX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
} else {
|
||||
if (minX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
|
||||
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
|
||||
}
|
||||
}
|
||||
side <<= 1;
|
||||
}
|
||||
return culling;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function checkOcclusion(occluders:Vector.<Occluder>, occludersLength:int, transform:Transform3D):Boolean {
|
||||
var ax:Number = transform.a*minX + transform.b*minY + transform.c*minZ + transform.d;
|
||||
var ay:Number = transform.e*minX + transform.f*minY + transform.g*minZ + transform.h;
|
||||
var az:Number = transform.i*minX + transform.j*minY + transform.k*minZ + transform.l;
|
||||
var bx:Number = transform.a*maxX + transform.b*minY + transform.c*minZ + transform.d;
|
||||
var by:Number = transform.e*maxX + transform.f*minY + transform.g*minZ + transform.h;
|
||||
var bz:Number = transform.i*maxX + transform.j*minY + transform.k*minZ + transform.l;
|
||||
var cx:Number = transform.a*minX + transform.b*maxY + transform.c*minZ + transform.d;
|
||||
var cy:Number = transform.e*minX + transform.f*maxY + transform.g*minZ + transform.h;
|
||||
var cz:Number = transform.i*minX + transform.j*maxY + transform.k*minZ + transform.l;
|
||||
var dx:Number = transform.a*maxX + transform.b*maxY + transform.c*minZ + transform.d;
|
||||
var dy:Number = transform.e*maxX + transform.f*maxY + transform.g*minZ + transform.h;
|
||||
var dz:Number = transform.i*maxX + transform.j*maxY + transform.k*minZ + transform.l;
|
||||
var ex:Number = transform.a*minX + transform.b*minY + transform.c*maxZ + transform.d;
|
||||
var ey:Number = transform.e*minX + transform.f*minY + transform.g*maxZ + transform.h;
|
||||
var ez:Number = transform.i*minX + transform.j*minY + transform.k*maxZ + transform.l;
|
||||
var fx:Number = transform.a*maxX + transform.b*minY + transform.c*maxZ + transform.d;
|
||||
var fy:Number = transform.e*maxX + transform.f*minY + transform.g*maxZ + transform.h;
|
||||
var fz:Number = transform.i*maxX + transform.j*minY + transform.k*maxZ + transform.l;
|
||||
var gx:Number = transform.a*minX + transform.b*maxY + transform.c*maxZ + transform.d;
|
||||
var gy:Number = transform.e*minX + transform.f*maxY + transform.g*maxZ + transform.h;
|
||||
var gz:Number = transform.i*minX + transform.j*maxY + transform.k*maxZ + transform.l;
|
||||
var hx:Number = transform.a*maxX + transform.b*maxY + transform.c*maxZ + transform.d;
|
||||
var hy:Number = transform.e*maxX + transform.f*maxY + transform.g*maxZ + transform.h;
|
||||
var hz:Number = transform.i*maxX + transform.j*maxY + transform.k*maxZ + transform.l;
|
||||
for (var i:int = 0; i < occludersLength; i++) {
|
||||
var occluder:Occluder = occluders[i];
|
||||
for (var plane:CullingPlane = occluder.planeList; plane != null; plane = plane.next) {
|
||||
if (plane.x*ax + plane.y*ay + plane.z*az > plane.offset ||
|
||||
plane.x*bx + plane.y*by + plane.z*bz > plane.offset ||
|
||||
plane.x*cx + plane.y*cy + plane.z*cz > plane.offset ||
|
||||
plane.x*dx + plane.y*dy + plane.z*dz > plane.offset ||
|
||||
plane.x*ex + plane.y*ey + plane.z*ez > plane.offset ||
|
||||
plane.x*fx + plane.y*fy + plane.z*fz > plane.offset ||
|
||||
plane.x*gx + plane.y*gy + plane.z*gz > plane.offset ||
|
||||
plane.x*hx + plane.y*hy + plane.z*hz > plane.offset) break;
|
||||
}
|
||||
if (plane == null) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function checkRays(origins:Vector.<Vector3D>, directions:Vector.<Vector3D>, raysLength:int):Boolean {
|
||||
for (var i:int = 0; i < raysLength; i++) {
|
||||
var origin:Vector3D = origins[i];
|
||||
var direction:Vector3D = directions[i];
|
||||
if (origin.x >= minX && origin.x <= maxX && origin.y >= minY && origin.y <= maxY && origin.z >= minZ && origin.z <= maxZ) return true;
|
||||
if (origin.x < minX && direction.x <= 0 || origin.x > maxX && direction.x >= 0 || origin.y < minY && direction.y <= 0 || origin.y > maxY && direction.y >= 0 || origin.z < minZ && direction.z <= 0 || origin.z > maxZ && direction.z >= 0) continue;
|
||||
var a:Number;
|
||||
var b:Number;
|
||||
var c:Number;
|
||||
var d:Number;
|
||||
var threshold:Number = 0.000001;
|
||||
// Intersection of X and Y projection
|
||||
if (direction.x > threshold) {
|
||||
a = (minX - origin.x)/direction.x;
|
||||
b = (maxX - origin.x)/direction.x;
|
||||
} else if (direction.x < -threshold) {
|
||||
a = (maxX - origin.x)/direction.x;
|
||||
b = (minX - origin.x)/direction.x;
|
||||
} else {
|
||||
a = 0;
|
||||
b = 1e+22;
|
||||
}
|
||||
if (direction.y > threshold) {
|
||||
c = (minY - origin.y)/direction.y;
|
||||
d = (maxY - origin.y)/direction.y;
|
||||
} else if (direction.y < -threshold) {
|
||||
c = (maxY - origin.y)/direction.y;
|
||||
d = (minY - origin.y)/direction.y;
|
||||
} else {
|
||||
c = 0;
|
||||
d = 1e+22;
|
||||
}
|
||||
if (c >= b || d <= a) continue;
|
||||
if (c < a) {
|
||||
if (d < b) b = d;
|
||||
} else {
|
||||
a = c;
|
||||
if (d < b) b = d;
|
||||
}
|
||||
// Intersection of XY and Z projections
|
||||
if (direction.z > threshold) {
|
||||
c = (minZ - origin.z)/direction.z;
|
||||
d = (maxZ - origin.z)/direction.z;
|
||||
} else if (direction.z < -threshold) {
|
||||
c = (maxZ - origin.z)/direction.z;
|
||||
d = (minZ - origin.z)/direction.z;
|
||||
} else {
|
||||
c = 0;
|
||||
d = 1e+22;
|
||||
}
|
||||
if (c >= b || d <= a) continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function checkSphere(sphere:Vector3D):Boolean {
|
||||
return sphere.x + sphere.w > minX && sphere.x - sphere.w < maxX && sphere.y + sphere.w > minY && sphere.y - sphere.w < maxY && sphere.z + sphere.w > minZ && sphere.z - sphere.w < maxZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the ray crosses the <code>BoundBox</code>.
|
||||
*
|
||||
* @param origin Ray origin.
|
||||
* @param direction Ray direction.
|
||||
* @return <code>true</code> if intersection was found and <code>false</code> otherwise.
|
||||
*/
|
||||
public function intersectRay(origin:Vector3D, direction:Vector3D):Boolean {
|
||||
if (origin.x >= minX && origin.x <= maxX && origin.y >= minY && origin.y <= maxY && origin.z >= minZ && origin.z <= maxZ) return true;
|
||||
if (origin.x < minX && direction.x <= 0) return false;
|
||||
if (origin.x > maxX && direction.x >= 0) return false;
|
||||
if (origin.y < minY && direction.y <= 0) return false;
|
||||
if (origin.y > maxY && direction.y >= 0) return false;
|
||||
if (origin.z < minZ && direction.z <= 0) return false;
|
||||
if (origin.z > maxZ && direction.z >= 0) return false;
|
||||
var a:Number;
|
||||
var b:Number;
|
||||
var c:Number;
|
||||
var d:Number;
|
||||
var threshold:Number = 0.000001;
|
||||
// Intersection of X and Y projection
|
||||
if (direction.x > threshold) {
|
||||
a = (minX - origin.x) / direction.x;
|
||||
b = (maxX - origin.x) / direction.x;
|
||||
} else if (direction.x < -threshold) {
|
||||
a = (maxX - origin.x) / direction.x;
|
||||
b = (minX - origin.x) / direction.x;
|
||||
} else {
|
||||
a = -1e+22;
|
||||
b = 1e+22;
|
||||
}
|
||||
if (direction.y > threshold) {
|
||||
c = (minY - origin.y) / direction.y;
|
||||
d = (maxY - origin.y) / direction.y;
|
||||
} else if (direction.y < -threshold) {
|
||||
c = (maxY - origin.y) / direction.y;
|
||||
d = (minY - origin.y) / direction.y;
|
||||
} else {
|
||||
c = -1e+22;
|
||||
d = 1e+22;
|
||||
}
|
||||
if (c >= b || d <= a) return false;
|
||||
if (c < a) {
|
||||
if (d < b) b = d;
|
||||
} else {
|
||||
a = c;
|
||||
if (d < b) b = d;
|
||||
}
|
||||
// Intersection of XY and Z projections
|
||||
if (direction.z > threshold) {
|
||||
c = (minZ - origin.z) / direction.z;
|
||||
d = (maxZ - origin.z) / direction.z;
|
||||
} else if (direction.z < -threshold) {
|
||||
c = (maxZ - origin.z) / direction.z;
|
||||
d = (minZ - origin.z) / direction.z;
|
||||
} else {
|
||||
c = -1e+22;
|
||||
d = 1e+22;
|
||||
}
|
||||
if (c >= b || d <= a) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates an instance of <code>BoundBox</code>.
|
||||
* @return New <code>BoundBox</code> instance with same set of properties.
|
||||
*/
|
||||
public function clone():BoundBox {
|
||||
var res:BoundBox = new BoundBox();
|
||||
res.minX = minX;
|
||||
res.minY = minY;
|
||||
res.minZ = minZ;
|
||||
res.maxX = maxX;
|
||||
res.maxY = maxY;
|
||||
res.maxZ = maxZ;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of <code>BoundBox</code>.
|
||||
* @return A string representation of <code>BoundBox</code>.
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[BoundBox " + "X:[" + minX.toFixed(2) + ", " + maxX.toFixed(2) + "] Y:[" + minY.toFixed(2) + ", " + maxY.toFixed(2) + "] Z:[" + minZ.toFixed(2) + ", " + maxZ.toFixed(2) + "]]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1188
src/alternativa/engine3d/core/Camera3D.as
Normal file
1188
src/alternativa/engine3d/core/Camera3D.as
Normal file
File diff suppressed because it is too large
Load Diff
50
src/alternativa/engine3d/core/CullingPlane.as
Normal file
50
src/alternativa/engine3d/core/CullingPlane.as
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class CullingPlane {
|
||||
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
public var z:Number;
|
||||
public var offset:Number;
|
||||
|
||||
public var next:CullingPlane;
|
||||
|
||||
static public var collector:CullingPlane;
|
||||
|
||||
static public function create():CullingPlane {
|
||||
if (collector != null) {
|
||||
var res:CullingPlane = collector;
|
||||
collector = res.next;
|
||||
res.next = null;
|
||||
return res;
|
||||
} else {
|
||||
return new CullingPlane();
|
||||
}
|
||||
}
|
||||
|
||||
public function create():CullingPlane {
|
||||
if (collector != null) {
|
||||
var res:CullingPlane = collector;
|
||||
collector = res.next;
|
||||
res.next = null;
|
||||
return res;
|
||||
} else {
|
||||
return new CullingPlane();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
311
src/alternativa/engine3d/core/Debug.as
Normal file
311
src/alternativa/engine3d/core/Debug.as
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.objects.WireFrame;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Class stores values, that are passed to camera methods <code>addToDebug()</code> and <code>removeFromDebug()</code>.
|
||||
*
|
||||
* @see alternativa.engine3d.core.Camera3D#addToDebug()
|
||||
* @see alternativa.engine3d.core.Camera3D#removeFromDebug()
|
||||
*/
|
||||
public class Debug {
|
||||
|
||||
//static public const NAMES:int = 1;
|
||||
|
||||
//static public const AXES:int = 2;
|
||||
|
||||
//static public const CENTERS:int = 4;
|
||||
|
||||
/**
|
||||
* Display of objects bound boxes.
|
||||
*/
|
||||
static public const BOUNDS:int = 8;
|
||||
|
||||
/**
|
||||
* Display of content, that is depended on object type: wireframe for <code>Mesh</code>, schematic display for light sources.
|
||||
* Now has been implemented the support of not all classes
|
||||
*/
|
||||
static public const CONTENT:int = 16;
|
||||
|
||||
//static public const VERTICES:int = 32;
|
||||
|
||||
//static public const NORMALS:int = 64;
|
||||
|
||||
// /**
|
||||
// * Display of object NODES, that contains tree structure.
|
||||
// */
|
||||
// static public const NODES:int = 128;
|
||||
|
||||
// /**
|
||||
// * Display of light sources.
|
||||
// */
|
||||
// static public const LIGHTS:int = 256;
|
||||
|
||||
// /**
|
||||
// * Display of objects joints, that contains skeletal hierarchy.
|
||||
// */
|
||||
// static public const BONES:int = 512;
|
||||
|
||||
static private var boundWires:Dictionary = new Dictionary();
|
||||
|
||||
static private function createBoundWire():WireFrame {
|
||||
var res:WireFrame = new WireFrame();
|
||||
res.geometry.addLine(-0.5,-0.5,-0.5, 0.5,-0.5,-0.5);
|
||||
res.geometry.addLine(0.5,-0.5,-0.5, 0.5,0.5,-0.5);
|
||||
res.geometry.addLine(0.5,0.5,-0.5, -0.5,0.5,-0.5);
|
||||
res.geometry.addLine(-0.5,0.5,-0.5, -0.5,-0.5,-0.5);
|
||||
|
||||
res.geometry.addLine(-0.5,-0.5,0.5, 0.5,-0.5,0.5);
|
||||
res.geometry.addLine(0.5,-0.5,0.5, 0.5,0.5,0.5);
|
||||
res.geometry.addLine(0.5,0.5,0.5, -0.5,0.5,0.5);
|
||||
res.geometry.addLine(-0.5,0.5,0.5, -0.5,-0.5,0.5);
|
||||
|
||||
res.geometry.addLine(-0.5,-0.5,-0.5, -0.5,-0.5,0.5);
|
||||
res.geometry.addLine(0.5,-0.5,-0.5, 0.5,-0.5,0.5);
|
||||
res.geometry.addLine(0.5,0.5,-0.5, 0.5,0.5,0.5);
|
||||
res.geometry.addLine(-0.5,0.5,-0.5, -0.5,0.5,0.5);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d function drawBoundBox(camera:Camera3D, boundBox:BoundBox, transform:Transform3D, color:int = -1):void {
|
||||
var boundWire:WireFrame = boundWires[camera.context3D];
|
||||
if (boundWire == null) {
|
||||
boundWire = createBoundWire();
|
||||
boundWires[camera.context3D] = boundWire;
|
||||
boundWire.geometry.upload(camera.context3D);
|
||||
}
|
||||
boundWire.color = color >= 0 ? color : 0x99FF00;
|
||||
boundWire.thickness = 1;
|
||||
|
||||
boundWire.transform.compose((boundBox.minX + boundBox.maxX)*0.5, (boundBox.minY + boundBox.maxY)*0.5, (boundBox.minZ + boundBox.maxZ)*0.5, 0, 0, 0, boundBox.maxX - boundBox.minX, boundBox.maxY - boundBox.minY, boundBox.maxZ - boundBox.minZ);
|
||||
boundWire.localToCameraTransform.combine(transform, boundWire.transform);
|
||||
boundWire.collectDraws(camera, null, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
/*static alternativa3d function drawEdges(camera:Camera3D, canvas:Canvas, list:Face, color:int):void {
|
||||
var viewSizeX:Number = camera.viewSizeX;
|
||||
var viewSizeY:Number = camera.viewSizeY;
|
||||
var t:Number;
|
||||
canvas.gfx.lineStyle(0, color);
|
||||
for (var face:Face = list; face != null; face = face.processNext) {
|
||||
var wrapper:Wrapper = face.wrapper;
|
||||
var vertex:Vertex = wrapper.vertex;
|
||||
t = 1/vertex.cameraZ;
|
||||
var x:Number = vertex.cameraX*viewSizeX*t;
|
||||
var y:Number = vertex.cameraY*viewSizeY*t;
|
||||
canvas.gfx.moveTo(x, y);
|
||||
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
|
||||
vertex = wrapper.vertex;
|
||||
t = 1/vertex.cameraZ;
|
||||
canvas.gfx.lineTo(vertex.cameraX*viewSizeX*t, vertex.cameraY*viewSizeY*t);
|
||||
}
|
||||
canvas.gfx.lineTo(x, y);
|
||||
}
|
||||
}*/
|
||||
|
||||
//static private const boundVertexList:Vertex = Vertex.createList(8);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
/*static alternativa3d function drawBounds(camera:Camera3D, canvas:Canvas, transformation:Object3D, boundMinX:Number, boundMinY:Number, boundMinZ:Number, boundMaxX:Number, boundMaxY:Number, boundMaxZ:Number, color:int = -1, alpha:Number = 1):void {
|
||||
var vertex:Vertex;
|
||||
// Fill
|
||||
var a:Vertex = boundVertexList;
|
||||
a.x = boundMinX;
|
||||
a.y = boundMinY;
|
||||
a.z = boundMinZ;
|
||||
var b:Vertex = a.next;
|
||||
b.x = boundMaxX;
|
||||
b.y = boundMinY;
|
||||
b.z = boundMinZ;
|
||||
var c:Vertex = b.next;
|
||||
c.x = boundMinX;
|
||||
c.y = boundMaxY;
|
||||
c.z = boundMinZ;
|
||||
var d:Vertex = c.next;
|
||||
d.x = boundMaxX;
|
||||
d.y = boundMaxY;
|
||||
d.z = boundMinZ;
|
||||
var e:Vertex = d.next;
|
||||
e.x = boundMinX;
|
||||
e.y = boundMinY;
|
||||
e.z = boundMaxZ;
|
||||
var f:Vertex = e.next;
|
||||
f.x = boundMaxX;
|
||||
f.y = boundMinY;
|
||||
f.z = boundMaxZ;
|
||||
var g:Vertex = f.next;
|
||||
g.x = boundMinX;
|
||||
g.y = boundMaxY;
|
||||
g.z = boundMaxZ;
|
||||
var h:Vertex = g.next;
|
||||
h.x = boundMaxX;
|
||||
h.y = boundMaxY;
|
||||
h.z = boundMaxZ;
|
||||
// Transformation to camera
|
||||
for (vertex = a; vertex != null; vertex = vertex.next) {
|
||||
vertex.cameraX = transformation.ma*vertex.x + transformation.mb*vertex.y + transformation.mc*vertex.z + transformation.md;
|
||||
vertex.cameraY = transformation.me*vertex.x + transformation.mf*vertex.y + transformation.mg*vertex.z + transformation.mh;
|
||||
vertex.cameraZ = transformation.mi*vertex.x + transformation.mj*vertex.y + transformation.mk*vertex.z + transformation.ml;
|
||||
if (vertex.cameraZ <= 0) return;
|
||||
}
|
||||
// Projection
|
||||
var viewSizeX:Number = camera.viewSizeX;
|
||||
var viewSizeY:Number = camera.viewSizeY;
|
||||
for (vertex = a; vertex != null; vertex = vertex.next) {
|
||||
var t:Number = 1/vertex.cameraZ;
|
||||
vertex.cameraX = vertex.cameraX*viewSizeX*t;
|
||||
vertex.cameraY = vertex.cameraY*viewSizeY*t;
|
||||
}
|
||||
// Rendering
|
||||
canvas.gfx.lineStyle(0, (color < 0) ? ((transformation.culling > 0) ? 0xFFFF00 : 0x00FF00) : color, alpha);
|
||||
canvas.gfx.moveTo(a.cameraX, a.cameraY);
|
||||
canvas.gfx.lineTo(b.cameraX, b.cameraY);
|
||||
canvas.gfx.lineTo(d.cameraX, d.cameraY);
|
||||
canvas.gfx.lineTo(c.cameraX, c.cameraY);
|
||||
canvas.gfx.lineTo(a.cameraX, a.cameraY);
|
||||
canvas.gfx.moveTo(e.cameraX, e.cameraY);
|
||||
canvas.gfx.lineTo(f.cameraX, f.cameraY);
|
||||
canvas.gfx.lineTo(h.cameraX, h.cameraY);
|
||||
canvas.gfx.lineTo(g.cameraX, g.cameraY);
|
||||
canvas.gfx.lineTo(e.cameraX, e.cameraY);
|
||||
canvas.gfx.moveTo(a.cameraX, a.cameraY);
|
||||
canvas.gfx.lineTo(e.cameraX, e.cameraY);
|
||||
canvas.gfx.moveTo(b.cameraX, b.cameraY);
|
||||
canvas.gfx.lineTo(f.cameraX, f.cameraY);
|
||||
canvas.gfx.moveTo(d.cameraX, d.cameraY);
|
||||
canvas.gfx.lineTo(h.cameraX, h.cameraY);
|
||||
canvas.gfx.moveTo(c.cameraX, c.cameraY);
|
||||
canvas.gfx.lineTo(g.cameraX, g.cameraY);
|
||||
}*/
|
||||
|
||||
//static private const nodeVertexList:Vertex = Vertex.createList(4);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
/*static alternativa3d function drawKDNode(camera:Camera3D, canvas:Canvas, transformation:Object3D, axis:int, coord:Number, boundMinX:Number, boundMinY:Number, boundMinZ:Number, boundMaxX:Number, boundMaxY:Number, boundMaxZ:Number, alpha:Number):void {
|
||||
var vertex:Vertex;
|
||||
// Fill
|
||||
var a:Vertex = nodeVertexList;
|
||||
var b:Vertex = a.next;
|
||||
var c:Vertex = b.next;
|
||||
var d:Vertex = c.next;
|
||||
if (axis == 0) {
|
||||
a.x = coord;
|
||||
a.y = boundMinY;
|
||||
a.z = boundMaxZ;
|
||||
b.x = coord;
|
||||
b.y = boundMaxY;
|
||||
b.z = boundMaxZ;
|
||||
c.x = coord;
|
||||
c.y = boundMaxY;
|
||||
c.z = boundMinZ;
|
||||
d.x = coord;
|
||||
d.y = boundMinY;
|
||||
d.z = boundMinZ;
|
||||
} else if (axis == 1) {
|
||||
a.x = boundMaxX;
|
||||
a.y = coord;
|
||||
a.z = boundMaxZ;
|
||||
b.x = boundMinX;
|
||||
b.y = coord;
|
||||
b.z = boundMaxZ;
|
||||
c.x = boundMinX;
|
||||
c.y = coord;
|
||||
c.z = boundMinZ;
|
||||
d.x = boundMaxX;
|
||||
d.y = coord;
|
||||
d.z = boundMinZ;
|
||||
} else {
|
||||
a.x = boundMinX;
|
||||
a.y = boundMinY;
|
||||
a.z = coord;
|
||||
b.x = boundMaxX;
|
||||
b.y = boundMinY;
|
||||
b.z = coord;
|
||||
c.x = boundMaxX;
|
||||
c.y = boundMaxY;
|
||||
c.z = coord;
|
||||
d.x = boundMinX;
|
||||
d.y = boundMaxY;
|
||||
d.z = coord;
|
||||
}
|
||||
// Transformation to camera
|
||||
for (vertex = a; vertex != null; vertex = vertex.next) {
|
||||
vertex.cameraX = transformation.ma*vertex.x + transformation.mb*vertex.y + transformation.mc*vertex.z + transformation.md;
|
||||
vertex.cameraY = transformation.me*vertex.x + transformation.mf*vertex.y + transformation.mg*vertex.z + transformation.mh;
|
||||
vertex.cameraZ = transformation.mi*vertex.x + transformation.mj*vertex.y + transformation.mk*vertex.z + transformation.ml;
|
||||
if (vertex.cameraZ <= 0) return;
|
||||
}
|
||||
// Projection
|
||||
var viewSizeX:Number = camera.viewSizeX;
|
||||
var viewSizeY:Number = camera.viewSizeY;
|
||||
for (vertex = a; vertex != null; vertex = vertex.next) {
|
||||
var t:Number = 1/vertex.cameraZ;
|
||||
vertex.cameraX = vertex.cameraX*viewSizeX*t;
|
||||
vertex.cameraY = vertex.cameraY*viewSizeY*t;
|
||||
}
|
||||
// Rendering
|
||||
canvas.gfx.lineStyle(0, (axis == 0) ? 0xFF0000 : ((axis == 1) ? 0x00FF00 : 0x0000FF), alpha);
|
||||
canvas.gfx.moveTo(a.cameraX, a.cameraY);
|
||||
canvas.gfx.lineTo(b.cameraX, b.cameraY);
|
||||
canvas.gfx.lineTo(c.cameraX, c.cameraY);
|
||||
canvas.gfx.lineTo(d.cameraX, d.cameraY);
|
||||
canvas.gfx.lineTo(a.cameraX, a.cameraY);
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
/*static alternativa3d function drawBone(canvas:Canvas, x1:Number, y1:Number, x2:Number, y2:Number, size:Number, color:int):void {
|
||||
var nx:Number = x2 - x1;
|
||||
var ny:Number = y2 - y1;
|
||||
var nl:Number = Math.sqrt(nx*nx + ny*ny);
|
||||
if (nl > 0.001) {
|
||||
nx /= nl;
|
||||
ny /= nl;
|
||||
var lx:Number = ny*size;
|
||||
var ly:Number = -nx*size;
|
||||
var rx:Number = -ny*size;
|
||||
var ry:Number = nx*size;
|
||||
if (nl > size*2) {
|
||||
nl = size;
|
||||
} else {
|
||||
nl = nl/2;
|
||||
}
|
||||
canvas.gfx.lineStyle(1, color);
|
||||
canvas.gfx.beginFill(color, 0.6);
|
||||
canvas.gfx.moveTo(x1, y1);
|
||||
canvas.gfx.lineTo(x1 + nx*nl + lx, y1 + ny*nl + ly);
|
||||
canvas.gfx.lineTo(x2, y2);
|
||||
canvas.gfx.lineTo(x1 + nx*nl + rx, y1 + ny*nl + ry);
|
||||
canvas.gfx.lineTo(x1, y1);
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
150
src/alternativa/engine3d/core/DebugDrawUnit.as
Normal file
150
src/alternativa/engine3d/core/DebugDrawUnit.as
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.materials.ShaderProgram;
|
||||
import alternativa.engine3d.materials.compiler.Variable;
|
||||
import alternativa.engine3d.materials.compiler.VariableType;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DebugDrawUnit extends DrawUnit {
|
||||
|
||||
alternativa3d var shader:ShaderProgram;
|
||||
|
||||
alternativa3d var vertexConstantsIndexes:Dictionary = new Dictionary(false);
|
||||
alternativa3d var fragmentConstantsIndexes:Dictionary = new Dictionary(false);
|
||||
|
||||
override alternativa3d function clear():void {
|
||||
var k:*;
|
||||
for (k in vertexConstantsIndexes) {
|
||||
delete vertexConstantsIndexes[k];
|
||||
}
|
||||
for (k in fragmentConstantsIndexes) {
|
||||
delete fragmentConstantsIndexes[k];
|
||||
}
|
||||
super.clear();
|
||||
}
|
||||
|
||||
override alternativa3d function setVertexConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
|
||||
super.setVertexConstantsFromVector(firstRegister, data, numRegisters);
|
||||
for (var i:int = 0; i < numRegisters; i++) {
|
||||
vertexConstantsIndexes[int(firstRegister + i)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function setVertexConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
|
||||
super.setVertexConstantsFromNumbers(firstRegister, x, y, z, w);
|
||||
vertexConstantsIndexes[firstRegister] = true;
|
||||
}
|
||||
|
||||
override alternativa3d function setVertexConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
|
||||
super.setVertexConstantsFromTransform(firstRegister, transform);
|
||||
vertexConstantsIndexes[firstRegister] = true;
|
||||
vertexConstantsIndexes[int(firstRegister + 1)] = true;
|
||||
vertexConstantsIndexes[int(firstRegister + 2)] = true;
|
||||
}
|
||||
|
||||
alternativa3d override function setProjectionConstants(camera:Camera3D, firstRegister:int, transform:Transform3D = null):void {
|
||||
super.setProjectionConstants(camera, firstRegister, transform);
|
||||
vertexConstantsIndexes[firstRegister] = true;
|
||||
vertexConstantsIndexes[int(firstRegister + 1)] = true;
|
||||
vertexConstantsIndexes[int(firstRegister + 2)] = true;
|
||||
vertexConstantsIndexes[int(firstRegister + 3)] = true;
|
||||
}
|
||||
|
||||
override alternativa3d function setFragmentConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
|
||||
super.setFragmentConstantsFromVector(firstRegister, data, numRegisters);
|
||||
for (var i:int = 0; i < numRegisters; i++) {
|
||||
fragmentConstantsIndexes[int(firstRegister + i)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function setFragmentConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
|
||||
super.setFragmentConstantsFromNumbers(firstRegister, x, y, z, w);
|
||||
fragmentConstantsIndexes[firstRegister] = true;
|
||||
}
|
||||
|
||||
override alternativa3d function setFragmentConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
|
||||
super.setFragmentConstantsFromTransform(firstRegister, transform);
|
||||
fragmentConstantsIndexes[firstRegister] = true;
|
||||
fragmentConstantsIndexes[int(firstRegister + 1)] = true;
|
||||
fragmentConstantsIndexes[int(firstRegister + 2)] = true;
|
||||
}
|
||||
|
||||
public function check():void {
|
||||
if (object == null) throw new Error("Object not set.");
|
||||
if (program == null) throw new Error("Program not set.");
|
||||
if (indexBuffer == null) throw new Error("IndexBuffer not set.");
|
||||
|
||||
if (shader == null) return;
|
||||
var index:int;
|
||||
var variable:Variable;
|
||||
for each (variable in shader.vertexShader._linkedVariables) {
|
||||
index = variable.index;
|
||||
if (index >= 0) {
|
||||
switch (variable.type) {
|
||||
case VariableType.ATTRIBUTE:
|
||||
if (!hasVertexBuffer(index)) {
|
||||
throw new Error("VertexBuffer " + index + " with variable name " + variable.name + " not set.");
|
||||
}
|
||||
break;
|
||||
case VariableType.CONSTANT:
|
||||
if (!vertexConstantsIndexes[index]) {
|
||||
throw new Error("Vertex Constant " + index + " with variable name " + variable.name + " not set.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for each (variable in shader.fragmentShader._linkedVariables) {
|
||||
index = variable.index;
|
||||
if (index >= 0) {
|
||||
switch (variable.type) {
|
||||
case VariableType.SAMPLER:
|
||||
if (!hasTexture(index)) {
|
||||
throw new Error("Sampler " + index + " with variable name " + variable.name + " not set.");
|
||||
}
|
||||
break;
|
||||
case VariableType.CONSTANT:
|
||||
if (!fragmentConstantsIndexes[index]) {
|
||||
throw new Error("Fragment Constant " + index + " with variable name " + variable.name + " not set.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function hasVertexBuffer(index:int):Boolean {
|
||||
for (var i:int = 0; i < vertexBuffersLength; i++) {
|
||||
if (vertexBuffersIndexes[i] == index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private function hasTexture(index:int):Boolean {
|
||||
for (var i:int = 0; i < texturesLength; i++) {
|
||||
if (texturesSamplers[i] == index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
50
src/alternativa/engine3d/core/DebugMaterialsRenderer.as
Normal file
50
src/alternativa/engine3d/core/DebugMaterialsRenderer.as
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.materials.ShaderProgram;
|
||||
|
||||
import flash.display3D.IndexBuffer3D;
|
||||
import flash.display3D.Program3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DebugMaterialsRenderer extends Renderer {
|
||||
|
||||
override alternativa3d function createDrawUnit(object:Object3D, program:Program3D, indexBuffer:IndexBuffer3D, firstIndex:int, numTriangles:int, debugShader:ShaderProgram = null):DrawUnit {
|
||||
var res:DebugDrawUnit;
|
||||
if (collector != null) {
|
||||
res = DebugDrawUnit(collector);
|
||||
collector = collector.next;
|
||||
res.next = null;
|
||||
} else {
|
||||
res = new DebugDrawUnit();
|
||||
}
|
||||
res.shader = debugShader;
|
||||
res.object = object;
|
||||
res.program = program;
|
||||
res.indexBuffer = indexBuffer;
|
||||
res.firstIndex = firstIndex;
|
||||
res.numTriangles = numTriangles;
|
||||
return res;
|
||||
}
|
||||
|
||||
override alternativa3d function addDrawUnit(drawUnit:DrawUnit, renderPriority:int):void {
|
||||
DebugDrawUnit(drawUnit).check();
|
||||
super.addDrawUnit(drawUnit, renderPriority);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
248
src/alternativa/engine3d/core/DrawUnit.as
Normal file
248
src/alternativa/engine3d/core/DrawUnit.as
Normal file
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display3D.Context3DBlendFactor;
|
||||
import flash.display3D.Context3DTriangleFace;
|
||||
import flash.display3D.IndexBuffer3D;
|
||||
import flash.display3D.Program3D;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.display3D.textures.TextureBase;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DrawUnit {
|
||||
|
||||
alternativa3d var next:DrawUnit;
|
||||
|
||||
// Required parameters
|
||||
alternativa3d var object:Object3D;
|
||||
alternativa3d var program:Program3D;
|
||||
alternativa3d var indexBuffer:IndexBuffer3D;
|
||||
alternativa3d var firstIndex:int;
|
||||
alternativa3d var numTriangles:int;
|
||||
|
||||
// Additional parameters
|
||||
alternativa3d var blendSource:String = Context3DBlendFactor.ONE;
|
||||
alternativa3d var blendDestination:String = Context3DBlendFactor.ZERO;
|
||||
alternativa3d var culling:String = Context3DTriangleFace.FRONT;
|
||||
|
||||
// Textures
|
||||
alternativa3d var textures:Vector.<TextureBase> = new Vector.<TextureBase>();
|
||||
alternativa3d var texturesSamplers:Vector.<int> = new Vector.<int>();
|
||||
alternativa3d var texturesLength:int = 0;
|
||||
|
||||
// Vertex buffers
|
||||
alternativa3d var vertexBuffers:Vector.<VertexBuffer3D> = new Vector.<VertexBuffer3D>();
|
||||
alternativa3d var vertexBuffersIndexes:Vector.<int> = new Vector.<int>();
|
||||
alternativa3d var vertexBuffersOffsets:Vector.<int> = new Vector.<int>();
|
||||
alternativa3d var vertexBuffersFormats:Vector.<String> = new Vector.<String>();
|
||||
alternativa3d var vertexBuffersLength:int = 0;
|
||||
|
||||
// Constants
|
||||
alternativa3d var vertexConstants:Vector.<Number> = new Vector.<Number>();
|
||||
alternativa3d var vertexConstantsRegistersCount:int = 0;
|
||||
alternativa3d var fragmentConstants:Vector.<Number> = new Vector.<Number>(28*4, true);
|
||||
alternativa3d var fragmentConstantsRegistersCount:int = 0;
|
||||
|
||||
public function DrawUnit() {
|
||||
}
|
||||
|
||||
alternativa3d function clear():void {
|
||||
object = null;
|
||||
program = null;
|
||||
indexBuffer = null;
|
||||
blendSource = Context3DBlendFactor.ONE;
|
||||
blendDestination = Context3DBlendFactor.ZERO;
|
||||
culling = Context3DTriangleFace.FRONT;
|
||||
textures.length = 0;
|
||||
texturesLength = 0;
|
||||
vertexBuffers.length = 0;
|
||||
vertexBuffersLength = 0;
|
||||
vertexConstantsRegistersCount = 0;
|
||||
fragmentConstantsRegistersCount = 0;
|
||||
}
|
||||
|
||||
alternativa3d function setTextureAt(sampler:int, texture:TextureBase):void {
|
||||
if (uint(sampler) > 8) throw new Error("Sampler index " + sampler + " is out of bounds.");
|
||||
if (texture == null) throw new Error("Texture is null");
|
||||
texturesSamplers[texturesLength] = sampler;
|
||||
textures[texturesLength] = texture;
|
||||
texturesLength++;
|
||||
}
|
||||
|
||||
alternativa3d function setVertexBufferAt(index:int, buffer:VertexBuffer3D, bufferOffset:int, format:String):void {
|
||||
if (uint(index) > 8) throw new Error("VertexBuffer index " + index + " is out of bounds.");
|
||||
if (buffer == null) throw new Error("Buffer is null");
|
||||
vertexBuffersIndexes[vertexBuffersLength] = index;
|
||||
vertexBuffers[vertexBuffersLength] = buffer;
|
||||
vertexBuffersOffsets[vertexBuffersLength] = bufferOffset;
|
||||
vertexBuffersFormats[vertexBuffersLength] = format;
|
||||
vertexBuffersLength++;
|
||||
}
|
||||
|
||||
alternativa3d function setVertexConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
|
||||
if (uint(firstRegister) > (128 - numRegisters)) throw new Error("Register index " + firstRegister + " is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + numRegisters > vertexConstantsRegistersCount) {
|
||||
vertexConstantsRegistersCount = firstRegister + numRegisters;
|
||||
vertexConstants.length = vertexConstantsRegistersCount << 2;
|
||||
}
|
||||
for (var i:int = 0, len:int = numRegisters << 2; i < len; i++) {
|
||||
vertexConstants[offset] = data[i];
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function setVertexConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
|
||||
if (uint(firstRegister) > 127) throw new Error("Register index " + firstRegister + " is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + 1 > vertexConstantsRegistersCount) {
|
||||
vertexConstantsRegistersCount = firstRegister + 1;
|
||||
vertexConstants.length = vertexConstantsRegistersCount << 2;
|
||||
}
|
||||
vertexConstants[offset] = x; offset++;
|
||||
vertexConstants[offset] = y; offset++;
|
||||
vertexConstants[offset] = z; offset++;
|
||||
vertexConstants[offset] = w;
|
||||
}
|
||||
|
||||
alternativa3d function setVertexConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
|
||||
if (uint(firstRegister) > 125) throw new Error("Register index " + firstRegister + " is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + 3 > vertexConstantsRegistersCount) {
|
||||
vertexConstantsRegistersCount = firstRegister + 3;
|
||||
vertexConstants.length = vertexConstantsRegistersCount << 2;
|
||||
}
|
||||
vertexConstants[offset] = transform.a; offset++;
|
||||
vertexConstants[offset] = transform.b; offset++;
|
||||
vertexConstants[offset] = transform.c; offset++;
|
||||
vertexConstants[offset] = transform.d; offset++;
|
||||
vertexConstants[offset] = transform.e; offset++;
|
||||
vertexConstants[offset] = transform.f; offset++;
|
||||
vertexConstants[offset] = transform.g; offset++;
|
||||
vertexConstants[offset] = transform.h; offset++;
|
||||
vertexConstants[offset] = transform.i; offset++;
|
||||
vertexConstants[offset] = transform.j; offset++;
|
||||
vertexConstants[offset] = transform.k; offset++;
|
||||
vertexConstants[offset] = transform.l;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function setProjectionConstants(camera:Camera3D, firstRegister:int, transform:Transform3D = null):void {
|
||||
if (uint(firstRegister) > 124) throw new Error("Register index is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + 4 > vertexConstantsRegistersCount) {
|
||||
vertexConstantsRegistersCount = firstRegister + 4;
|
||||
vertexConstants.length = vertexConstantsRegistersCount << 2;
|
||||
}
|
||||
if (transform != null) {
|
||||
vertexConstants[offset] = transform.a*camera.m0; offset++;
|
||||
vertexConstants[offset] = transform.b*camera.m0; offset++;
|
||||
vertexConstants[offset] = transform.c*camera.m0; offset++;
|
||||
vertexConstants[offset] = transform.d*camera.m0; offset++;
|
||||
vertexConstants[offset] = transform.e*camera.m5; offset++;
|
||||
vertexConstants[offset] = transform.f*camera.m5; offset++;
|
||||
vertexConstants[offset] = transform.g*camera.m5; offset++;
|
||||
vertexConstants[offset] = transform.h*camera.m5; offset++;
|
||||
vertexConstants[offset] = transform.i*camera.m10; offset++;
|
||||
vertexConstants[offset] = transform.j*camera.m10; offset++;
|
||||
vertexConstants[offset] = transform.k*camera.m10; offset++;
|
||||
vertexConstants[offset] = transform.l*camera.m10 + camera.m14; offset++;
|
||||
if (!camera.orthographic) {
|
||||
vertexConstants[offset] = transform.i; offset++;
|
||||
vertexConstants[offset] = transform.j; offset++;
|
||||
vertexConstants[offset] = transform.k; offset++;
|
||||
vertexConstants[offset] = transform.l;
|
||||
} else {
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 1;
|
||||
}
|
||||
} else {
|
||||
vertexConstants[offset] = camera.m0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = camera.m5; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = camera.m10; offset++;
|
||||
vertexConstants[offset] = camera.m14; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
if (!camera.orthographic) {
|
||||
vertexConstants[offset] = 1; offset++;
|
||||
vertexConstants[offset] = 0;
|
||||
} else {
|
||||
vertexConstants[offset] = 0; offset++;
|
||||
vertexConstants[offset] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function setFragmentConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
|
||||
if (uint(firstRegister) > (28 - numRegisters)) throw new Error("Register index " + firstRegister + " is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + numRegisters > fragmentConstantsRegistersCount) {
|
||||
fragmentConstantsRegistersCount = firstRegister + numRegisters;
|
||||
}
|
||||
for (var i:int = 0, len:int = numRegisters << 2; i < len; i++) {
|
||||
fragmentConstants[offset] = data[i];
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function setFragmentConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
|
||||
if (uint(firstRegister) > 27) throw new Error("Register index " + firstRegister + " is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + 1 > fragmentConstantsRegistersCount) {
|
||||
fragmentConstantsRegistersCount = firstRegister + 1;
|
||||
}
|
||||
fragmentConstants[offset] = x; offset++;
|
||||
fragmentConstants[offset] = y; offset++;
|
||||
fragmentConstants[offset] = z; offset++;
|
||||
fragmentConstants[offset] = w;
|
||||
}
|
||||
|
||||
alternativa3d function setFragmentConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
|
||||
if (uint(firstRegister) > 25) throw new Error("Register index " + firstRegister + " is out of bounds.");
|
||||
var offset:int = firstRegister << 2;
|
||||
if (firstRegister + 3 > fragmentConstantsRegistersCount) {
|
||||
fragmentConstantsRegistersCount = firstRegister + 3;
|
||||
}
|
||||
fragmentConstants[offset] = transform.a; offset++;
|
||||
fragmentConstants[offset] = transform.b; offset++;
|
||||
fragmentConstants[offset] = transform.c; offset++;
|
||||
fragmentConstants[offset] = transform.d; offset++;
|
||||
fragmentConstants[offset] = transform.e; offset++;
|
||||
fragmentConstants[offset] = transform.f; offset++;
|
||||
fragmentConstants[offset] = transform.g; offset++;
|
||||
fragmentConstants[offset] = transform.h; offset++;
|
||||
fragmentConstants[offset] = transform.i; offset++;
|
||||
fragmentConstants[offset] = transform.j; offset++;
|
||||
fragmentConstants[offset] = transform.k; offset++;
|
||||
fragmentConstants[offset] = transform.l;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
116
src/alternativa/engine3d/core/Light3D.as
Normal file
116
src/alternativa/engine3d/core/Light3D.as
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.shadows.Shadow;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Base class for light sources. Light sources are involved in the hierarchy of 3d objects,
|
||||
* have transformation and bounding boxes (<code>BoundBox</code>).
|
||||
* Light source influences on objects, boundboxes of which intersect with boundbox of the given light source.
|
||||
*
|
||||
* Light3D does not meant for instantiating, use subclasses instead.
|
||||
*
|
||||
* @see alternativa.engine3d.core.BoundBox
|
||||
*/
|
||||
public class Light3D extends Object3D {
|
||||
|
||||
public var shadow:Shadow;
|
||||
|
||||
/**
|
||||
* Color of the light.
|
||||
*/
|
||||
public var color:uint;
|
||||
|
||||
/**
|
||||
* Intensity.
|
||||
*/
|
||||
public var intensity:Number = 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var lightToObjectTransform:Transform3D = new Transform3D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var lightID:String;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var red:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var green:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var blue:Number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var lastLightNumber:uint = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function Light3D() {
|
||||
lightID = "l" + lastLightNumber.toString(16);
|
||||
name = "L" + (lastLightNumber++).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function calculateVisibility(camera:Camera3D):void {
|
||||
if (intensity != 0 && color > 0) {
|
||||
camera.lights[camera.lightsLength] = this;
|
||||
camera.lightsLength++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Check if given object placed in field of influence of the light.
|
||||
* @param targetObject Object for checking.
|
||||
* @return True
|
||||
*/
|
||||
alternativa3d function checkBound(targetObject:Object3D):Boolean {
|
||||
// this check is implemented in subclasses
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Object3D {
|
||||
var res:Light3D = new Light3D();
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function clonePropertiesFrom(source:Object3D):void {
|
||||
super.clonePropertiesFrom(source);
|
||||
var src:Light3D = source as Light3D;
|
||||
color = src.color;
|
||||
intensity = src.intensity;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1453
src/alternativa/engine3d/core/Object3D.as
Normal file
1453
src/alternativa/engine3d/core/Object3D.as
Normal file
File diff suppressed because it is too large
Load Diff
1386
src/alternativa/engine3d/core/Occluder.as
Normal file
1386
src/alternativa/engine3d/core/Occluder.as
Normal file
File diff suppressed because it is too large
Load Diff
59
src/alternativa/engine3d/core/RayIntersectionData.as
Normal file
59
src/alternativa/engine3d/core/RayIntersectionData.as
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
* A result of searching for intersection of an Object3D and a ray with intersectRay() method of Object3D.
|
||||
*
|
||||
* @see Object3D#intersectRay()
|
||||
*/
|
||||
public class RayIntersectionData {
|
||||
|
||||
/**
|
||||
* First object intersected by the ray.
|
||||
*/
|
||||
public var object:Object3D;
|
||||
|
||||
/**
|
||||
* The point of intersection il local coordinates of object.
|
||||
*/
|
||||
public var point:Vector3D;
|
||||
|
||||
/**
|
||||
* Surface of <code>object</code> on which intersection occurred.
|
||||
*/
|
||||
public var surface:Surface;
|
||||
|
||||
/**
|
||||
* Distance from ray's origin to intersection point expressed in length of <code>localDirection</code> vector.
|
||||
*/
|
||||
public var time:Number;
|
||||
|
||||
/**
|
||||
* Texture coordinates of intersection point.
|
||||
*/
|
||||
public var uv:Point;
|
||||
|
||||
/**
|
||||
* Returns the string representation of the specified object.
|
||||
* @return The string representation of the specified object.
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[RayIntersectionData " + object + ", " + point + ", " + uv + ", " + time + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
238
src/alternativa/engine3d/core/Renderer.as
Normal file
238
src/alternativa/engine3d/core/Renderer.as
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.materials.ShaderProgram;
|
||||
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DCompareMode;
|
||||
import flash.display3D.Context3DProgramType;
|
||||
import flash.display3D.IndexBuffer3D;
|
||||
import flash.display3D.Program3D;
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Renderer {
|
||||
|
||||
public static const SKY:int = 10;
|
||||
|
||||
public static const OPAQUE:int = 20;
|
||||
|
||||
public static const DECALS:int = 30;
|
||||
|
||||
public static const TRANSPARENT_SORT:int = 40;
|
||||
|
||||
public static const NEXT_LAYER:int = 50;
|
||||
|
||||
// Collector
|
||||
protected var collector:DrawUnit;
|
||||
|
||||
alternativa3d var camera:Camera3D;
|
||||
|
||||
alternativa3d var drawUnits:Vector.<DrawUnit> = new Vector.<DrawUnit>();
|
||||
|
||||
// Key - context, value - properties.
|
||||
protected static var properties:Dictionary = new Dictionary(true);
|
||||
|
||||
protected var _context3D:Context3D;
|
||||
protected var _contextProperties:RendererContext3DProperties;
|
||||
|
||||
alternativa3d function render(context3D:Context3D):void {
|
||||
updateContext3D(context3D);
|
||||
|
||||
var drawUnitsLength:int = drawUnits.length;
|
||||
for (var i:int = 0; i < drawUnitsLength; i++) {
|
||||
var list:DrawUnit = drawUnits[i];
|
||||
if (list != null) {
|
||||
switch (i) {
|
||||
case SKY:
|
||||
_context3D.setDepthTest(false, Context3DCompareMode.ALWAYS);
|
||||
break;
|
||||
case OPAQUE:
|
||||
_context3D.setDepthTest(true, Context3DCompareMode.LESS);
|
||||
break;
|
||||
case DECALS:
|
||||
_context3D.setDepthTest(false, Context3DCompareMode.LESS_EQUAL);
|
||||
break;
|
||||
case TRANSPARENT_SORT:
|
||||
if (list.next != null) list = sortByAverageZ(list);
|
||||
_context3D.setDepthTest(false, Context3DCompareMode.LESS);
|
||||
break;
|
||||
case NEXT_LAYER:
|
||||
_context3D.setDepthTest(false, Context3DCompareMode.ALWAYS);
|
||||
break;
|
||||
}
|
||||
// Rendering
|
||||
while (list != null) {
|
||||
var next:DrawUnit = list.next;
|
||||
renderDrawUnit(list, _context3D, camera);
|
||||
// Send to collector
|
||||
list.clear();
|
||||
list.next = collector;
|
||||
collector = list;
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear
|
||||
drawUnits.length = 0;
|
||||
}
|
||||
|
||||
alternativa3d function createDrawUnit(object:Object3D, program:Program3D, indexBuffer:IndexBuffer3D, firstIndex:int, numTriangles:int, debugShader:ShaderProgram = null):DrawUnit {
|
||||
var res:DrawUnit;
|
||||
if (collector != null) {
|
||||
res = collector;
|
||||
collector = collector.next;
|
||||
res.next = null;
|
||||
} else {
|
||||
//trace("new DrawUnit");
|
||||
res = new DrawUnit();
|
||||
}
|
||||
res.object = object;
|
||||
res.program = program;
|
||||
res.indexBuffer = indexBuffer;
|
||||
res.firstIndex = firstIndex;
|
||||
res.numTriangles = numTriangles;
|
||||
return res;
|
||||
}
|
||||
|
||||
alternativa3d function addDrawUnit(drawUnit:DrawUnit, renderPriority:int):void {
|
||||
// Increase array of priorities, if it is necessary
|
||||
if (renderPriority >= drawUnits.length) drawUnits.length = renderPriority + 1;
|
||||
// Add
|
||||
drawUnit.next = drawUnits[renderPriority];
|
||||
drawUnits[renderPriority] = drawUnit;
|
||||
}
|
||||
|
||||
protected function renderDrawUnit(drawUnit:DrawUnit, context:Context3D, camera:Camera3D):void {
|
||||
context.setBlendFactors(drawUnit.blendSource, drawUnit.blendDestination);
|
||||
context.setCulling(drawUnit.culling);
|
||||
var _usedBuffers:uint = _contextProperties.usedBuffers;
|
||||
var _usedTextures:uint = _contextProperties.usedTextures;
|
||||
|
||||
var bufferIndex:int;
|
||||
var bufferBit:int;
|
||||
var currentBuffers:int;
|
||||
var textureSampler:int;
|
||||
var textureBit:int;
|
||||
var currentTextures:int;
|
||||
for (var i:int = 0; i < drawUnit.vertexBuffersLength; i++) {
|
||||
bufferIndex = drawUnit.vertexBuffersIndexes[i];
|
||||
bufferBit = 1 << bufferIndex;
|
||||
currentBuffers |= bufferBit;
|
||||
_usedBuffers &= ~bufferBit;
|
||||
context.setVertexBufferAt(bufferIndex, drawUnit.vertexBuffers[i], drawUnit.vertexBuffersOffsets[i], drawUnit.vertexBuffersFormats[i]);
|
||||
}
|
||||
if (drawUnit.vertexConstantsRegistersCount > 0) {
|
||||
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 0, drawUnit.vertexConstants, drawUnit.vertexConstantsRegistersCount);
|
||||
}
|
||||
if (drawUnit.fragmentConstantsRegistersCount > 0) {
|
||||
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, drawUnit.fragmentConstants, drawUnit.fragmentConstantsRegistersCount);
|
||||
}
|
||||
for (i = 0; i < drawUnit.texturesLength; i++) {
|
||||
textureSampler = drawUnit.texturesSamplers[i];
|
||||
textureBit = 1 << textureSampler;
|
||||
currentTextures |= textureBit;
|
||||
_usedTextures &= ~textureBit;
|
||||
context.setTextureAt(textureSampler, drawUnit.textures[i]);
|
||||
}
|
||||
context.setProgram(drawUnit.program);
|
||||
for (bufferIndex = 0; _usedBuffers > 0; bufferIndex++) {
|
||||
bufferBit = _usedBuffers & 1;
|
||||
_usedBuffers >>= 1;
|
||||
if (bufferBit) context.setVertexBufferAt(bufferIndex, null);
|
||||
}
|
||||
for (textureSampler = 0; _usedTextures > 0; textureSampler++) {
|
||||
textureBit = _usedTextures & 1;
|
||||
_usedTextures >>= 1;
|
||||
if (textureBit) context.setTextureAt(textureSampler, null);
|
||||
}
|
||||
context.drawTriangles(drawUnit.indexBuffer, drawUnit.firstIndex, drawUnit.numTriangles);
|
||||
_contextProperties.usedBuffers = currentBuffers;
|
||||
_contextProperties.usedTextures = currentTextures;
|
||||
camera.numDraws++;
|
||||
camera.numTriangles += drawUnit.numTriangles;
|
||||
}
|
||||
|
||||
protected function updateContext3D(value:Context3D):void {
|
||||
if (_context3D != value) {
|
||||
_contextProperties = properties[value];
|
||||
if (_contextProperties == null) {
|
||||
_contextProperties = new RendererContext3DProperties();
|
||||
properties[value] = _contextProperties;
|
||||
}
|
||||
_context3D = value;
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function sortByAverageZ(list:DrawUnit, direction:Boolean = true):DrawUnit {
|
||||
var left:DrawUnit = list;
|
||||
var right:DrawUnit = list.next;
|
||||
while (right != null && right.next != null) {
|
||||
list = list.next;
|
||||
right = right.next.next;
|
||||
}
|
||||
right = list.next;
|
||||
list.next = null;
|
||||
if (left.next != null) {
|
||||
left = sortByAverageZ(left, direction);
|
||||
}
|
||||
if (right.next != null) {
|
||||
right = sortByAverageZ(right, direction);
|
||||
}
|
||||
var flag:Boolean = direction ? (left.object.localToCameraTransform.l > right.object.localToCameraTransform.l) : (left.object.localToCameraTransform.l < right.object.localToCameraTransform.l);
|
||||
if (flag) {
|
||||
list = left;
|
||||
left = left.next;
|
||||
} else {
|
||||
list = right;
|
||||
right = right.next;
|
||||
}
|
||||
var last:DrawUnit = list;
|
||||
while (true) {
|
||||
if (left == null) {
|
||||
last.next = right;
|
||||
return list;
|
||||
} else if (right == null) {
|
||||
last.next = left;
|
||||
return list;
|
||||
}
|
||||
if (flag) {
|
||||
if (direction ? (left.object.localToCameraTransform.l > right.object.localToCameraTransform.l) : (left.object.localToCameraTransform.l < right.object.localToCameraTransform.l)) {
|
||||
last = left;
|
||||
left = left.next;
|
||||
} else {
|
||||
last.next = right;
|
||||
last = right;
|
||||
right = right.next;
|
||||
flag = false;
|
||||
}
|
||||
} else {
|
||||
if (direction ? (left.object.localToCameraTransform.l < right.object.localToCameraTransform.l) : (left.object.localToCameraTransform.l > right.object.localToCameraTransform.l)) {
|
||||
last = right;
|
||||
right = right.next;
|
||||
} else {
|
||||
last.next = left;
|
||||
last = left;
|
||||
left = left.next;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/alternativa/engine3d/core/RendererContext3DProperties.as
Normal file
23
src/alternativa/engine3d/core/RendererContext3DProperties.as
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Stores settings of context.
|
||||
*/
|
||||
public class RendererContext3DProperties {
|
||||
|
||||
public var usedBuffers:uint = 0;
|
||||
public var usedTextures:uint = 0;
|
||||
|
||||
}
|
||||
}
|
||||
57
src/alternativa/engine3d/core/Resource.as
Normal file
57
src/alternativa/engine3d/core/Resource.as
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display3D.Context3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Base class for GPU data. GPU data can be divided in 2 groups: geometry data and texture data.
|
||||
* The type of resources for uploading geometry data in GPU is Geometry.
|
||||
* <code>BitmapTextureResource</code> allows to use textures of type is <code>BitmapData</code> and <code>ATFTextureResource</code> deals with <code>ByteArray</code> consists of ATF data,
|
||||
* <code>ExternalTextureResource</code> should be used with <code>TexturesLoader</code>, which loads textures from files and automatically uploads in GPU.
|
||||
*
|
||||
*
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
* @see alternativa.engine3d.resources.TextureResource
|
||||
* @see alternativa.engine3d.resources.BitmapTextureResource
|
||||
* @see alternativa.engine3d.resources.ATFTextureResource
|
||||
* @see alternativa.engine3d.resources.ExternalTextureResource
|
||||
*/
|
||||
public class Resource {
|
||||
|
||||
/**
|
||||
* Defines if this resource is uploaded inti a <code>Context3D</code>.
|
||||
*/
|
||||
public function get isUploaded():Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads resource into given <code>Context3D</code>.
|
||||
*
|
||||
* @param context3D <code>Context3D</code> to which resource will uploaded.
|
||||
*/
|
||||
public function upload(context3D:Context3D):void {
|
||||
throw new Error("Cannot upload without data");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this resource from <code>Context3D</code> to which it was uploaded.
|
||||
*/
|
||||
public function dispose():void {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
269
src/alternativa/engine3d/core/Transform3D.as
Normal file
269
src/alternativa/engine3d/core/Transform3D.as
Normal file
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Transform3D {
|
||||
|
||||
public var a:Number = 1;
|
||||
public var b:Number = 0;
|
||||
public var c:Number = 0;
|
||||
public var d:Number = 0;
|
||||
|
||||
public var e:Number = 0;
|
||||
public var f:Number = 1;
|
||||
public var g:Number = 0;
|
||||
public var h:Number = 0;
|
||||
|
||||
public var i:Number = 0;
|
||||
public var j:Number = 0;
|
||||
public var k:Number = 1;
|
||||
public var l:Number = 0;
|
||||
|
||||
public function identity():void {
|
||||
a = 1;
|
||||
b = 0;
|
||||
c = 0;
|
||||
d = 0;
|
||||
e = 0;
|
||||
f = 1;
|
||||
g = 0;
|
||||
h = 0;
|
||||
i = 0;
|
||||
j = 0;
|
||||
k = 1;
|
||||
l = 0;
|
||||
}
|
||||
|
||||
public function compose(x:Number, y:Number, z:Number, rotationX:Number, rotationY:Number, rotationZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number):void {
|
||||
var cosX:Number = Math.cos(rotationX);
|
||||
var sinX:Number = Math.sin(rotationX);
|
||||
var cosY:Number = Math.cos(rotationY);
|
||||
var sinY:Number = Math.sin(rotationY);
|
||||
var cosZ:Number = Math.cos(rotationZ);
|
||||
var sinZ:Number = Math.sin(rotationZ);
|
||||
var cosZsinY:Number = cosZ*sinY;
|
||||
var sinZsinY:Number = sinZ*sinY;
|
||||
var cosYscaleX:Number = cosY*scaleX;
|
||||
var sinXscaleY:Number = sinX*scaleY;
|
||||
var cosXscaleY:Number = cosX*scaleY;
|
||||
var cosXscaleZ:Number = cosX*scaleZ;
|
||||
var sinXscaleZ:Number = sinX*scaleZ;
|
||||
a = cosZ*cosYscaleX;
|
||||
b = cosZsinY*sinXscaleY - sinZ*cosXscaleY;
|
||||
c = cosZsinY*cosXscaleZ + sinZ*sinXscaleZ;
|
||||
d = x;
|
||||
e = sinZ*cosYscaleX;
|
||||
f = sinZsinY*sinXscaleY + cosZ*cosXscaleY;
|
||||
g = sinZsinY*cosXscaleZ - cosZ*sinXscaleZ;
|
||||
h = y;
|
||||
i = -sinY*scaleX;
|
||||
j = cosY*sinXscaleY;
|
||||
k = cosY*cosXscaleZ;
|
||||
l = z;
|
||||
}
|
||||
|
||||
public function composeInverse(x:Number, y:Number, z:Number, rotationX:Number, rotationY:Number, rotationZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number):void {
|
||||
var cosX:Number = Math.cos(rotationX);
|
||||
var sinX:Number = Math.sin(-rotationX);
|
||||
var cosY:Number = Math.cos(rotationY);
|
||||
var sinY:Number = Math.sin(-rotationY);
|
||||
var cosZ:Number = Math.cos(rotationZ);
|
||||
var sinZ:Number = Math.sin(-rotationZ);
|
||||
var sinXsinY:Number = sinX*sinY;
|
||||
var cosYscaleX:Number = cosY/scaleX;
|
||||
var cosXscaleY:Number = cosX/scaleY;
|
||||
var sinXscaleZ:Number = sinX/scaleZ;
|
||||
var cosXscaleZ:Number = cosX/scaleZ;
|
||||
a = cosZ*cosYscaleX;
|
||||
b = -sinZ*cosYscaleX;
|
||||
c = sinY/scaleX;
|
||||
d = -a*x - b*y - c*z;
|
||||
e = sinZ*cosXscaleY + sinXsinY*cosZ/scaleY;
|
||||
f = cosZ*cosXscaleY - sinXsinY*sinZ/scaleY;
|
||||
g = -sinX*cosY/scaleY;
|
||||
h = -e*x - f*y - g*z;
|
||||
i = sinZ*sinXscaleZ - cosZ*sinY*cosXscaleZ;
|
||||
j = cosZ*sinXscaleZ + sinY*sinZ*cosXscaleZ;
|
||||
k = cosY*cosXscaleZ;
|
||||
l = -i*x - j*y - k*z;
|
||||
}
|
||||
|
||||
public function invert():void {
|
||||
var ta:Number = a;
|
||||
var tb:Number = b;
|
||||
var tc:Number = c;
|
||||
var td:Number = d;
|
||||
var te:Number = e;
|
||||
var tf:Number = f;
|
||||
var tg:Number = g;
|
||||
var th:Number = h;
|
||||
var ti:Number = i;
|
||||
var tj:Number = j;
|
||||
var tk:Number = k;
|
||||
var tl:Number = l;
|
||||
var det:Number = 1/(-tc*tf*ti + tb*tg*ti + tc*te*tj - ta*tg*tj - tb*te*tk + ta*tf*tk);
|
||||
a = (-tg*tj + tf*tk)*det;
|
||||
b = (tc*tj - tb*tk)*det;
|
||||
c = (-tc*tf + tb*tg)*det;
|
||||
d = (td*tg*tj - tc*th*tj - td*tf*tk + tb*th*tk + tc*tf*tl - tb*tg*tl)*det;
|
||||
e = (tg*ti - te*tk)*det;
|
||||
f = (-tc*ti + ta*tk)*det;
|
||||
g = (tc*te - ta*tg)*det;
|
||||
h = (tc*th*ti - td*tg*ti + td*te*tk - ta*th*tk - tc*te*tl + ta*tg*tl)*det;
|
||||
i = (-tf*ti + te*tj)*det;
|
||||
j = (tb*ti - ta*tj)*det;
|
||||
k = (-tb*te + ta*tf)*det;
|
||||
l = (td*tf*ti - tb*th*ti - td*te*tj + ta*th*tj + tb*te*tl - ta*tf*tl)*det;
|
||||
}
|
||||
|
||||
public function initFromVector(vector:Vector.<Number>):void {
|
||||
a = vector[0];
|
||||
b = vector[1];
|
||||
c = vector[2];
|
||||
d = vector[3];
|
||||
e = vector[4];
|
||||
f = vector[5];
|
||||
g = vector[6];
|
||||
h = vector[7];
|
||||
i = vector[8];
|
||||
j = vector[9];
|
||||
k = vector[10];
|
||||
l = vector[11];
|
||||
}
|
||||
|
||||
public function append(transform:Transform3D):void {
|
||||
var ta:Number = a;
|
||||
var tb:Number = b;
|
||||
var tc:Number = c;
|
||||
var td:Number = d;
|
||||
var te:Number = e;
|
||||
var tf:Number = f;
|
||||
var tg:Number = g;
|
||||
var th:Number = h;
|
||||
var ti:Number = i;
|
||||
var tj:Number = j;
|
||||
var tk:Number = k;
|
||||
var tl:Number = l;
|
||||
a = transform.a*ta + transform.b*te + transform.c*ti;
|
||||
b = transform.a*tb + transform.b*tf + transform.c*tj;
|
||||
c = transform.a*tc + transform.b*tg + transform.c*tk;
|
||||
d = transform.a*td + transform.b*th + transform.c*tl + transform.d;
|
||||
e = transform.e*ta + transform.f*te + transform.g*ti;
|
||||
f = transform.e*tb + transform.f*tf + transform.g*tj;
|
||||
g = transform.e*tc + transform.f*tg + transform.g*tk;
|
||||
h = transform.e*td + transform.f*th + transform.g*tl + transform.h;
|
||||
i = transform.i*ta + transform.j*te + transform.k*ti;
|
||||
j = transform.i*tb + transform.j*tf + transform.k*tj;
|
||||
k = transform.i*tc + transform.j*tg + transform.k*tk;
|
||||
l = transform.i*td + transform.j*th + transform.k*tl + transform.l;
|
||||
}
|
||||
|
||||
public function prepend(transform:Transform3D):void {
|
||||
var ta:Number = a;
|
||||
var tb:Number = b;
|
||||
var tc:Number = c;
|
||||
var td:Number = d;
|
||||
var te:Number = e;
|
||||
var tf:Number = f;
|
||||
var tg:Number = g;
|
||||
var th:Number = h;
|
||||
var ti:Number = i;
|
||||
var tj:Number = j;
|
||||
var tk:Number = k;
|
||||
var tl:Number = l;
|
||||
a = ta*transform.a + tb*transform.e + tc*transform.i;
|
||||
b = ta*transform.b + tb*transform.f + tc*transform.j;
|
||||
c = ta*transform.c + tb*transform.g + tc*transform.k;
|
||||
d = ta*transform.d + tb*transform.h + tc*transform.l + td;
|
||||
e = te*transform.a + tf*transform.e + tg*transform.i;
|
||||
f = te*transform.b + tf*transform.f + tg*transform.j;
|
||||
g = te*transform.c + tf*transform.g + tg*transform.k;
|
||||
h = te*transform.d + tf*transform.h + tg*transform.l + th;
|
||||
i = ti*transform.a + tj*transform.e + tk*transform.i;
|
||||
j = ti*transform.b + tj*transform.f + tk*transform.j;
|
||||
k = ti*transform.c + tj*transform.g + tk*transform.k;
|
||||
l = ti*transform.d + tj*transform.h + tk*transform.l + tl;
|
||||
|
||||
}
|
||||
|
||||
public function combine(transformA:Transform3D, transformB:Transform3D):void {
|
||||
a = transformA.a*transformB.a + transformA.b*transformB.e + transformA.c*transformB.i;
|
||||
b = transformA.a*transformB.b + transformA.b*transformB.f + transformA.c*transformB.j;
|
||||
c = transformA.a*transformB.c + transformA.b*transformB.g + transformA.c*transformB.k;
|
||||
d = transformA.a*transformB.d + transformA.b*transformB.h + transformA.c*transformB.l + transformA.d;
|
||||
e = transformA.e*transformB.a + transformA.f*transformB.e + transformA.g*transformB.i;
|
||||
f = transformA.e*transformB.b + transformA.f*transformB.f + transformA.g*transformB.j;
|
||||
g = transformA.e*transformB.c + transformA.f*transformB.g + transformA.g*transformB.k;
|
||||
h = transformA.e*transformB.d + transformA.f*transformB.h + transformA.g*transformB.l + transformA.h;
|
||||
i = transformA.i*transformB.a + transformA.j*transformB.e + transformA.k*transformB.i;
|
||||
j = transformA.i*transformB.b + transformA.j*transformB.f + transformA.k*transformB.j;
|
||||
k = transformA.i*transformB.c + transformA.j*transformB.g + transformA.k*transformB.k;
|
||||
l = transformA.i*transformB.d + transformA.j*transformB.h + transformA.k*transformB.l + transformA.l;
|
||||
}
|
||||
|
||||
public function calculateInversion(source:Transform3D):void {
|
||||
var ta:Number = source.a;
|
||||
var tb:Number = source.b;
|
||||
var tc:Number = source.c;
|
||||
var td:Number = source.d;
|
||||
var te:Number = source.e;
|
||||
var tf:Number = source.f;
|
||||
var tg:Number = source.g;
|
||||
var th:Number = source.h;
|
||||
var ti:Number = source.i;
|
||||
var tj:Number = source.j;
|
||||
var tk:Number = source.k;
|
||||
var tl:Number = source.l;
|
||||
var det:Number = 1/(-tc*tf*ti + tb*tg*ti + tc*te*tj - ta*tg*tj - tb*te*tk + ta*tf*tk);
|
||||
a = (-tg*tj + tf*tk)*det;
|
||||
b = (tc*tj - tb*tk)*det;
|
||||
c = (-tc*tf + tb*tg)*det;
|
||||
d = (td*tg*tj - tc*th*tj - td*tf*tk + tb*th*tk + tc*tf*tl - tb*tg*tl)*det;
|
||||
e = (tg*ti - te*tk)*det;
|
||||
f = (-tc*ti + ta*tk)*det;
|
||||
g = (tc*te - ta*tg)*det;
|
||||
h = (tc*th*ti - td*tg*ti + td*te*tk - ta*th*tk - tc*te*tl + ta*tg*tl)*det;
|
||||
i = (-tf*ti + te*tj)*det;
|
||||
j = (tb*ti - ta*tj)*det;
|
||||
k = (-tb*te + ta*tf)*det;
|
||||
l = (td*tf*ti - tb*th*ti - td*te*tj + ta*th*tj + tb*te*tl - ta*tf*tl)*det;
|
||||
}
|
||||
|
||||
public function copy(source:Transform3D):void {
|
||||
a = source.a;
|
||||
b = source.b;
|
||||
c = source.c;
|
||||
d = source.d;
|
||||
e = source.e;
|
||||
f = source.f;
|
||||
g = source.g;
|
||||
h = source.h;
|
||||
i = source.i;
|
||||
j = source.j;
|
||||
k = source.k;
|
||||
l = source.l;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return "[Transform3D" +
|
||||
" a:" + a.toFixed(3) + " b:" + b.toFixed(3) + " c:" + a.toFixed(3) + " d:" + d.toFixed(3) +
|
||||
" e:" + e.toFixed(3) + " f:" + f.toFixed(3) + " g:" + a.toFixed(3) + " h:" + h.toFixed(3) +
|
||||
" i:" + i.toFixed(3) + " j:" + j.toFixed(3) + " k:" + a.toFixed(3) + " l:" + l.toFixed(3) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
112
src/alternativa/engine3d/core/VertexAttributes.as
Normal file
112
src/alternativa/engine3d/core/VertexAttributes.as
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display3D.Context3DVertexBufferFormat;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Types of attributes which defines format of vertex streams. It can be used as values of array,
|
||||
* passed to <code>geometry.addVertexStream(attributes)</code> as an argument.
|
||||
*
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
*/
|
||||
public class VertexAttributes {
|
||||
/**
|
||||
* Coordinates in 3D space. Defines by sequence of three floats.
|
||||
*
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
* @see #getAttributeStride()
|
||||
*/
|
||||
public static const POSITION:uint = 1;
|
||||
/**
|
||||
* Vertex normal. Defines by sequence of three floats.
|
||||
*
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
* @see #getAttributeStride()
|
||||
*/
|
||||
public static const NORMAL:uint = 2;
|
||||
/**
|
||||
* This data type combines values of vertex tangent and binormal within one sequence of four floats.
|
||||
* The first three values defines tangent direction and the fourth can be 1 or -1 which defines to what side binormal is ordered.
|
||||
*
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
*/
|
||||
public static const TANGENT4:uint = 3;
|
||||
/**
|
||||
* Data of linking of two <code>Joint</code>s with vertex. Defines by sequence of four floats in following order:
|
||||
* id of the first <code>Joint</code> multiplied with 3, power of influence of the first <code>Joint</code>,
|
||||
* id of the second <code>Joint</code> multiplied with 3, power of influence of the second <code>Joint</code>.
|
||||
* There are a four 'slots' for this data type, so influence of 8 <code>Joint</code>s can be described.
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
* @see alternativa.engine3d.objects.Skin
|
||||
*/
|
||||
public static const JOINTS:Vector.<uint> = Vector.<uint>([4,5,6,7]);
|
||||
|
||||
/**
|
||||
* Texture coordinates data type. There are a 8 independent channels. Coordinates defines by the couples (u, v).
|
||||
*
|
||||
* @see alternativa.engine3d.resources.Geometry
|
||||
*/
|
||||
public static const TEXCOORDS:Vector.<uint> = Vector.<uint>([8,9,10,11,12,13,14,15]);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static const FORMATS:Array = [
|
||||
Context3DVertexBufferFormat.FLOAT_1, //NONE
|
||||
Context3DVertexBufferFormat.FLOAT_3, //POSITION
|
||||
Context3DVertexBufferFormat.FLOAT_3, //NORMAL
|
||||
Context3DVertexBufferFormat.FLOAT_4, //TANGENT4
|
||||
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[0]
|
||||
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[1]
|
||||
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[2]
|
||||
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[3]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[0]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[1]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[2]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[3]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[4]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[5]
|
||||
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[6]
|
||||
Context3DVertexBufferFormat.FLOAT_2 //TEXCOORDS[7]
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns a dimensions of given attribute type (Number of floats by which defines given type)
|
||||
*
|
||||
* @param attribute Type of the attribute.
|
||||
* @return
|
||||
*/
|
||||
public static function getAttributeStride(attribute:int):int {
|
||||
switch(FORMATS[attribute]) {
|
||||
case Context3DVertexBufferFormat.FLOAT_1:
|
||||
return 1;
|
||||
break;
|
||||
case Context3DVertexBufferFormat.FLOAT_2:
|
||||
return 2;
|
||||
break;
|
||||
case Context3DVertexBufferFormat.FLOAT_3:
|
||||
return 3;
|
||||
break;
|
||||
case Context3DVertexBufferFormat.FLOAT_4:
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
24
src/alternativa/engine3d/core/VertexStream.as
Normal file
24
src/alternativa/engine3d/core/VertexStream.as
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class VertexStream {
|
||||
public var buffer:VertexBuffer3D;
|
||||
public var attributes:Array;
|
||||
public var data:ByteArray;
|
||||
}
|
||||
}
|
||||
1432
src/alternativa/engine3d/core/View.as
Normal file
1432
src/alternativa/engine3d/core/View.as
Normal file
File diff suppressed because it is too large
Load Diff
140
src/alternativa/engine3d/core/events/Event3D.as
Normal file
140
src/alternativa/engine3d/core/events/Event3D.as
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core.events {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.*;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
use namespace alternativa3d;
|
||||
public class Event3D extends Event {
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>added</code> event object.
|
||||
* @eventType added
|
||||
*/
|
||||
public static const ADDED:String = "added3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>removed</code> event object.
|
||||
|
||||
* @eventType removed
|
||||
*/
|
||||
public static const REMOVED:String = "removed3D";
|
||||
|
||||
|
||||
/**
|
||||
* This class should be used as base class for all events, which can have <code>Object3D</code> as an event target.
|
||||
* @param type
|
||||
* @param bubbles
|
||||
*/
|
||||
public function Event3D(type:String, bubbles:Boolean = true) {
|
||||
super(type, bubbles);
|
||||
_bubbles = bubbles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _target:Object3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _currentTarget:Object3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _bubbles:Boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _eventPhase:uint = 3;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var stop:Boolean = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var stopImmediate:Boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether an event is a bubbling event. If the event can bubble, this value is <code>true</code>; otherwise it is <code>false</code>.
|
||||
*/
|
||||
override public function get bubbles():Boolean {
|
||||
return _bubbles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current phase in the event flow.
|
||||
*/
|
||||
override public function get eventPhase():uint {
|
||||
return _eventPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event target. This property contains the target node.
|
||||
*/
|
||||
override public function get target():Object {
|
||||
return _target;
|
||||
}
|
||||
|
||||
/**
|
||||
* The object that is actively processing the Event object with an event listener.
|
||||
*/
|
||||
override public function get currentTarget():Object {
|
||||
return _currentTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents processing of any event listeners in nodes subsequent to the current node in the event flow.
|
||||
* Does not affect on receiving events in listeners of (<code>currentTarget</code>).
|
||||
*/
|
||||
override public function stopPropagation():void {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents processing of any event listeners in the current node and any subsequent nodes in the event flow.
|
||||
*/
|
||||
override public function stopImmediatePropagation():void {
|
||||
stopImmediate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates an instance of an Event subclass.
|
||||
* Returns a new <code>Event3D</code> object that is a copy of the original instance of the <code>Event</code> object.
|
||||
* @return A new <code>Event3D</code> object that is identical to the original.
|
||||
*/
|
||||
override public function clone():Event {
|
||||
var result:Event3D = new Event3D(type, _bubbles);
|
||||
result._target = _target;
|
||||
result._currentTarget = _currentTarget;
|
||||
result._eventPhase = _eventPhase;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string containing all the properties of the <code>Event3D</code> object.
|
||||
* @return A string containing all the properties of the <code>Event3D</code> object
|
||||
*/
|
||||
override public function toString():String {
|
||||
return formatToString("Event3D", "type", "bubbles", "eventPhase");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
187
src/alternativa/engine3d/core/events/MouseEvent3D.as
Normal file
187
src/alternativa/engine3d/core/events/MouseEvent3D.as
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.core.events {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.*;
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
*
|
||||
* Event <code>MouseEvent3D</code> dispatches by <code>Object3D</code>, in cases when <code>MouseEvent</code> dispatches by <code>DisplayObject</code>.
|
||||
*/
|
||||
public class MouseEvent3D extends Event3D {
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>click3D</code> event object.
|
||||
* @eventType click3D
|
||||
*/
|
||||
public static const CLICK:String = "click3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>doubleClick3D</code> event object.
|
||||
* @eventType doubleClick3D
|
||||
*/
|
||||
public static const DOUBLE_CLICK:String = "doubleClick3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>mouseDown3D</code> event object.
|
||||
* @eventType mouseDown3D
|
||||
*/
|
||||
public static const MOUSE_DOWN:String = "mouseDown3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>mouseUp3D</code> event object.
|
||||
* @eventType mouseUp3D
|
||||
*/
|
||||
public static const MOUSE_UP:String = "mouseUp3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>mouseOver3D</code> event object.
|
||||
* @eventType mouseOver3D
|
||||
*/
|
||||
public static const MOUSE_OVER:String = "mouseOver3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>mouseOut3D</code> event object.
|
||||
* @eventType mouseOut3D
|
||||
*/
|
||||
public static const MOUSE_OUT:String = "mouseOut3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>rollOver3D</code> event object.
|
||||
* @eventType rollOver3D
|
||||
*/
|
||||
public static const ROLL_OVER:String = "rollOver3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>rollOut3D</code> event object.
|
||||
* @eventType rollOut3D
|
||||
*/
|
||||
public static const ROLL_OUT:String = "rollOut3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>mouseMove3D</code> event object.
|
||||
* @eventType mouseMove3D
|
||||
*/
|
||||
public static const MOUSE_MOVE:String = "mouseMove3D";
|
||||
|
||||
/**
|
||||
* Defines the value of the <code>type</code> property of a <code>mouseWheel3D</code> event object.
|
||||
* @eventType mouseWheel3D
|
||||
*/
|
||||
public static const MOUSE_WHEEL:String = "mouseWheel3D";
|
||||
|
||||
/**
|
||||
* On Windows or Linux, indicates whether the Ctrl key is active (<code>true</code>) or inactive (<code>false</code>). On Macintosh, indicates whether either the Control key or the Command key is activated.
|
||||
*/
|
||||
public var ctrlKey:Boolean;
|
||||
/**
|
||||
* Indicates whether the Alt key is active (<code>true</code>) or inactive (<code>false</code>).
|
||||
*/
|
||||
public var altKey:Boolean;
|
||||
/**
|
||||
* Indicates whether the Shift key is active (<code>true</code>) or inactive (<code>false</code>).
|
||||
*/
|
||||
public var shiftKey:Boolean;
|
||||
/**
|
||||
* Indicates whether the main mouse button is active (<code>true</code>) or inactive (<code>false</code>).
|
||||
*/
|
||||
public var buttonDown:Boolean;
|
||||
/**
|
||||
* Indicates how many lines should be scrolled for each unit the user rotates the mouse wheel.
|
||||
*/
|
||||
public var delta:int;
|
||||
|
||||
/**
|
||||
* A reference to an object that is related to the event. This property applies to the <code>mouseOut</code>, <code>mouseOver</code>, <code>rollOut</code> и <code>rollOver</code>.
|
||||
* For example, when <code>mouseOut</code> occurs, <code>relatedObject</code> point to the object over which mouse cursor placed now.
|
||||
*/
|
||||
public var relatedObject:Object3D;
|
||||
|
||||
/**
|
||||
* X coordinate of the event at local target object's space.
|
||||
*/
|
||||
public var localX:Number;
|
||||
|
||||
/**
|
||||
* Y coordinate of the event at local target object's space.
|
||||
*/
|
||||
public var localY:Number;
|
||||
|
||||
/**
|
||||
* Z coordinate of the event at local target object's space.
|
||||
*/
|
||||
public var localZ:Number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _surface:Surface;
|
||||
|
||||
/**
|
||||
* Creates a MouseEvent3D object.
|
||||
* @param type Type.
|
||||
* @param bubbles Indicates whether an event is a bubbling event.
|
||||
* @param localY Y coordinate of the event at local target object's space.
|
||||
* @param localX X coordinate of the event at local target object's space.
|
||||
* @param localZ Z coordinate of the event at local target object's space.
|
||||
* @param relatedObject <code>Object3D</code>, eelated to the <code>MouseEvent3D</code>.
|
||||
* @param altKey Indicates whether the Alt key is active.
|
||||
* @param ctrlKey Indicates whether the Control key is active.
|
||||
* @param shiftKey Indicates whether the Shift key is active.
|
||||
* @param buttonDown Indicates whether the main mouse button is active .
|
||||
* @param delta Indicates how many lines should be scrolled for each unit the user rotates the mouse wheel.
|
||||
*/
|
||||
public function MouseEvent3D(type:String, bubbles:Boolean = true, localX:Number = NaN, localY:Number = NaN, localZ:Number = NaN, relatedObject:Object3D = null, ctrlKey:Boolean = false, altKey:Boolean = false, shiftKey:Boolean = false, buttonDown:Boolean = false, delta:int = 0) {
|
||||
super(type, bubbles);
|
||||
this.localX = localX;
|
||||
this.localY = localY;
|
||||
this.localZ = localZ;
|
||||
this.relatedObject = relatedObject;
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.altKey = altKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.buttonDown = buttonDown;
|
||||
this.delta = delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>Surface</code> on which event has been received. The object that owns the surface, can differs from the target event.
|
||||
*
|
||||
*/
|
||||
public function get surface():Surface {
|
||||
return _surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates an instance of an Event subclass.
|
||||
* Returns a new <code>MouseEvent3D</code> object that is a copy of the original instance of the Event object.
|
||||
* @return A new <code>MouseEvent3D</code> object that is identical to the original.
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new MouseEvent3D(type, _bubbles, localX, localY, localZ, relatedObject, ctrlKey, altKey, shiftKey, buttonDown, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string containing all the properties of the <code>MouseEvent3D</code> object.
|
||||
* @return A string containing all the properties of the <code>MouseEvent3D</code> object
|
||||
*/
|
||||
override public function toString():String {
|
||||
return formatToString("MouseEvent3D", "type", "bubbles", "eventPhase", "localX", "localY", "localZ", "relatedObject", "altKey", "ctrlKey", "shiftKey", "buttonDown", "delta");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
724
src/alternativa/engine3d/effects/AGALMiniAssembler.as
Normal file
724
src/alternativa/engine3d/effects/AGALMiniAssembler.as
Normal file
@@ -0,0 +1,724 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.effects {
|
||||
// ===========================================================================
|
||||
// Imports
|
||||
// ---------------------------------------------------------------------------
|
||||
//import flash.display3D.*;
|
||||
|
||||
import flash.utils.*;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class AGALMiniAssembler
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
// AGAL bytes and error buffer
|
||||
private var _agalcode:ByteArray = null;
|
||||
private var _error:String = "";
|
||||
|
||||
private var debugEnabled:Boolean = false;
|
||||
|
||||
private static var initialized:Boolean = false;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get error():String { return _error; }
|
||||
public function get agalcode():ByteArray { return _agalcode; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function AGALMiniAssembler( debugging:Boolean = false ):void
|
||||
{
|
||||
debugEnabled = debugging;
|
||||
if ( !initialized )
|
||||
init();
|
||||
}
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function assemble( mode:String, source:String, verbose:Boolean = false ):ByteArray
|
||||
{
|
||||
var start:uint = getTimer();
|
||||
|
||||
_agalcode = new ByteArray();
|
||||
_error = "";
|
||||
|
||||
var isFrag:Boolean = false;
|
||||
|
||||
if ( mode == FRAGMENT )
|
||||
isFrag = true
|
||||
else if ( mode != VERTEX )
|
||||
_error = 'ERROR: mode needs to be "' + FRAGMENT + '" or "' + VERTEX + '" but is "' + mode + '".';
|
||||
|
||||
agalcode.endian = Endian.LITTLE_ENDIAN;
|
||||
agalcode.writeByte( 0xa0 ); // tag version
|
||||
agalcode.writeUnsignedInt( 0x1 ); // AGAL version, big endian, bit pattern will be 0x01000000
|
||||
agalcode.writeByte( 0xa1 ); // tag program id
|
||||
agalcode.writeByte( isFrag ? 1 : 0 ); // vertex or fragment
|
||||
|
||||
var lines:Array = source.replace( /[\f\n\r\v]+/g, "\n" ).split( "\n" );
|
||||
var nest:int = 0;
|
||||
var nops:int = 0;
|
||||
var i:int;
|
||||
var lng:int = lines.length;
|
||||
|
||||
for ( i = 0; i < lng && _error == ""; i++ )
|
||||
{
|
||||
var line:String = new String( lines[i] );
|
||||
|
||||
// remove comments
|
||||
var startcomment:int = line.search( "//" );
|
||||
if ( startcomment != -1 )
|
||||
line = line.slice( 0, startcomment );
|
||||
|
||||
// grab options
|
||||
var optsi:int = line.search( /<.*>/g );
|
||||
var opts:Array;
|
||||
if ( optsi != -1 )
|
||||
{
|
||||
opts = line.slice( optsi ).match( /([\w\.\-\+]+)/gi );
|
||||
line = line.slice( 0, optsi );
|
||||
}
|
||||
|
||||
// find opcode
|
||||
var opCode:Array = line.match( /^\w{3}/ig );
|
||||
var opFound:OpCode = OPMAP[ opCode[0] ];
|
||||
|
||||
// if debug is enabled, output the opcodes
|
||||
if ( debugEnabled )
|
||||
trace( opFound );
|
||||
|
||||
if ( opFound == null )
|
||||
{
|
||||
if ( line.length >= 3 )
|
||||
trace( "warning: bad line "+i+": "+lines[i] );
|
||||
continue;
|
||||
}
|
||||
|
||||
line = line.slice( line.search( opFound.name ) + opFound.name.length );
|
||||
|
||||
// nesting check
|
||||
if ( opFound.flags & OP_DEC_NEST )
|
||||
{
|
||||
nest--;
|
||||
if ( nest < 0 )
|
||||
{
|
||||
_error = "error: conditional closes without open.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( opFound.flags & OP_INC_NEST )
|
||||
{
|
||||
nest++;
|
||||
if ( nest > MAX_NESTING )
|
||||
{
|
||||
_error = "error: nesting to deep, maximum allowed is "+MAX_NESTING+".";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ( opFound.flags & OP_FRAG_ONLY ) && !isFrag )
|
||||
{
|
||||
_error = "error: opcode is only allowed in fragment programs.";
|
||||
break;
|
||||
}
|
||||
if ( verbose )
|
||||
trace( "emit opcode=" + opFound );
|
||||
|
||||
agalcode.writeUnsignedInt( opFound.emitCode );
|
||||
nops++;
|
||||
|
||||
if ( nops > MAX_OPCODES )
|
||||
{
|
||||
_error = "error: too many opcodes. maximum is "+MAX_OPCODES+".";
|
||||
break;
|
||||
}
|
||||
|
||||
// get operands, use regexp
|
||||
var regs:Array = line.match( /vc\[([vof][actps]?)(\d*)?(\.[xyzw](\+\d{1,3})?)?\](\.[xyzw]{1,4})?|([vof][actps]?)(\d*)?(\.[xyzw]{1,4})?/gi );
|
||||
if ( regs.length != opFound.numRegister )
|
||||
{
|
||||
_error = "error: wrong number of operands. found "+regs.length+" but expected "+opFound.numRegister+".";
|
||||
break;
|
||||
}
|
||||
|
||||
var badreg:Boolean = false;
|
||||
var pad:uint = 64 + 64 + 32;
|
||||
var regLength:uint = regs.length;
|
||||
|
||||
for ( var j:int = 0; j < regLength; j++ )
|
||||
{
|
||||
var isRelative:Boolean = false;
|
||||
var relreg:Array = regs[ j ].match( /\[.*\]/ig );
|
||||
if ( relreg.length > 0 )
|
||||
{
|
||||
regs[ j ] = regs[ j ].replace( relreg[ 0 ], "0" );
|
||||
|
||||
if ( verbose )
|
||||
trace( "IS REL" );
|
||||
isRelative = true;
|
||||
}
|
||||
|
||||
var res:Array = regs[j].match( /^\b[A-Za-z]{1,2}/ig );
|
||||
var regFound:Register = REGMAP[ res[ 0 ] ];
|
||||
|
||||
// if debug is enabled, output the registers
|
||||
if ( debugEnabled )
|
||||
trace( regFound );
|
||||
|
||||
if ( regFound == null )
|
||||
{
|
||||
_error = "error: could not parse operand "+j+" ("+regs[j]+").";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( isFrag )
|
||||
{
|
||||
if ( !( regFound.flags & REG_FRAG ) )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") only allowed in vertex programs.";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
if ( isRelative )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") relative adressing not allowed in fragment programs.";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !( regFound.flags & REG_VERT ) )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") only allowed in fragment programs.";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
regs[j] = regs[j].slice( regs[j].search( regFound.name ) + regFound.name.length );
|
||||
//trace( "REGNUM: " +regs[j] );
|
||||
var idxmatch:Array = isRelative ? relreg[0].match( /\d+/ ) : regs[j].match( /\d+/ );
|
||||
var regidx:uint = 0;
|
||||
|
||||
if ( idxmatch )
|
||||
regidx = uint( idxmatch[0] );
|
||||
|
||||
if ( regFound.range < regidx )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") index exceeds limit of "+(regFound.range+1)+".";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var regmask:uint = 0;
|
||||
var maskmatch:Array = regs[j].match( /(\.[xyzw]{1,4})/ );
|
||||
var isDest:Boolean = ( j == 0 && !( opFound.flags & OP_NO_DEST ) );
|
||||
var isSampler:Boolean = ( j == 2 && ( opFound.flags & OP_SPECIAL_TEX ) );
|
||||
var reltype:uint = 0;
|
||||
var relsel:uint = 0;
|
||||
var reloffset:int = 0;
|
||||
|
||||
if ( isDest && isRelative )
|
||||
{
|
||||
_error = "error: relative can not be destination";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( maskmatch )
|
||||
{
|
||||
regmask = 0;
|
||||
var cv:uint;
|
||||
var maskLength:uint = maskmatch[0].length;
|
||||
for ( var k:int = 1; k < maskLength; k++ )
|
||||
{
|
||||
cv = maskmatch[0].charCodeAt(k) - "x".charCodeAt(0);
|
||||
if ( cv > 2 )
|
||||
cv = 3;
|
||||
if ( isDest )
|
||||
regmask |= 1 << cv;
|
||||
else
|
||||
regmask |= cv << ( ( k - 1 ) << 1 );
|
||||
}
|
||||
if ( !isDest )
|
||||
for ( ; k <= 4; k++ )
|
||||
regmask |= cv << ( ( k - 1 ) << 1 ) // repeat last
|
||||
}
|
||||
else
|
||||
{
|
||||
regmask = isDest ? 0xf : 0xe4; // id swizzle or mask
|
||||
}
|
||||
|
||||
if ( isRelative )
|
||||
{
|
||||
var relname:Array = relreg[0].match( /[A-Za-z]{1,2}/ig );
|
||||
var regFoundRel:Register = REGMAP[ relname[0]];
|
||||
if ( regFoundRel == null )
|
||||
{
|
||||
_error = "error: bad index register";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
reltype = regFoundRel.emitCode;
|
||||
var selmatch:Array = relreg[0].match( /(\.[xyzw]{1,1})/ );
|
||||
if ( selmatch.length==0 )
|
||||
{
|
||||
_error = "error: bad index register select";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
relsel = selmatch[0].charCodeAt(1) - "x".charCodeAt(0);
|
||||
if ( relsel > 2 )
|
||||
relsel = 3;
|
||||
var relofs:Array = relreg[0].match( /\+\d{1,3}/ig );
|
||||
if ( relofs.length > 0 )
|
||||
reloffset = relofs[0];
|
||||
if ( reloffset < 0 || reloffset > 255 )
|
||||
{
|
||||
_error = "error: index offset "+reloffset+" out of bounds. [0..255]";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
if ( verbose )
|
||||
trace( "RELATIVE: type="+reltype+"=="+relname[0]+" sel="+relsel+"=="+selmatch[0]+" idx="+regidx+" offset="+reloffset );
|
||||
}
|
||||
|
||||
if ( verbose )
|
||||
trace( " emit argcode="+regFound+"["+regidx+"]["+regmask+"]" );
|
||||
if ( isDest )
|
||||
{
|
||||
agalcode.writeShort( regidx );
|
||||
agalcode.writeByte( regmask );
|
||||
agalcode.writeByte( regFound.emitCode );
|
||||
pad -= 32;
|
||||
} else
|
||||
{
|
||||
if ( isSampler )
|
||||
{
|
||||
if ( verbose )
|
||||
trace( " emit sampler" );
|
||||
var samplerbits:uint = 5; // type 5
|
||||
var optsLength:uint = opts.length;
|
||||
var bias:Number = 0;
|
||||
for ( k = 0; k<optsLength; k++ )
|
||||
{
|
||||
if ( verbose )
|
||||
trace( " opt: "+opts[k] );
|
||||
var optfound:Sampler = SAMPLEMAP [opts[k]];
|
||||
if ( optfound == null )
|
||||
{
|
||||
// todo check that it's a number...
|
||||
//trace( "Warning, unknown sampler option: "+opts[k] );
|
||||
bias = Number(opts[k]);
|
||||
if ( verbose )
|
||||
trace( " bias: " + bias );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( optfound.flag != SAMPLER_SPECIAL_SHIFT )
|
||||
samplerbits &= ~( 0xf << optfound.flag );
|
||||
samplerbits |= uint( optfound.mask ) << uint( optfound.flag );
|
||||
}
|
||||
}
|
||||
agalcode.writeShort( regidx );
|
||||
agalcode.writeByte(int(bias*8.0));
|
||||
agalcode.writeByte(0);
|
||||
agalcode.writeUnsignedInt( samplerbits );
|
||||
|
||||
if ( verbose )
|
||||
trace( " bits: " + ( samplerbits - 5 ) );
|
||||
pad -= 64;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( j == 0 )
|
||||
{
|
||||
agalcode.writeUnsignedInt( 0 );
|
||||
pad -= 32;
|
||||
}
|
||||
agalcode.writeShort( regidx );
|
||||
agalcode.writeByte( reloffset );
|
||||
agalcode.writeByte( regmask );
|
||||
agalcode.writeByte( regFound.emitCode );
|
||||
agalcode.writeByte( reltype );
|
||||
agalcode.writeShort( isRelative ? ( relsel | ( 1 << 15 ) ) : 0 );
|
||||
|
||||
pad -= 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pad unused regs
|
||||
for ( j = 0; j < pad; j += 8 )
|
||||
agalcode.writeByte( 0 );
|
||||
|
||||
if ( badreg )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( _error != "" )
|
||||
{
|
||||
_error += "\n at line " + i + " " + lines[i];
|
||||
agalcode.length = 0;
|
||||
trace( _error );
|
||||
}
|
||||
|
||||
// trace the bytecode bytes if debugging is enabled
|
||||
if ( debugEnabled )
|
||||
{
|
||||
var dbgLine:String = "generated bytecode:";
|
||||
var agalLength:uint = agalcode.length;
|
||||
for ( var index:uint = 0; index < agalLength; index++ )
|
||||
{
|
||||
if ( !( index % 16 ) )
|
||||
dbgLine += "\n";
|
||||
if ( !( index % 4 ) )
|
||||
dbgLine += " ";
|
||||
|
||||
var byteStr:String = agalcode[ index ].toString( 16 );
|
||||
if ( byteStr.length < 2 )
|
||||
byteStr = "0" + byteStr;
|
||||
|
||||
dbgLine += byteStr;
|
||||
}
|
||||
trace( dbgLine );
|
||||
}
|
||||
|
||||
if ( verbose )
|
||||
trace( "AGALMiniAssembler.assemble time: " + ( ( getTimer() - start ) / 1000 ) + "s" );
|
||||
|
||||
return agalcode;
|
||||
}
|
||||
|
||||
static private function init():void
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
// Fill the dictionaries with opcodes and registers
|
||||
OPMAP[ MOV ] = new OpCode( MOV, 2, 0x00, 0 );
|
||||
OPMAP[ ADD ] = new OpCode( ADD, 3, 0x01, 0 );
|
||||
OPMAP[ SUB ] = new OpCode( SUB, 3, 0x02, 0 );
|
||||
OPMAP[ MUL ] = new OpCode( MUL, 3, 0x03, 0 );
|
||||
OPMAP[ DIV ] = new OpCode( DIV, 3, 0x04, 0 );
|
||||
OPMAP[ RCP ] = new OpCode( RCP, 2, 0x05, 0 );
|
||||
OPMAP[ MIN ] = new OpCode( MIN, 3, 0x06, 0 );
|
||||
OPMAP[ MAX ] = new OpCode( MAX, 3, 0x07, 0 );
|
||||
OPMAP[ FRC ] = new OpCode( FRC, 2, 0x08, 0 );
|
||||
OPMAP[ SQT ] = new OpCode( SQT, 2, 0x09, 0 );
|
||||
OPMAP[ RSQ ] = new OpCode( RSQ, 2, 0x0a, 0 );
|
||||
OPMAP[ POW ] = new OpCode( POW, 3, 0x0b, 0 );
|
||||
OPMAP[ LOG ] = new OpCode( LOG, 2, 0x0c, 0 );
|
||||
OPMAP[ EXP ] = new OpCode( EXP, 2, 0x0d, 0 );
|
||||
OPMAP[ NRM ] = new OpCode( NRM, 2, 0x0e, 0 );
|
||||
OPMAP[ SIN ] = new OpCode( SIN, 2, 0x0f, 0 );
|
||||
OPMAP[ COS ] = new OpCode( COS, 2, 0x10, 0 );
|
||||
OPMAP[ CRS ] = new OpCode( CRS, 3, 0x11, 0 );
|
||||
OPMAP[ DP3 ] = new OpCode( DP3, 3, 0x12, 0 );
|
||||
OPMAP[ DP4 ] = new OpCode( DP4, 3, 0x13, 0 );
|
||||
OPMAP[ ABS ] = new OpCode( ABS, 2, 0x14, 0 );
|
||||
OPMAP[ NEG ] = new OpCode( NEG, 2, 0x15, 0 );
|
||||
OPMAP[ SAT ] = new OpCode( SAT, 2, 0x16, 0 );
|
||||
OPMAP[ M33 ] = new OpCode( M33, 3, 0x17, OP_SPECIAL_MATRIX );
|
||||
OPMAP[ M44 ] = new OpCode( M44, 3, 0x18, OP_SPECIAL_MATRIX );
|
||||
OPMAP[ M34 ] = new OpCode( M34, 3, 0x19, OP_SPECIAL_MATRIX );
|
||||
OPMAP[ IFZ ] = new OpCode( IFZ, 1, 0x1a, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ INZ ] = new OpCode( INZ, 1, 0x1b, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ IFE ] = new OpCode( IFE, 2, 0x1c, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ INE ] = new OpCode( INE, 2, 0x1d, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ IFG ] = new OpCode( IFG, 2, 0x1e, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ IFL ] = new OpCode( IFL, 2, 0x1f, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ IEG ] = new OpCode( IEG, 2, 0x20, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ IEL ] = new OpCode( IEL, 2, 0x21, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ ELS ] = new OpCode( ELS, 0, 0x22, OP_NO_DEST | OP_INC_NEST | OP_DEC_NEST );
|
||||
OPMAP[ EIF ] = new OpCode( EIF, 0, 0x23, OP_NO_DEST | OP_DEC_NEST );
|
||||
OPMAP[ REP ] = new OpCode( REP, 1, 0x24, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
|
||||
OPMAP[ ERP ] = new OpCode( ERP, 0, 0x25, OP_NO_DEST | OP_DEC_NEST );
|
||||
OPMAP[ BRK ] = new OpCode( BRK, 0, 0x26, OP_NO_DEST );
|
||||
OPMAP[ KIL ] = new OpCode( KIL, 1, 0x27, OP_NO_DEST | OP_FRAG_ONLY );
|
||||
OPMAP[ TEX ] = new OpCode( TEX, 3, 0x28, OP_FRAG_ONLY | OP_SPECIAL_TEX );
|
||||
OPMAP[ SGE ] = new OpCode( SGE, 3, 0x29, 0 );
|
||||
OPMAP[ SLT ] = new OpCode( SLT, 3, 0x2a, 0 );
|
||||
OPMAP[ SGN ] = new OpCode( SGN, 2, 0x2b, 0 );
|
||||
|
||||
REGMAP[ VA ] = new Register( VA, "vertex attribute", 0x0, 7, REG_VERT | REG_READ );
|
||||
REGMAP[ VC ] = new Register( VC, "vertex constant", 0x1, 127, REG_VERT | REG_READ );
|
||||
REGMAP[ VT ] = new Register( VT, "vertex temporary", 0x2, 7, REG_VERT | REG_WRITE | REG_READ );
|
||||
REGMAP[ OP ] = new Register( OP, "vertex output", 0x3, 0, REG_VERT | REG_WRITE );
|
||||
REGMAP[ V ] = new Register( V, "varying", 0x4, 7, REG_VERT | REG_FRAG | REG_READ | REG_WRITE );
|
||||
REGMAP[ FC ] = new Register( FC, "fragment constant", 0x1, 27, REG_FRAG | REG_READ );
|
||||
REGMAP[ FT ] = new Register( FT, "fragment temporary", 0x2, 7, REG_FRAG | REG_WRITE | REG_READ );
|
||||
REGMAP[ FS ] = new Register( FS, "texture sampler", 0x5, 7, REG_FRAG | REG_READ );
|
||||
REGMAP[ OC ] = new Register( OC, "fragment output", 0x3, 0, REG_FRAG | REG_WRITE );
|
||||
|
||||
SAMPLEMAP[ D2 ] = new Sampler( D2, SAMPLER_DIM_SHIFT, 0 );
|
||||
SAMPLEMAP[ D3 ] = new Sampler( D3, SAMPLER_DIM_SHIFT, 2 );
|
||||
SAMPLEMAP[ CUBE ] = new Sampler( CUBE, SAMPLER_DIM_SHIFT, 1 );
|
||||
SAMPLEMAP[ MIPNEAREST ] = new Sampler( MIPNEAREST, SAMPLER_MIPMAP_SHIFT, 1 );
|
||||
SAMPLEMAP[ MIPLINEAR ] = new Sampler( MIPLINEAR, SAMPLER_MIPMAP_SHIFT, 2 );
|
||||
SAMPLEMAP[ MIPNONE ] = new Sampler( MIPNONE, SAMPLER_MIPMAP_SHIFT, 0 );
|
||||
SAMPLEMAP[ NOMIP ] = new Sampler( NOMIP, SAMPLER_MIPMAP_SHIFT, 0 );
|
||||
SAMPLEMAP[ NEAREST ] = new Sampler( NEAREST, SAMPLER_FILTER_SHIFT, 0 );
|
||||
SAMPLEMAP[ LINEAR ] = new Sampler( LINEAR, SAMPLER_FILTER_SHIFT, 1 );
|
||||
SAMPLEMAP[ CENTROID ] = new Sampler( CENTROID, SAMPLER_SPECIAL_SHIFT, 1 << 0 );
|
||||
SAMPLEMAP[ SINGLE ] = new Sampler( SINGLE, SAMPLER_SPECIAL_SHIFT, 1 << 1 );
|
||||
SAMPLEMAP[ DEPTH ] = new Sampler( DEPTH, SAMPLER_SPECIAL_SHIFT, 1 << 2 );
|
||||
SAMPLEMAP[ REPEAT ] = new Sampler( REPEAT, SAMPLER_REPEAT_SHIFT, 1 );
|
||||
SAMPLEMAP[ WRAP ] = new Sampler( WRAP, SAMPLER_REPEAT_SHIFT, 1 );
|
||||
SAMPLEMAP[ CLAMP ] = new Sampler( CLAMP, SAMPLER_REPEAT_SHIFT, 0 );
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Constants
|
||||
// ----------------------------------------------------------------------
|
||||
private static const OPMAP:Dictionary = new Dictionary();
|
||||
private static const REGMAP:Dictionary = new Dictionary();
|
||||
private static const SAMPLEMAP:Dictionary = new Dictionary();
|
||||
|
||||
private static const MAX_NESTING:int = 4;
|
||||
private static const MAX_OPCODES:int = 256;
|
||||
|
||||
private static const FRAGMENT:String = "fragment";
|
||||
private static const VERTEX:String = "vertex";
|
||||
|
||||
// masks and shifts
|
||||
private static const SAMPLER_DIM_SHIFT:uint = 12;
|
||||
private static const SAMPLER_SPECIAL_SHIFT:uint = 16;
|
||||
private static const SAMPLER_REPEAT_SHIFT:uint = 20;
|
||||
private static const SAMPLER_MIPMAP_SHIFT:uint = 24;
|
||||
private static const SAMPLER_FILTER_SHIFT:uint = 28;
|
||||
|
||||
// regmap flags
|
||||
private static const REG_WRITE:uint = 0x1;
|
||||
private static const REG_READ:uint = 0x2;
|
||||
private static const REG_FRAG:uint = 0x20;
|
||||
private static const REG_VERT:uint = 0x40;
|
||||
|
||||
// opmap flags
|
||||
private static const OP_SCALAR:uint = 0x1;
|
||||
private static const OP_INC_NEST:uint = 0x2;
|
||||
private static const OP_DEC_NEST:uint = 0x4;
|
||||
private static const OP_SPECIAL_TEX:uint = 0x8;
|
||||
private static const OP_SPECIAL_MATRIX:uint = 0x10;
|
||||
private static const OP_FRAG_ONLY:uint = 0x20;
|
||||
private static const OP_VERT_ONLY:uint = 0x40;
|
||||
private static const OP_NO_DEST:uint = 0x80;
|
||||
|
||||
// opcodes
|
||||
private static const MOV:String = "mov";
|
||||
private static const ADD:String = "add";
|
||||
private static const SUB:String = "sub";
|
||||
private static const MUL:String = "mul";
|
||||
private static const DIV:String = "div";
|
||||
private static const RCP:String = "rcp";
|
||||
private static const MIN:String = "min";
|
||||
private static const MAX:String = "max";
|
||||
private static const FRC:String = "frc";
|
||||
private static const SQT:String = "sqt";
|
||||
private static const RSQ:String = "rsq";
|
||||
private static const POW:String = "pow";
|
||||
private static const LOG:String = "log";
|
||||
private static const EXP:String = "exp";
|
||||
private static const NRM:String = "nrm";
|
||||
private static const SIN:String = "sin";
|
||||
private static const COS:String = "cos";
|
||||
private static const CRS:String = "crs";
|
||||
private static const DP3:String = "dp3";
|
||||
private static const DP4:String = "dp4";
|
||||
private static const ABS:String = "abs";
|
||||
private static const NEG:String = "neg";
|
||||
private static const SAT:String = "sat";
|
||||
private static const M33:String = "m33";
|
||||
private static const M44:String = "m44";
|
||||
private static const M34:String = "m34";
|
||||
private static const IFZ:String = "ifz";
|
||||
private static const INZ:String = "inz";
|
||||
private static const IFE:String = "ife";
|
||||
private static const INE:String = "ine";
|
||||
private static const IFG:String = "ifg";
|
||||
private static const IFL:String = "ifl";
|
||||
private static const IEG:String = "ieg";
|
||||
private static const IEL:String = "iel";
|
||||
private static const ELS:String = "els";
|
||||
private static const EIF:String = "eif";
|
||||
private static const REP:String = "rep";
|
||||
private static const ERP:String = "erp";
|
||||
private static const BRK:String = "brk";
|
||||
private static const KIL:String = "kil";
|
||||
private static const TEX:String = "tex";
|
||||
private static const SGE:String = "sge";
|
||||
private static const SLT:String = "slt";
|
||||
private static const SGN:String = "sgn";
|
||||
|
||||
// registers
|
||||
private static const VA:String = "va";
|
||||
private static const VC:String = "vc";
|
||||
private static const VT:String = "vt";
|
||||
private static const OP:String = "op";
|
||||
private static const V:String = "v";
|
||||
private static const FC:String = "fc";
|
||||
private static const FT:String = "ft";
|
||||
private static const FS:String = "fs";
|
||||
private static const OC:String = "oc";
|
||||
|
||||
// samplers
|
||||
private static const D2:String = "2d";
|
||||
private static const D3:String = "3d";
|
||||
private static const CUBE:String = "cube";
|
||||
private static const MIPNEAREST:String = "mipnearest";
|
||||
private static const MIPLINEAR:String = "miplinear";
|
||||
private static const MIPNONE:String = "mipnone";
|
||||
private static const NOMIP:String = "nomip";
|
||||
private static const NEAREST:String = "nearest";
|
||||
private static const LINEAR:String = "linear";
|
||||
private static const CENTROID:String = "centroid";
|
||||
private static const SINGLE:String = "single";
|
||||
private static const DEPTH:String = "depth";
|
||||
private static const REPEAT:String = "repeat";
|
||||
private static const WRAP:String = "wrap";
|
||||
private static const CLAMP:String = "clamp";
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
// Helper Classes
|
||||
// --------------------------------------------------------------------------------
|
||||
{
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
class OpCode
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
private var _emitCode:uint;
|
||||
private var _flags:uint;
|
||||
private var _name:String;
|
||||
private var _numRegister:uint;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get emitCode():uint { return _emitCode; }
|
||||
public function get flags():uint { return _flags; }
|
||||
public function get name():String { return _name; }
|
||||
public function get numRegister():uint { return _numRegister; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function OpCode( name:String, numRegister:uint, emitCode:uint, flags:uint)
|
||||
{
|
||||
_name = name;
|
||||
_numRegister = numRegister;
|
||||
_emitCode = emitCode;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function toString():String
|
||||
{
|
||||
return "[OpCode name=\""+_name+"\", numRegister="+_numRegister+", emitCode="+_emitCode+", flags="+_flags+"]";
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
class Register
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
private var _emitCode:uint;
|
||||
private var _name:String;
|
||||
private var _longName:String;
|
||||
private var _flags:uint;
|
||||
private var _range:uint;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get emitCode():uint { return _emitCode; }
|
||||
public function get longName():String { return _longName; }
|
||||
public function get name():String { return _name; }
|
||||
public function get flags():uint { return _flags; }
|
||||
public function get range():uint { return _range; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function Register( name:String, longName:String, emitCode:uint, range:uint, flags:uint)
|
||||
{
|
||||
_name = name;
|
||||
_longName = longName;
|
||||
_emitCode = emitCode;
|
||||
_range = range;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function toString():String
|
||||
{
|
||||
return "[Register name=\""+_name+"\", longName=\""+_longName+"\", emitCode="+_emitCode+", range="+_range+", flags="+ _flags+"]";
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
class Sampler
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
private var _flag:uint;
|
||||
private var _mask:uint;
|
||||
private var _name:String;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get flag():uint { return _flag; }
|
||||
public function get mask():uint { return _mask; }
|
||||
public function get name():String { return _name; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function Sampler( name:String, flag:uint, mask:uint )
|
||||
{
|
||||
_name = name;
|
||||
_flag = flag;
|
||||
_mask = mask;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function toString():String
|
||||
{
|
||||
return "[Sampler name=\""+_name+"\", flag=\""+_flag+"\", mask="+mask+"]";
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/alternativa/engine3d/effects/Particle.as
Normal file
63
src/alternativa/engine3d/effects/Particle.as
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.effects {
|
||||
|
||||
import flash.display3D.textures.TextureBase;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Particle {
|
||||
|
||||
public var diffuse:TextureBase;
|
||||
public var opacity:TextureBase;
|
||||
public var blendSource:String;
|
||||
public var blendDestination:String;
|
||||
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
public var z:Number;
|
||||
public var rotation:Number;
|
||||
|
||||
public var width:Number;
|
||||
public var height:Number;
|
||||
public var originX:Number;
|
||||
public var originY:Number;
|
||||
|
||||
public var uvScaleX:Number;
|
||||
public var uvScaleY:Number;
|
||||
public var uvOffsetX:Number;
|
||||
public var uvOffsetY:Number;
|
||||
|
||||
public var red:Number;
|
||||
public var green:Number;
|
||||
public var blue:Number;
|
||||
public var alpha:Number;
|
||||
|
||||
public var next:Particle;
|
||||
|
||||
static public var collector:Particle;
|
||||
|
||||
static public function create():Particle {
|
||||
var res:Particle;
|
||||
if (collector != null) {
|
||||
res = collector;
|
||||
collector = collector.next;
|
||||
res.next = null;
|
||||
} else {
|
||||
//trace("new Particle");
|
||||
res = new Particle();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
183
src/alternativa/engine3d/effects/ParticleEffect.as
Normal file
183
src/alternativa/engine3d/effects/ParticleEffect.as
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.effects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Transform3D;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class ParticleEffect {
|
||||
|
||||
public var name:String;
|
||||
|
||||
public var scale:Number = 1;
|
||||
|
||||
public var boundBox:BoundBox;
|
||||
|
||||
alternativa3d var next:ParticleEffect;
|
||||
|
||||
alternativa3d var nextInSystem:ParticleEffect;
|
||||
|
||||
alternativa3d var system:ParticleSystem;
|
||||
|
||||
alternativa3d var startTime:Number;
|
||||
|
||||
alternativa3d var lifeTime:Number = Number.MAX_VALUE;
|
||||
|
||||
alternativa3d var particleList:Particle;
|
||||
|
||||
alternativa3d var aabb:BoundBox = new BoundBox();
|
||||
|
||||
alternativa3d var keyPosition:Vector3D;
|
||||
|
||||
protected var keyDirection:Vector3D;
|
||||
|
||||
protected var timeKeys:Vector.<Number> = new Vector.<Number>();
|
||||
protected var positionKeys:Vector.<Vector3D> = new Vector.<Vector3D>();
|
||||
protected var directionKeys:Vector.<Vector3D> = new Vector.<Vector3D>();
|
||||
protected var scriptKeys:Vector.<Function> = new Vector.<Function>();
|
||||
protected var keysCount:int = 0;
|
||||
|
||||
private static var randomNumbers:Vector.<Number>;
|
||||
private static const randomNumbersCount:int = 1000;
|
||||
|
||||
private static const vector:Vector3D = new Vector3D();
|
||||
|
||||
private var randomOffset:int;
|
||||
private var randomCounter:int;
|
||||
|
||||
private var _position:Vector3D = new Vector3D(0, 0, 0);
|
||||
private var _direction:Vector3D = new Vector3D(0, 0, 1);
|
||||
|
||||
public function ParticleEffect() {
|
||||
if (randomNumbers == null) {
|
||||
randomNumbers = new Vector.<Number>();
|
||||
for (var i:int = 0; i < randomNumbersCount; i++) randomNumbers[i] = Math.random();
|
||||
}
|
||||
randomOffset = Math.random()*randomNumbersCount;
|
||||
}
|
||||
|
||||
public function get position():Vector3D {
|
||||
return _position.clone();
|
||||
}
|
||||
|
||||
public function set position(value:Vector3D):void {
|
||||
_position.x = value.x;
|
||||
_position.y = value.y;
|
||||
_position.z = value.z;
|
||||
_position.w = value.w;
|
||||
if (system != null) setPositionKeys(system.getTime() - startTime);
|
||||
}
|
||||
|
||||
public function get direction():Vector3D {
|
||||
return _direction.clone();
|
||||
}
|
||||
|
||||
public function set direction(value:Vector3D):void {
|
||||
_direction.x = value.x;
|
||||
_direction.y = value.y;
|
||||
_direction.z = value.z;
|
||||
_direction.w = value.w;
|
||||
if (system != null) setDirectionKeys(system.getTime() - startTime);
|
||||
}
|
||||
|
||||
public function stop():void {
|
||||
var time:Number = system.getTime() - startTime;
|
||||
for (var i:int = 0; i < keysCount; i++) {
|
||||
if (time < timeKeys[i]) break;
|
||||
}
|
||||
keysCount = i;
|
||||
}
|
||||
|
||||
protected function get particleSystem():ParticleSystem {
|
||||
return system;
|
||||
}
|
||||
|
||||
protected function get cameraTransform():Transform3D {
|
||||
return system.cameraToLocalTransform;
|
||||
}
|
||||
|
||||
protected function random():Number {
|
||||
var res:Number = randomNumbers[randomCounter]; randomCounter++;
|
||||
if (randomCounter == randomNumbersCount) randomCounter = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected function addKey(time:Number, script:Function):void {
|
||||
timeKeys[keysCount] = time;
|
||||
positionKeys[keysCount] = new Vector3D();
|
||||
directionKeys[keysCount] = new Vector3D();
|
||||
scriptKeys[keysCount] = script;
|
||||
keysCount++;
|
||||
}
|
||||
|
||||
protected function setLife(time:Number):void {
|
||||
lifeTime = time;
|
||||
}
|
||||
|
||||
alternativa3d function calculateAABB():void {
|
||||
aabb.minX = boundBox.minX*scale + _position.x;
|
||||
aabb.minY = boundBox.minY*scale + _position.y;
|
||||
aabb.minZ = boundBox.minZ*scale + _position.z;
|
||||
aabb.maxX = boundBox.maxX*scale + _position.x;
|
||||
aabb.maxY = boundBox.maxY*scale + _position.y;
|
||||
aabb.maxZ = boundBox.maxZ*scale + _position.z;
|
||||
}
|
||||
|
||||
alternativa3d function setPositionKeys(time:Number):void {
|
||||
for (var i:int = 0; i < keysCount; i++) {
|
||||
if (time <= timeKeys[i]) {
|
||||
var pos:Vector3D = positionKeys[i];
|
||||
pos.x = _position.x;
|
||||
pos.y = _position.y;
|
||||
pos.z = _position.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function setDirectionKeys(time:Number):void {
|
||||
vector.x = _direction.x;
|
||||
vector.y = _direction.y;
|
||||
vector.z = _direction.z;
|
||||
vector.normalize();
|
||||
for (var i:int = 0; i < keysCount; i++) {
|
||||
if (time <= timeKeys[i]) {
|
||||
var dir:Vector3D = directionKeys[i];
|
||||
dir.x = vector.x;
|
||||
dir.y = vector.y;
|
||||
dir.z = vector.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function calculate(time:Number):Boolean {
|
||||
randomCounter = randomOffset;
|
||||
for (var i:int = 0; i < keysCount; i++) {
|
||||
var keyTime:Number = timeKeys[i];
|
||||
if (time >= keyTime) {
|
||||
keyPosition = positionKeys[i];
|
||||
keyDirection = directionKeys[i];
|
||||
var script:Function = scriptKeys[i];
|
||||
script.call(this, keyTime, time - keyTime);
|
||||
} else break;
|
||||
}
|
||||
return i < keysCount || particleList != null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
139
src/alternativa/engine3d/effects/ParticlePrototype.as
Normal file
139
src/alternativa/engine3d/effects/ParticlePrototype.as
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.effects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Transform3D;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class ParticlePrototype {
|
||||
|
||||
// Atlas
|
||||
public var atlas:TextureAtlas;
|
||||
|
||||
// Blend
|
||||
private var blendSource:String;
|
||||
private var blendDestination:String;
|
||||
|
||||
// If <code>true</code>, then play animation
|
||||
private var animated:Boolean;
|
||||
|
||||
// Size
|
||||
private var width:Number;
|
||||
private var height:Number;
|
||||
|
||||
// Key frames of animation.
|
||||
private var timeKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var rotationKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var scaleXKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var scaleYKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var redKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var greenKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var blueKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var alphaKeys:Vector.<Number> = new Vector.<Number>();
|
||||
private var keysCount:int = 0;
|
||||
|
||||
public function ParticlePrototype(width:Number, height:Number, atlas:TextureAtlas, animated:Boolean = false, blendSource:String = "sourceAlpha", blendDestination:String = "oneMinusSourceAlpha") {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.atlas = atlas;
|
||||
this.animated = animated;
|
||||
this.blendSource = blendSource;
|
||||
this.blendDestination = blendDestination;
|
||||
}
|
||||
|
||||
public function addKey(time:Number, rotation:Number = 0, scaleX:Number = 1, scaleY:Number = 1, red:Number = 1, green:Number = 1, blue:Number = 1, alpha:Number = 1):void {
|
||||
var lastIndex:int = keysCount - 1;
|
||||
if (keysCount > 0 && time <= timeKeys[lastIndex]) throw new Error("Keys must be successively.");
|
||||
timeKeys[keysCount] = time;
|
||||
rotationKeys[keysCount] = rotation;
|
||||
scaleXKeys[keysCount] = scaleX;
|
||||
scaleYKeys[keysCount] = scaleY;
|
||||
redKeys[keysCount] = red;
|
||||
greenKeys[keysCount] = green;
|
||||
blueKeys[keysCount] = blue;
|
||||
alphaKeys[keysCount] = alpha;
|
||||
keysCount++;
|
||||
}
|
||||
|
||||
public function createParticle(effect:ParticleEffect, time:Number, position:Vector3D, rotation:Number = 0, scaleX:Number = 1, scaleY:Number = 1, alpha:Number = 1, firstFrame:int = 0):void {
|
||||
var b:int = keysCount - 1;
|
||||
if (atlas.diffuse._texture != null && keysCount > 1 && time >= timeKeys[0] && time < timeKeys[b]) {
|
||||
|
||||
for (b = 1; b < keysCount; b++) {
|
||||
if (time < timeKeys[b]) {
|
||||
var systemScale:Number = effect.system.scale;
|
||||
var effectScale:Number = effect.scale;
|
||||
var transform:Transform3D = effect.system.localToCameraTransform;
|
||||
var wind:Vector3D = effect.system.wind;
|
||||
var gravity:Vector3D = effect.system.gravity;
|
||||
// Interpolation
|
||||
var a:int = b - 1;
|
||||
var t:Number = (time - timeKeys[a])/(timeKeys[b] - timeKeys[a]);
|
||||
// Frame calculation
|
||||
var pos:int = firstFrame + (animated ? time*atlas.fps : 0);
|
||||
if (atlas.loop) {
|
||||
pos = pos%atlas.rangeLength;
|
||||
if (pos < 0) pos += atlas.rangeLength;
|
||||
} else {
|
||||
if (pos < 0) pos = 0;
|
||||
if (pos >= atlas.rangeLength) pos = atlas.rangeLength - 1;
|
||||
}
|
||||
pos += atlas.rangeBegin;
|
||||
var col:int = pos%atlas.columnsCount;
|
||||
var row:int = pos/atlas.columnsCount;
|
||||
// Particle creation
|
||||
var particle:Particle = Particle.create();
|
||||
particle.diffuse = atlas.diffuse._texture;
|
||||
particle.opacity = (atlas.opacity != null) ? atlas.opacity._texture : null;
|
||||
particle.blendSource = blendSource;
|
||||
particle.blendDestination = blendDestination;
|
||||
var cx:Number = effect.keyPosition.x + position.x*effectScale;
|
||||
var cy:Number = effect.keyPosition.y + position.y*effectScale;
|
||||
var cz:Number = effect.keyPosition.z + position.z*effectScale;
|
||||
particle.x = cx*transform.a + cy*transform.b + cz*transform.c + transform.d;
|
||||
particle.y = cx*transform.e + cy*transform.f + cz*transform.g + transform.h;
|
||||
particle.z = cx*transform.i + cy*transform.j + cz*transform.k + transform.l;
|
||||
var rot:Number = rotationKeys[a] + (rotationKeys[b] - rotationKeys[a])*t;
|
||||
particle.rotation = (scaleX*scaleY > 0) ? (rotation + rot) : (rotation - rot);
|
||||
particle.width = systemScale*effectScale*scaleX*width*(scaleXKeys[a] + (scaleXKeys[b] - scaleXKeys[a])*t);
|
||||
particle.height = systemScale*effectScale*scaleY*height*(scaleYKeys[a] + (scaleYKeys[b] - scaleYKeys[a])*t);
|
||||
particle.originX = atlas.originX;
|
||||
particle.originY = atlas.originY;
|
||||
particle.uvScaleX = 1/atlas.columnsCount;
|
||||
particle.uvScaleY = 1/atlas.rowsCount;
|
||||
particle.uvOffsetX = col/atlas.columnsCount;
|
||||
particle.uvOffsetY = row/atlas.rowsCount;
|
||||
particle.red = redKeys[a] + (redKeys[b] - redKeys[a])*t;
|
||||
particle.green = greenKeys[a] + (greenKeys[b] - greenKeys[a])*t;
|
||||
particle.blue = blueKeys[a] + (blueKeys[b] - blueKeys[a])*t;
|
||||
particle.alpha = alpha*(alphaKeys[a] + (alphaKeys[b] - alphaKeys[a])*t);
|
||||
particle.next = effect.particleList;
|
||||
effect.particleList = particle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get lifeTime():Number {
|
||||
var lastIndex:int = keysCount - 1;
|
||||
return timeKeys[lastIndex];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
481
src/alternativa/engine3d/effects/ParticleSystem.as
Normal file
481
src/alternativa/engine3d/effects/ParticleSystem.as
Normal file
@@ -0,0 +1,481 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.effects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Debug;
|
||||
import alternativa.engine3d.core.DrawUnit;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Renderer;
|
||||
import alternativa.engine3d.materials.compiler.Procedure;
|
||||
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DBlendFactor;
|
||||
import flash.display3D.Context3DProgramType;
|
||||
import flash.display3D.Context3DTriangleFace;
|
||||
import flash.display3D.Context3DVertexBufferFormat;
|
||||
import flash.display3D.IndexBuffer3D;
|
||||
import flash.display3D.Program3D;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.display3D.textures.TextureBase;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class ParticleSystem extends Object3D {
|
||||
|
||||
static private const limit:int = 31;
|
||||
static private var vertexBuffer:VertexBuffer3D;
|
||||
static private var indexBuffer:IndexBuffer3D;
|
||||
static private var diffuseProgram:Program3D;
|
||||
static private var opacityProgram:Program3D;
|
||||
static private var diffuseBlendProgram:Program3D;
|
||||
static private var opacityBlendProgram:Program3D;
|
||||
|
||||
public var resolveByAABB:Boolean = true;
|
||||
|
||||
public var gravity:Vector3D = new Vector3D(0, 0, -1);
|
||||
public var wind:Vector3D = new Vector3D();
|
||||
|
||||
public var fogColor:int = 0;
|
||||
public var fogMaxDensity:Number = 0;
|
||||
public var fogNear:Number = 0;
|
||||
public var fogFar:Number = 0;
|
||||
|
||||
alternativa3d var scale:Number = 1;
|
||||
|
||||
alternativa3d var effectList:ParticleEffect;
|
||||
|
||||
private var drawUnit:DrawUnit = null;
|
||||
private var diffuse:TextureBase = null;
|
||||
private var opacity:TextureBase = null;
|
||||
private var blendSource:String = null;
|
||||
private var blendDestination:String = null;
|
||||
private var counter:int;
|
||||
|
||||
private var za:Number;
|
||||
private var zb:Number;
|
||||
private var fake:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
private var fakeCounter:int = 0;
|
||||
|
||||
public function ParticleSystem() {
|
||||
super();
|
||||
}
|
||||
|
||||
private var pause:Boolean = false;
|
||||
private var stopTime:Number;
|
||||
private var subtractiveTime:Number = 0;
|
||||
|
||||
public function stop():void {
|
||||
if (!pause) {
|
||||
stopTime = getTimer()*0.001;
|
||||
pause = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function play():void {
|
||||
if (pause) {
|
||||
subtractiveTime += getTimer()*0.001 - stopTime;
|
||||
pause = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function prevFrame():void {
|
||||
stopTime -= 0.001;
|
||||
}
|
||||
|
||||
public function nextFrame():void {
|
||||
stopTime += 0.001;
|
||||
}
|
||||
|
||||
public function addEffect(effect:ParticleEffect):ParticleEffect {
|
||||
// Checking on belonging
|
||||
if (effect.system != null) throw new Error("Cannot add the same effect twice.");
|
||||
// Set parameters
|
||||
effect.startTime = getTime();
|
||||
effect.system = this;
|
||||
effect.setPositionKeys(0);
|
||||
effect.setDirectionKeys(0);
|
||||
// Add
|
||||
effect.nextInSystem = effectList;
|
||||
effectList = effect;
|
||||
return effect;
|
||||
}
|
||||
|
||||
public function getEffectByName(name:String):ParticleEffect {
|
||||
for (var effect:ParticleEffect = effectList; effect != null; effect = effect.nextInSystem) {
|
||||
if (effect.name == name) return effect;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
alternativa3d function getTime():Number {
|
||||
return pause ? (stopTime - subtractiveTime) : (getTimer()*0.001 - subtractiveTime);
|
||||
}
|
||||
|
||||
override alternativa3d function collectDraws(camera:Camera3D, lights:Vector.<Light3D>, lightsLength:int):void {
|
||||
// Create geometry and program
|
||||
if (vertexBuffer == null) createAndUpload(camera.context3D);
|
||||
// Average size
|
||||
scale = Math.sqrt(localToCameraTransform.a*localToCameraTransform.a + localToCameraTransform.e*localToCameraTransform.e + localToCameraTransform.i*localToCameraTransform.i);
|
||||
scale += Math.sqrt(localToCameraTransform.b*localToCameraTransform.b + localToCameraTransform.f*localToCameraTransform.f + localToCameraTransform.j*localToCameraTransform.j);
|
||||
scale += Math.sqrt(localToCameraTransform.c*localToCameraTransform.c + localToCameraTransform.g*localToCameraTransform.g + localToCameraTransform.k*localToCameraTransform.k);
|
||||
scale /= 3;
|
||||
// TODO: add rotation on slope of the Z-axis in local space of camera
|
||||
// Calculate frustrum
|
||||
camera.calculateFrustum(cameraToLocalTransform);
|
||||
// Loop items
|
||||
var visibleEffectList:ParticleEffect;
|
||||
var conflictAnyway:Boolean = false;
|
||||
var time:Number = getTime();
|
||||
for (var effect:ParticleEffect = effectList, prev:ParticleEffect = null; effect != null;) {
|
||||
// Check if actual
|
||||
var effectTime:Number = time - effect.startTime;
|
||||
if (effectTime <= effect.lifeTime) {
|
||||
// Check bounds
|
||||
var culling:int = 63;
|
||||
if (effect.boundBox != null) {
|
||||
effect.calculateAABB();
|
||||
culling = effect.aabb.checkFrustumCulling(camera.frustum, 63);
|
||||
}
|
||||
if (culling >= 0) {
|
||||
// Gather the particles
|
||||
if (effect.calculate(effectTime)) {
|
||||
// Add
|
||||
if (effect.particleList != null) {
|
||||
effect.next = visibleEffectList;
|
||||
visibleEffectList = effect;
|
||||
conflictAnyway ||= effect.boundBox == null;
|
||||
}
|
||||
// Go to next effect
|
||||
prev = effect;
|
||||
effect = effect.nextInSystem;
|
||||
} else {
|
||||
// Removing
|
||||
if (prev != null) {
|
||||
prev.nextInSystem = effect.nextInSystem;
|
||||
effect = prev.nextInSystem;
|
||||
} else {
|
||||
effectList = effect.nextInSystem;
|
||||
effect = effectList;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Go to next effect
|
||||
prev = effect;
|
||||
effect = effect.nextInSystem;
|
||||
}
|
||||
} else {
|
||||
// Removing
|
||||
if (prev != null) {
|
||||
prev.nextInSystem = effect.nextInSystem;
|
||||
effect = prev.nextInSystem;
|
||||
} else {
|
||||
effectList = effect.nextInSystem;
|
||||
effect = effectList;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Gather draws
|
||||
if (visibleEffectList != null) {
|
||||
if (visibleEffectList.next != null) {
|
||||
/*if (resolveByAABB && !conflictAnyway) {
|
||||
drawAABBEffects(camera, visibleEffectList);
|
||||
} else {*/
|
||||
drawConflictEffects(camera, visibleEffectList);
|
||||
//}
|
||||
} else {
|
||||
drawParticleList(camera, visibleEffectList.particleList);
|
||||
visibleEffectList.particleList = null;
|
||||
if (camera.debug && visibleEffectList.boundBox != null && (camera.checkInDebug(this) & Debug.BOUNDS)) Debug.drawBoundBox(camera, visibleEffectList.aabb, localToCameraTransform);
|
||||
}
|
||||
// Reset
|
||||
flush(camera);
|
||||
drawUnit = null;
|
||||
diffuse = null;
|
||||
opacity = null;
|
||||
blendSource = null;
|
||||
blendDestination = null;
|
||||
fakeCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private function createAndUpload(context:Context3D):void {
|
||||
var vertices:Vector.<Number> = new Vector.<Number>();
|
||||
var indices:Vector.<uint> = new Vector.<uint>();
|
||||
for (var i:int = 0; i < limit; i++) {
|
||||
vertices.push(0,0,0, 0,0,i*4, 0,1,0, 0,1,i*4, 1,1,0, 1,1,i*4, 1,0,0, 1,0,i*4);
|
||||
indices.push(i*4, i*4 + 1, i*4 + 3, i*4 + 2, i*4 + 3, i*4 + 1);
|
||||
}
|
||||
vertexBuffer = context.createVertexBuffer(limit*4, 6);
|
||||
vertexBuffer.uploadFromVector(vertices, 0, limit*4);
|
||||
indexBuffer = context.createIndexBuffer(limit*6);
|
||||
indexBuffer.uploadFromVector(indices, 0, limit*6);
|
||||
var vertexProgram:Array = [
|
||||
// Pivot
|
||||
"mov t2, c[a1.z]", // originX, originY, width, height
|
||||
"sub t0.z, a0.x, t2.x",
|
||||
"sub t0.w, a0.y, t2.y",
|
||||
// Width and height
|
||||
"mul t0.z, t0.z, t2.z",
|
||||
"mul t0.w, t0.w, t2.w",
|
||||
// Rotation
|
||||
"mov t2, c[a1.z+1]", // x, y, z, rotation
|
||||
"mov t1.z, t2.w",
|
||||
"sin t1.x, t1.z", // sin
|
||||
"cos t1.y, t1.z", // cos
|
||||
"mul t1.z, t0.z, t1.y", // x*cos
|
||||
"mul t1.w, t0.w, t1.x", // y*sin
|
||||
"sub t0.x, t1.z, t1.w", // X
|
||||
"mul t1.z, t0.z, t1.x", // x*sin
|
||||
"mul t1.w, t0.w, t1.y", // y*cos
|
||||
"add t0.y, t1.z, t1.w", // Y
|
||||
// Translation
|
||||
"add t0.x, t0.x, t2.x",
|
||||
"add t0.y, t0.y, t2.y",
|
||||
"add t0.z, a0.z, t2.z",
|
||||
"mov t0.w, a0.w",
|
||||
// Projection
|
||||
"dp4 o0.x, t0, c124",
|
||||
"dp4 o0.y, t0, c125",
|
||||
"dp4 o0.z, t0, c126",
|
||||
"dp4 o0.w, t0, c127",
|
||||
// UV correction and passing out
|
||||
"mov t2, c[a1.z+2]", // uvScaleX, uvScaleY, uvOffsetX, uvOffsetY
|
||||
"mul t1.x, a1.x, t2.x",
|
||||
"mul t1.y, a1.y, t2.y",
|
||||
"add t1.x, t1.x, t2.z",
|
||||
"add t1.y, t1.y, t2.w",
|
||||
"mov v0, t1",
|
||||
// Passing color
|
||||
"mov v1, c[a1.z+3]", // red, green, blue, alpha
|
||||
// Passing coordinates in the camera space
|
||||
"mov v2, t0",
|
||||
];
|
||||
var fragmentDiffuseProgram:Array = [
|
||||
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
|
||||
"mul t0, t0, v1",
|
||||
// Fog
|
||||
"sub t1.w, v2.z, c1.x",
|
||||
"div t1.w, t1.w, c1.y",
|
||||
"max t1.w, t1.w, c1.z",
|
||||
"min t1.w, t1.w, c0.w",
|
||||
"sub t1.xyz, c0.xyz, t0.xyz",
|
||||
"mul t1.xyz, t1.xyz, t1.w",
|
||||
"add t0.xyz, t0.xyz, t1.xyz",
|
||||
"mov o0, t0",
|
||||
];
|
||||
var fragmentOpacityProgram:Array = [
|
||||
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
|
||||
"tex t1, v0, s1 <2d,clamp,linear,miplinear>",
|
||||
"mov t0.w, t1.x",
|
||||
"mul t0, t0, v1",
|
||||
// Fog
|
||||
"sub t1.w, v2.z, c1.x",
|
||||
"div t1.w, t1.w, c1.y",
|
||||
"max t1.w, t1.w, c1.z",
|
||||
"min t1.w, t1.w, c0.w",
|
||||
"sub t1.xyz, c0.xyz, t0.xyz",
|
||||
"mul t1.xyz, t1.xyz, t1.w",
|
||||
"add t0.xyz, t0.xyz, t1.xyz",
|
||||
"mov o0, t0",
|
||||
];
|
||||
var fragmentDiffuseBlendProgram:Array = [
|
||||
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
|
||||
"mul t0, t0, v1",
|
||||
// Fog
|
||||
"sub t1.w, v2.z, c1.x",
|
||||
"div t1.w, t1.w, c1.y",
|
||||
"max t1.w, t1.w, c1.z",
|
||||
"min t1.w, t1.w, c0.w",
|
||||
"sub t1.w, c1.w, t1.w",
|
||||
"mul t0.w, t0.w, t1.w",
|
||||
"mov o0, t0",
|
||||
];
|
||||
var fragmentOpacityBlendProgram:Array = [
|
||||
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
|
||||
"tex t1, v0, s1 <2d,clamp,linear,miplinear>",
|
||||
"mov t0.w, t1.x",
|
||||
"mul t0, t0, v1",
|
||||
// Fog
|
||||
"sub t1.w, v2.z, c1.x",
|
||||
"div t1.w, t1.w, c1.y",
|
||||
"max t1.w, t1.w, c1.z",
|
||||
"min t1.w, t1.w, c0.w",
|
||||
"sub t1.w, c1.w, t1.w",
|
||||
"mul t0.w, t0.w, t1.w",
|
||||
"mov o0, t0",
|
||||
];
|
||||
diffuseProgram = context.createProgram();
|
||||
opacityProgram = context.createProgram();
|
||||
diffuseBlendProgram = context.createProgram();
|
||||
opacityBlendProgram = context.createProgram();
|
||||
var compiledVertexProgram:ByteArray = compileProgram(Context3DProgramType.VERTEX, vertexProgram);
|
||||
diffuseProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentDiffuseProgram));
|
||||
opacityProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentOpacityProgram));
|
||||
diffuseBlendProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentDiffuseBlendProgram));
|
||||
opacityBlendProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentOpacityBlendProgram));
|
||||
}
|
||||
|
||||
private function compileProgram(mode:String, program:Array):ByteArray {
|
||||
/*var string:String = "";
|
||||
var length:int = program.length;
|
||||
for (var i:int = 0; i < length; i++) {
|
||||
var line:String = program[i];
|
||||
string += line + ((i < length - 1) ? " \n" : "");
|
||||
}*/
|
||||
var proc:Procedure = new Procedure(program);
|
||||
return proc.getByteCode(mode);
|
||||
}
|
||||
|
||||
private function flush(camera:Camera3D):void {
|
||||
if (fakeCounter == fake.length) fake[fakeCounter] = new Object3D();
|
||||
var object:Object3D = fake[fakeCounter];
|
||||
fakeCounter++;
|
||||
object.localToCameraTransform.l = (za + zb)/2;
|
||||
// Fill
|
||||
drawUnit.object = object;
|
||||
drawUnit.numTriangles = counter << 1;
|
||||
if (blendDestination == Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA) {
|
||||
drawUnit.program = (opacity != null) ? opacityProgram : diffuseProgram;
|
||||
} else {
|
||||
drawUnit.program = (opacity != null) ? opacityBlendProgram : diffuseBlendProgram;
|
||||
}
|
||||
// Set streams
|
||||
drawUnit.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
|
||||
drawUnit.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
|
||||
// Set constants
|
||||
drawUnit.setProjectionConstants(camera, 124);
|
||||
drawUnit.setFragmentConstantsFromNumbers(0, ((fogColor >> 16) & 0xFF)/0xFF, ((fogColor >> 8) & 0xFF)/0xFF, (fogColor & 0xFF)/0xFF, fogMaxDensity);
|
||||
drawUnit.setFragmentConstantsFromNumbers(1, fogNear, fogFar - fogNear, 0, 1);
|
||||
// Set textures
|
||||
drawUnit.setTextureAt(0, diffuse);
|
||||
if (opacity != null) drawUnit.setTextureAt(1, opacity);
|
||||
// Set blending
|
||||
drawUnit.blendSource = blendSource;
|
||||
drawUnit.blendDestination = blendDestination;
|
||||
drawUnit.culling = Context3DTriangleFace.NONE;
|
||||
// Send to render
|
||||
camera.renderer.addDrawUnit(drawUnit, Renderer.TRANSPARENT_SORT);
|
||||
}
|
||||
|
||||
private function drawParticleList(camera:Camera3D, list:Particle):void {
|
||||
// Sorting
|
||||
if (list.next != null) list = sortParticleList(list);
|
||||
// Gather draws
|
||||
var last:Particle;
|
||||
for (var particle:Particle = list; particle != null; particle = particle.next) {
|
||||
if (counter >= limit || particle.diffuse != diffuse || particle.opacity != opacity || particle.blendSource != blendSource || particle.blendDestination != blendDestination) {
|
||||
if (drawUnit != null) flush(camera);
|
||||
drawUnit = camera.renderer.createDrawUnit(null, null, indexBuffer, 0, 0);
|
||||
diffuse = particle.diffuse;
|
||||
opacity = particle.opacity;
|
||||
blendSource = particle.blendSource;
|
||||
blendDestination = particle.blendDestination;
|
||||
counter = 0;
|
||||
za = particle.z;
|
||||
}
|
||||
// Write constants
|
||||
var offset:int = counter << 2;
|
||||
drawUnit.setVertexConstantsFromNumbers(offset++, particle.originX, particle.originY, particle.width, particle.height);
|
||||
drawUnit.setVertexConstantsFromNumbers(offset++, particle.x, particle.y, particle.z, particle.rotation);
|
||||
drawUnit.setVertexConstantsFromNumbers(offset++, particle.uvScaleX, particle.uvScaleY, particle.uvOffsetX, particle.uvOffsetY);
|
||||
drawUnit.setVertexConstantsFromNumbers(offset++, particle.red, particle.green, particle.blue, particle.alpha);
|
||||
counter++;
|
||||
zb = particle.z;
|
||||
last = particle;
|
||||
}
|
||||
// Send to the collector
|
||||
last.next = Particle.collector;
|
||||
Particle.collector = list;
|
||||
}
|
||||
|
||||
private function sortParticleList(list:Particle):Particle {
|
||||
var left:Particle = list;
|
||||
var right:Particle = list.next;
|
||||
while (right != null && right.next != null) {
|
||||
list = list.next;
|
||||
right = right.next.next;
|
||||
}
|
||||
right = list.next;
|
||||
list.next = null;
|
||||
if (left.next != null) {
|
||||
left = sortParticleList(left);
|
||||
}
|
||||
if (right.next != null) {
|
||||
right = sortParticleList(right);
|
||||
}
|
||||
var flag:Boolean = left.z > right.z;
|
||||
if (flag) {
|
||||
list = left;
|
||||
left = left.next;
|
||||
} else {
|
||||
list = right;
|
||||
right = right.next;
|
||||
}
|
||||
var last:Particle = list;
|
||||
while (true) {
|
||||
if (left == null) {
|
||||
last.next = right;
|
||||
return list;
|
||||
} else if (right == null) {
|
||||
last.next = left;
|
||||
return list;
|
||||
}
|
||||
if (flag) {
|
||||
if (left.z > right.z) {
|
||||
last = left;
|
||||
left = left.next;
|
||||
} else {
|
||||
last.next = right;
|
||||
last = right;
|
||||
right = right.next;
|
||||
flag = false;
|
||||
}
|
||||
} else {
|
||||
if (right.z > left.z) {
|
||||
last = right;
|
||||
right = right.next;
|
||||
} else {
|
||||
last.next = left;
|
||||
last = left;
|
||||
left = left.next;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function drawConflictEffects(camera:Camera3D, effectList:ParticleEffect):void {
|
||||
var particleList:Particle;
|
||||
for (var effect:ParticleEffect = effectList; effect != null; effect = next) {
|
||||
var next:ParticleEffect = effect.next;
|
||||
effect.next = null;
|
||||
var last:Particle = effect.particleList;
|
||||
while (last.next != null) last = last.next;
|
||||
last.next = particleList;
|
||||
particleList = effect.particleList;
|
||||
effect.particleList = null;
|
||||
if (camera.debug && effect.boundBox != null && (camera.checkInDebug(this) & Debug.BOUNDS)) Debug.drawBoundBox(camera, effect.aabb, localToCameraTransform, 0xFF0000);
|
||||
}
|
||||
drawParticleList(camera, particleList);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
45
src/alternativa/engine3d/effects/TextureAtlas.as
Normal file
45
src/alternativa/engine3d/effects/TextureAtlas.as
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.effects {
|
||||
|
||||
import alternativa.engine3d.resources.TextureResource;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class TextureAtlas {
|
||||
|
||||
public var diffuse:TextureResource;
|
||||
public var opacity:TextureResource;
|
||||
public var columnsCount:int;
|
||||
public var rowsCount:int;
|
||||
public var rangeBegin:int;
|
||||
public var rangeLength:int;
|
||||
public var fps:int;
|
||||
public var loop:Boolean;
|
||||
public var originX:Number;
|
||||
public var originY:Number;
|
||||
|
||||
public function TextureAtlas(diffuse:TextureResource, opacity:TextureResource = null, columnsCount:int = 1, rowsCount:int = 1, rangeBegin:int = 0, rangeLength:int = 1, fps:int = 30, loop:Boolean = false, originX:Number = 0.5, originY:Number = 0.5) {
|
||||
this.diffuse = diffuse;
|
||||
this.opacity = opacity;
|
||||
this.columnsCount = columnsCount;
|
||||
this.rowsCount = rowsCount;
|
||||
this.rangeBegin = rangeBegin;
|
||||
this.rangeLength = rangeLength;
|
||||
this.fps = fps;
|
||||
this.loop = loop;
|
||||
this.originX = originX;
|
||||
this.originY = originY;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
64
src/alternativa/engine3d/lights/AmbientLight.as
Normal file
64
src/alternativa/engine3d/lights/AmbientLight.as
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.lights {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* An ambient light source represents a fixed-intensity and fixed-color light source
|
||||
* that affects all objects in the scene equally. Upon rendering, all objects in
|
||||
* the scene are brightened with the specified intensity and color.
|
||||
* This type of light source is mainly used to provide the scene with a basic view of the different objects in it.
|
||||
*
|
||||
* This description taken from http://en.wikipedia.org/wiki/Shading#Ambient_lighting
|
||||
*/
|
||||
public class AmbientLight extends Light3D {
|
||||
|
||||
/**
|
||||
* Creates a AmbientLight object.
|
||||
* @param color Light color.
|
||||
*/
|
||||
public function AmbientLight(color:uint) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not do anything.
|
||||
*
|
||||
*/
|
||||
override public function calculateBoundBox():void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function calculateVisibility(camera:Camera3D):void {
|
||||
camera.ambient[0] += ((color >> 16) & 0xFF)*intensity/255;
|
||||
camera.ambient[1] += ((color >> 8) & 0xFF)*intensity/255;
|
||||
camera.ambient[2] += (color & 0xFF)*intensity/255;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Object3D {
|
||||
var res:AmbientLight = new AmbientLight(color);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
67
src/alternativa/engine3d/lights/DirectionalLight.as
Normal file
67
src/alternativa/engine3d/lights/DirectionalLight.as
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.lights {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* A directional light source illuminates all objects equally from a given direction,
|
||||
* like an area light of infinite size and infinite distance from the scene;
|
||||
* there is shading, but cannot be any distance falloff.
|
||||
*
|
||||
* This description taken from http://en.wikipedia.org/wiki/Shading#Directional_lighting
|
||||
*
|
||||
* Lightning direction defines by z-axis of DirectionalLight.
|
||||
* You can use lookAt() to make DirectionalLight point at given coordinates.
|
||||
*/
|
||||
public class DirectionalLight extends Light3D {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param color Color of light source.
|
||||
*/
|
||||
public function DirectionalLight(color:uint) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets direction of DirectionalLight to given coordinates.
|
||||
*/
|
||||
public function lookAt(x:Number, y:Number, z:Number):void {
|
||||
var dx:Number = x - this.x;
|
||||
var dy:Number = y - this.y;
|
||||
var dz:Number = z - this.z;
|
||||
rotationX = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy)) - Math.PI/2;
|
||||
rotationY = 0;
|
||||
rotationZ = -Math.atan2(dx, dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not do anything.
|
||||
*/
|
||||
override public function calculateBoundBox():void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Object3D {
|
||||
var res:DirectionalLight = new DirectionalLight(color);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
206
src/alternativa/engine3d/lights/OmniLight.as
Normal file
206
src/alternativa/engine3d/lights/OmniLight.as
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.lights {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Transform3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* OmniLight is an attenuated light source placed at one point and spreads outward in all directions.
|
||||
*
|
||||
*/
|
||||
public class OmniLight extends Light3D {
|
||||
|
||||
/**
|
||||
* Distance from which falloff starts.
|
||||
*/
|
||||
public var attenuationBegin:Number;
|
||||
|
||||
/**
|
||||
* Distance from at which falloff is complete.
|
||||
*/
|
||||
public var attenuationEnd:Number;
|
||||
|
||||
/**
|
||||
* Creates a OmniLight object.
|
||||
* @param color Light color.
|
||||
* @param attenuationBegin Distance from which falloff starts.
|
||||
* @param attenuationEnd Distance from at which falloff is complete.
|
||||
*/
|
||||
public function OmniLight(color:uint, attenuationBegin:Number, attenuationEnd:Number) {
|
||||
this.color = color;
|
||||
this.attenuationBegin = attenuationBegin;
|
||||
this.attenuationEnd = attenuationEnd;
|
||||
calculateBoundBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function updateBoundBox(boundBox:BoundBox, transform:Transform3D = null):void {
|
||||
if (transform != null) {
|
||||
|
||||
} else {
|
||||
if (-attenuationEnd < boundBox.minX) boundBox.minX = -attenuationEnd;
|
||||
if (attenuationEnd > boundBox.maxX) boundBox.maxX = attenuationEnd;
|
||||
if (-attenuationEnd < boundBox.minY) boundBox.minY = -attenuationEnd;
|
||||
if (attenuationEnd > boundBox.maxY) boundBox.maxY = attenuationEnd;
|
||||
if (-attenuationEnd < boundBox.minZ) boundBox.minZ = -attenuationEnd;
|
||||
if (attenuationEnd > boundBox.maxZ) boundBox.maxZ = attenuationEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function checkBound(targetObject:Object3D):Boolean {
|
||||
var rScale:Number = Math.sqrt(lightToObjectTransform.a*lightToObjectTransform.a + lightToObjectTransform.e*lightToObjectTransform.e + lightToObjectTransform.i*lightToObjectTransform.i);
|
||||
rScale += Math.sqrt(lightToObjectTransform.b*lightToObjectTransform.b + lightToObjectTransform.f*lightToObjectTransform.f + lightToObjectTransform.j*lightToObjectTransform.j);
|
||||
rScale += Math.sqrt(lightToObjectTransform.c*lightToObjectTransform.c + lightToObjectTransform.g*lightToObjectTransform.g + lightToObjectTransform.k*lightToObjectTransform.k);
|
||||
rScale /= 3;
|
||||
rScale *= attenuationEnd;
|
||||
rScale *= rScale;
|
||||
var len:Number = 0;
|
||||
var bb:BoundBox = targetObject.boundBox;
|
||||
var minX:Number = bb.minX;
|
||||
var minY:Number = bb.minY;
|
||||
var minZ:Number = bb.minZ;
|
||||
var maxX:Number = bb.maxX;
|
||||
var px:Number = lightToObjectTransform.d;
|
||||
var py:Number = lightToObjectTransform.h;
|
||||
var pz:Number = lightToObjectTransform.l;
|
||||
|
||||
var maxY:Number = bb.maxY;
|
||||
var maxZ:Number = bb.maxZ;
|
||||
if (px < minX) {
|
||||
if (py < minY) {
|
||||
if (pz < minZ) {
|
||||
len = (minX - px)*(minX - px) + (minY - py)*(minY - py) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (minX - px)*(minX - px) + (minY - py)*(minY - py);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (minX - px)*(minX - px) + (minY - py)*(minY - py) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
} else if (py < maxY) {
|
||||
if (pz < minZ) {
|
||||
len = (minX - px)*(minX - px) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (minX - px)*(minX - px);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (minX - px)*(minX - px) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
} else if (py > maxY) {
|
||||
if (pz < minZ) {
|
||||
len = (minX - px)*(minX - px) + (maxY - py)*(maxY - py) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (minX - px)*(minX - px) + (maxY - py)*(maxY - py);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (minX - px)*(minX - px) + (maxY - py)*(maxY - py) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
}
|
||||
} else if (px < maxX) {
|
||||
if (py < minY) {
|
||||
if (pz < minZ) {
|
||||
len = (minY - py)*(minY - py) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (minY - py)*(minY - py);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (minY - py)*(minY - py) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
} else if (py < maxY) {
|
||||
if (pz < minZ) {
|
||||
len = (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
return true;
|
||||
} else if (pz > maxZ) {
|
||||
len = (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
} else if (py > maxY) {
|
||||
if (pz < minZ) {
|
||||
len = (maxY - py)*(maxY - py) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (maxY - py)*(maxY - py);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (maxY - py)*(maxY - py) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
}
|
||||
} else if (px > maxX) {
|
||||
if (py < minY) {
|
||||
if (pz < minZ) {
|
||||
len = (maxX - px)*(maxX - px) + (minY - py)*(minY - py) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (maxX - px)*(maxX - px) + (minY - py)*(minY - py);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (maxX - px)*(maxX - px) + (minY - py)*(minY - py) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
} else if (py < maxY) {
|
||||
if (pz < minZ) {
|
||||
len = (maxX - px)*(maxX - px) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (maxX - px)*(maxX - px);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (maxX - px)*(maxX - px) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
} else if (py > maxY) {
|
||||
if (pz < minZ) {
|
||||
len = (maxX - px)*(maxX - px) + (maxY - py)*(maxY - py) + (minZ - pz)*(minZ - pz);
|
||||
return len < rScale;
|
||||
} else if (pz < maxZ) {
|
||||
len = (maxX - px)*(maxX - px) + (maxY - py)*(maxY - py);
|
||||
return len < rScale;
|
||||
} else if (pz > maxZ) {
|
||||
len = (maxX - px)*(maxX - px) + (maxY - py)*(maxY - py) + (maxZ - pz)*(maxZ - pz);
|
||||
return len < rScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Object3D {
|
||||
var res:OmniLight = new OmniLight(color, attenuationBegin, attenuationEnd);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
211
src/alternativa/engine3d/lights/SpotLight.as
Normal file
211
src/alternativa/engine3d/lights/SpotLight.as
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.lights {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Transform3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* OmniLight is an attenuated light source placed at one point and spreads outward in a coned direction.
|
||||
*
|
||||
* Lightning direction defines by z-axis of OmniLight.
|
||||
* You can use lookAt() to make DirectionalLight point at given coordinates.
|
||||
*/
|
||||
public class SpotLight extends Light3D {
|
||||
|
||||
/**
|
||||
* Distance from which falloff starts.
|
||||
*/
|
||||
public var attenuationBegin:Number;
|
||||
|
||||
/**
|
||||
* Distance from at which falloff is complete.
|
||||
*/
|
||||
public var attenuationEnd:Number;
|
||||
|
||||
/**
|
||||
* Adjusts the angle of a light's cone.
|
||||
*/
|
||||
public var hotspot:Number;
|
||||
|
||||
/**
|
||||
* Adjusts the angle of a light's falloff. For photometric lights, the Field angle is comparable
|
||||
* to the Falloff angle. It is the angle at which the light's intensity has fallen to zero.
|
||||
*/
|
||||
public var falloff:Number;
|
||||
|
||||
/**
|
||||
* Creates a new SpotLight instance.
|
||||
* @param color Light color.
|
||||
* @param attenuationBegin Distance from which falloff starts.
|
||||
* @param attenuationEnd Distance from at which falloff is complete.
|
||||
* @param hotspot Adjusts the angle of a light's cone. The Hotspot value is measured in radians.
|
||||
* @param falloff Adjusts the angle of a light's falloff. The Falloff value is measured in radians.
|
||||
*/
|
||||
public function SpotLight(color:uint, attenuationBegin:Number, attenuationEnd:Number, hotspot:Number, falloff:Number) {
|
||||
this.color = color;
|
||||
this.attenuationBegin = attenuationBegin;
|
||||
this.attenuationEnd = attenuationEnd;
|
||||
this.hotspot = hotspot;
|
||||
this.falloff = falloff;
|
||||
calculateBoundBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function updateBoundBox(boundBox:BoundBox, transform:Transform3D = null):void {
|
||||
var r:Number = (falloff < Math.PI) ? Math.sin(falloff*0.5)*attenuationEnd : attenuationEnd;
|
||||
var bottom:Number = (falloff < Math.PI) ? 0 : Math.cos(falloff*0.5)*attenuationEnd;
|
||||
boundBox.minX = -r;
|
||||
boundBox.minY = -r;
|
||||
boundBox.minZ = bottom;
|
||||
boundBox.maxX = r;
|
||||
boundBox.maxY = r;
|
||||
boundBox.maxZ = attenuationEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set direction of the light direction to the given coordinates..
|
||||
*/
|
||||
public function lookAt(x:Number, y:Number, z:Number):void {
|
||||
var dx:Number = x - this.x;
|
||||
var dy:Number = y - this.y;
|
||||
var dz:Number = z - this.z;
|
||||
rotationX = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy)) - Math.PI/2;
|
||||
rotationY = 0;
|
||||
rotationZ = -Math.atan2(dx, dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function checkBound(targetObject:Object3D):Boolean {
|
||||
var minX:Number = boundBox.minX;
|
||||
var minY:Number = boundBox.minY;
|
||||
var minZ:Number = boundBox.minZ;
|
||||
var maxX:Number = boundBox.maxX;
|
||||
var maxY:Number = boundBox.maxY;
|
||||
var maxZ:Number = boundBox.maxZ;
|
||||
var sum:Number;
|
||||
var pro:Number;
|
||||
// Half sizes of the source's boundbox
|
||||
var w:Number = (maxX - minX)*0.5;
|
||||
var l:Number = (maxY - minY)*0.5;
|
||||
var h:Number = (maxZ - minZ)*0.5;
|
||||
// Half-vectors of the source's boundbox
|
||||
var ax:Number = lightToObjectTransform.a*w;
|
||||
var ay:Number = lightToObjectTransform.e*w;
|
||||
var az:Number = lightToObjectTransform.i*w;
|
||||
var bx:Number = lightToObjectTransform.b*l;
|
||||
var by:Number = lightToObjectTransform.f*l;
|
||||
var bz:Number = lightToObjectTransform.j*l;
|
||||
var cx:Number = lightToObjectTransform.c*h;
|
||||
var cy:Number = lightToObjectTransform.g*h;
|
||||
var cz:Number = lightToObjectTransform.k*h;
|
||||
// Half sizes of the boundboxes
|
||||
var objectBB:BoundBox = targetObject.boundBox;
|
||||
var hw:Number = (objectBB.maxX - objectBB.minX)*0.5;
|
||||
var hl:Number = (objectBB.maxY - objectBB.minY)*0.5;
|
||||
var hh:Number = (objectBB.maxZ - objectBB.minZ)*0.5;
|
||||
// Vector between centers of the bounboxes
|
||||
var dx:Number = lightToObjectTransform.a*(minX + w) + lightToObjectTransform.b*(minY + l) + lightToObjectTransform.c*(minZ + h) + lightToObjectTransform.d - objectBB.minX - hw;
|
||||
var dy:Number = lightToObjectTransform.e*(minX + w) + lightToObjectTransform.f*(minY + l) + lightToObjectTransform.g*(minZ + h) + lightToObjectTransform.h - objectBB.minY - hl;
|
||||
var dz:Number = lightToObjectTransform.i*(minX + w) + lightToObjectTransform.j*(minY + l) + lightToObjectTransform.k*(minZ + h) + lightToObjectTransform.l - objectBB.minZ - hh;
|
||||
|
||||
// X of the object
|
||||
sum = 0;
|
||||
if (ax >= 0) sum += ax; else sum -= ax;
|
||||
if (bx >= 0) sum += bx; else sum -= bx;
|
||||
if (cx >= 0) sum += cx; else sum -= cx;
|
||||
sum += hw;
|
||||
if (dx >= 0) sum -= dx;
|
||||
sum += dx;
|
||||
if (sum <= 0) return false;
|
||||
// Y of the object
|
||||
sum = 0;
|
||||
if (ay >= 0) sum += ay; else sum -= ay;
|
||||
if (by >= 0) sum += by; else sum -= by;
|
||||
if (cy >= 0) sum += cy; else sum -= cy;
|
||||
sum += hl;
|
||||
if (dy >= 0) sum -= dy; else sum += dy;
|
||||
if (sum <= 0) return false;
|
||||
// Z of the object
|
||||
sum = 0;
|
||||
if (az >= 0) sum += az; else sum -= az;
|
||||
if (bz >= 0) sum += bz; else sum -= bz;
|
||||
if (cz >= 0) sum += cz; else sum -= cz;
|
||||
sum += hl;
|
||||
if (dz >= 0) sum -= dz; else sum += dz;
|
||||
if (sum <= 0) return false;
|
||||
// X of the source
|
||||
sum = 0;
|
||||
pro = lightToObjectTransform.a*ax + lightToObjectTransform.e*ay + lightToObjectTransform.i*az;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
pro = lightToObjectTransform.a*bx + lightToObjectTransform.e*by + lightToObjectTransform.i*bz;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
pro = lightToObjectTransform.a*cx + lightToObjectTransform.e*cy + lightToObjectTransform.i*cz;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
if (lightToObjectTransform.a >= 0) sum += lightToObjectTransform.a*hw; else sum -= lightToObjectTransform.a*hw;
|
||||
if (lightToObjectTransform.e >= 0) sum += lightToObjectTransform.e*hl; else sum -= lightToObjectTransform.e*hl;
|
||||
if (lightToObjectTransform.i >= 0) sum += lightToObjectTransform.i*hh; else sum -= lightToObjectTransform.i*hh;
|
||||
pro = lightToObjectTransform.a*dx + lightToObjectTransform.e*dy + lightToObjectTransform.i*dz;
|
||||
if (pro >= 0) sum -= pro; else sum += pro;
|
||||
if (sum <= 0) return false;
|
||||
// Y of the source
|
||||
sum = 0;
|
||||
pro = lightToObjectTransform.b*ax + lightToObjectTransform.f*ay + lightToObjectTransform.j*az;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
pro = lightToObjectTransform.b*bx + lightToObjectTransform.f*by + lightToObjectTransform.j*bz;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
pro = lightToObjectTransform.b*cx + lightToObjectTransform.f*cy + lightToObjectTransform.j*cz;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
if (lightToObjectTransform.b >= 0) sum += lightToObjectTransform.b*hw; else sum -= lightToObjectTransform.b*hw;
|
||||
if (lightToObjectTransform.f >= 0) sum += lightToObjectTransform.f*hl; else sum -= lightToObjectTransform.f*hl;
|
||||
if (lightToObjectTransform.j >= 0) sum += lightToObjectTransform.j*hh; else sum -= lightToObjectTransform.j*hh;
|
||||
pro = lightToObjectTransform.b*dx + lightToObjectTransform.f*dy + lightToObjectTransform.j*dz;
|
||||
if (pro >= 0) sum -= pro;
|
||||
sum += pro;
|
||||
if (sum <= 0) return false;
|
||||
// Z of the source
|
||||
sum = 0;
|
||||
pro = lightToObjectTransform.c*ax + lightToObjectTransform.g*ay + lightToObjectTransform.k*az;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
pro = lightToObjectTransform.c*bx + lightToObjectTransform.g*by + lightToObjectTransform.k*bz;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
pro = lightToObjectTransform.c*cx + lightToObjectTransform.g*cy + lightToObjectTransform.k*cz;
|
||||
if (pro >= 0) sum += pro; else sum -= pro;
|
||||
if (lightToObjectTransform.c >= 0) sum += lightToObjectTransform.c*hw; else sum -= lightToObjectTransform.c*hw;
|
||||
if (lightToObjectTransform.g >= 0) sum += lightToObjectTransform.g*hl; else sum -= lightToObjectTransform.g*hl;
|
||||
if (lightToObjectTransform.k >= 0) sum += lightToObjectTransform.k*hh; else sum -= lightToObjectTransform.k*hh;
|
||||
pro = lightToObjectTransform.c*dx + lightToObjectTransform.g*dy + lightToObjectTransform.k*dz;
|
||||
if (pro >= 0) sum -= pro; else sum += pro;
|
||||
if (sum <= 0) return false;
|
||||
// TODO: checking on random axises
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Object3D {
|
||||
var res:SpotLight = new SpotLight(color, attenuationBegin, attenuationEnd, hotspot, falloff);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
633
src/alternativa/engine3d/loaders/ExporterA3D.as
Normal file
633
src/alternativa/engine3d/loaders/ExporterA3D.as
Normal file
@@ -0,0 +1,633 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.AnimationClip;
|
||||
import alternativa.engine3d.animation.keys.TransformKey;
|
||||
import alternativa.engine3d.animation.keys.TransformTrack;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Transform3D;
|
||||
import alternativa.engine3d.core.VertexAttributes;
|
||||
import alternativa.engine3d.core.VertexStream;
|
||||
import alternativa.engine3d.lights.AmbientLight;
|
||||
import alternativa.engine3d.lights.DirectionalLight;
|
||||
import alternativa.engine3d.lights.OmniLight;
|
||||
import alternativa.engine3d.lights.SpotLight;
|
||||
import alternativa.engine3d.materials.LightMapMaterial;
|
||||
import alternativa.engine3d.materials.Material;
|
||||
import alternativa.engine3d.materials.StandardMaterial;
|
||||
import alternativa.engine3d.materials.TextureMaterial;
|
||||
import alternativa.engine3d.objects.Joint;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
import alternativa.engine3d.objects.Skin;
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
import alternativa.engine3d.resources.TextureResource;
|
||||
import alternativa.osgi.OSGi;
|
||||
import alternativa.osgi.service.clientlog.IClientLog;
|
||||
import alternativa.protocol.CompressionType;
|
||||
import alternativa.protocol.ICodec;
|
||||
import alternativa.protocol.IProtocol;
|
||||
import alternativa.protocol.OptionalMap;
|
||||
import alternativa.protocol.ProtocolBuffer;
|
||||
import alternativa.protocol.impl.PacketHelper;
|
||||
import alternativa.protocol.impl.Protocol;
|
||||
import alternativa.protocol.info.TypeCodecInfo;
|
||||
import alternativa.protocol.osgi.ProtocolActivator;
|
||||
import alternativa.types.Long;
|
||||
|
||||
import commons.A3DMatrix;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Endian;
|
||||
|
||||
import platform.client.formats.a3d.osgi.Activator;
|
||||
import platform.clients.fp10.libraries.alternativaprotocol.Activator;
|
||||
|
||||
import versions.version2.a3d.A3D2;
|
||||
import versions.version2.a3d.animation.A3D2AnimationClip;
|
||||
import versions.version2.a3d.animation.A3D2Keyframe;
|
||||
import versions.version2.a3d.animation.A3D2Track;
|
||||
import versions.version2.a3d.geometry.A3D2IndexBuffer;
|
||||
import versions.version2.a3d.geometry.A3D2VertexAttributes;
|
||||
import versions.version2.a3d.geometry.A3D2VertexBuffer;
|
||||
import versions.version2.a3d.materials.A3D2Image;
|
||||
import versions.version2.a3d.materials.A3D2Map;
|
||||
import versions.version2.a3d.materials.A3D2Material;
|
||||
import versions.version2.a3d.objects.A3D2AmbientLight;
|
||||
import versions.version2.a3d.objects.A3D2Box;
|
||||
import versions.version2.a3d.objects.A3D2DirectionalLight;
|
||||
import versions.version2.a3d.objects.A3D2Joint;
|
||||
import versions.version2.a3d.objects.A3D2JointBindTransform;
|
||||
import versions.version2.a3d.objects.A3D2Mesh;
|
||||
import versions.version2.a3d.objects.A3D2Object;
|
||||
import versions.version2.a3d.objects.A3D2OmniLight;
|
||||
import versions.version2.a3d.objects.A3D2Skin;
|
||||
import versions.version2.a3d.objects.A3D2SpotLight;
|
||||
import versions.version2.a3d.objects.A3D2Surface;
|
||||
import versions.version2.a3d.objects.A3D2Transform;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* An object which allows to convert hierarchy of three-dimensional objects to binary A3D format.
|
||||
*/
|
||||
public class ExporterA3D {
|
||||
|
||||
private var wasInit:Boolean = false;
|
||||
private var protocol:Protocol;
|
||||
private var parents:Dictionary;
|
||||
private var geometries:Dictionary;
|
||||
private var images:Dictionary;
|
||||
|
||||
private var indexBufferID:int;
|
||||
private var materialID:int;
|
||||
private var mapID:int;
|
||||
private var imageID:int;
|
||||
private var animationID:int;
|
||||
private var trackID:int;
|
||||
private var vertexBufferID:int;
|
||||
private var boxID:int;
|
||||
|
||||
private var tracksMap:Dictionary;
|
||||
private var materialsMap:Dictionary;
|
||||
private var mapsMap:Dictionary;
|
||||
|
||||
alternativa3d var idGenerator:IIDGenerator = new IncrementalIDGenerator();
|
||||
|
||||
/**
|
||||
* Creates an instance of ExporterA3D.
|
||||
*/
|
||||
public function ExporterA3D() {
|
||||
init();
|
||||
}
|
||||
|
||||
private function init():void {
|
||||
if (wasInit) return;
|
||||
if (OSGi.getInstance() != null) {
|
||||
protocol = Protocol(OSGi.getInstance().getService(IProtocol));
|
||||
return;
|
||||
}
|
||||
OSGi.clientLog = new DummyClientLog();
|
||||
var osgi:OSGi = new OSGi();
|
||||
osgi.registerService(IClientLog, new DummyClientLog());
|
||||
|
||||
new ProtocolActivator().start(osgi);
|
||||
new platform.client.formats.a3d.osgi.Activator().start(osgi);
|
||||
new platform.clients.fp10.libraries.alternativaprotocol.Activator().start(osgi);
|
||||
protocol = Protocol(osgi.getService(IProtocol));
|
||||
wasInit = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a scene to A3D format.
|
||||
* @param root Root object of scene.
|
||||
* @return Data in A3D format.
|
||||
*/
|
||||
public function export(root:Object3D = null, animations:Vector.<AnimationClip> = null):ByteArray {
|
||||
|
||||
boxID = 0;
|
||||
indexBufferID = 0;
|
||||
vertexBufferID = 0;
|
||||
materialID = 0;
|
||||
mapID = 0;
|
||||
imageID = 0;
|
||||
animationID = 0;
|
||||
materialsMap = new Dictionary();
|
||||
mapsMap = new Dictionary();
|
||||
geometries = new Dictionary();
|
||||
tracksMap = new Dictionary();
|
||||
|
||||
images = new Dictionary();
|
||||
|
||||
parents = new Dictionary();
|
||||
|
||||
var a3D:A3D2 = new A3D2(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
if (root != null) {
|
||||
exportHierarchy(root, a3D);
|
||||
}
|
||||
if (animations != null) {
|
||||
for each (var animation:AnimationClip in animations) {
|
||||
exportAnimation(animation, a3D);
|
||||
}
|
||||
}
|
||||
|
||||
var data:ByteArray = new ByteArray();
|
||||
var result:ByteArray = new ByteArray();
|
||||
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2, false));
|
||||
|
||||
var protocolBuffer:ProtocolBuffer = new ProtocolBuffer(data, data, new OptionalMap());
|
||||
data.writeShort(2);
|
||||
data.writeShort(0);
|
||||
codec.encode(protocolBuffer, a3D);
|
||||
data.position = 0;
|
||||
PacketHelper.wrapPacket(result, protocolBuffer, CompressionType.DEFLATE);
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportAnimation(source:AnimationClip, dest:A3D2):void {
|
||||
var anim:A3D2AnimationClip = new A3D2AnimationClip(animationID, source.loop, source.name, null, exportTracks(source, dest));
|
||||
if (dest.animationClips == null) dest.animationClips = new Vector.<A3D2AnimationClip>();
|
||||
dest.animationClips[animationID] = anim; animationID++;
|
||||
}
|
||||
|
||||
private function exportTracks(source:AnimationClip, dest:A3D2):Vector.<int> {
|
||||
var id:int;
|
||||
var result:Vector.<int> = new Vector.<int>();
|
||||
for (var i:int = 0; i < source.numTracks; i++) {
|
||||
var t:TransformTrack = source.getTrackAt(i) as TransformTrack;
|
||||
if (t != null && tracksMap[t] == null) {
|
||||
id = trackID++;
|
||||
var exportTrack:A3D2Track = new A3D2Track(id, exportKeyframes(t), t.object);
|
||||
tracksMap[t] = id;
|
||||
if (dest.animationTracks == null) dest.animationTracks = new Vector.<A3D2Track>();
|
||||
dest.animationTracks[id] = exportTrack;
|
||||
} else {
|
||||
id = tracksMap[t];
|
||||
}
|
||||
result.push(id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportKeyframes(source:TransformTrack):Vector.<A3D2Keyframe> {
|
||||
var result:Vector.<A3D2Keyframe> = new Vector.<A3D2Keyframe>();
|
||||
for (var key:TransformKey = TransformKey(source.keyFramesList); key.next != null; key = key.next) {
|
||||
var exportKey:A3D2Keyframe = new A3D2Keyframe(key._time, exportTransformFromKeyframe(key));
|
||||
result.push(exportKey);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportTransformFromKeyframe(key:TransformKey):A3D2Transform {
|
||||
var m:Matrix3D = Matrix3D(key.value);
|
||||
var vec:Vector.<Number> = m.rawData;
|
||||
var exportTransform:A3D2Transform = new A3D2Transform(
|
||||
new A3DMatrix(
|
||||
vec[0], vec[4], vec[8], vec[12],
|
||||
vec[1], vec[5], vec[9], vec[13],
|
||||
vec[2], vec[6], vec[10], vec[14]
|
||||
));
|
||||
return exportTransform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function exportHierarchy(source:Object3D, dest:A3D2):void {
|
||||
var id:Long = idGenerator.getID(source);
|
||||
if (source.transformChanged) {
|
||||
source.composeTransforms();
|
||||
}
|
||||
if (source is SpotLight) {
|
||||
exportSpotLight(id, source as SpotLight, dest);
|
||||
} else if (source is OmniLight) {
|
||||
exportOmniLight(id, source as OmniLight, dest);
|
||||
} else if (source is DirectionalLight) {
|
||||
exportDirLight(id, source as DirectionalLight, dest);
|
||||
} else if (source is AmbientLight) {
|
||||
exportAmbientLight(id, source as AmbientLight, dest);
|
||||
} else if (source is Skin) {
|
||||
exportSkin(id, source as Skin, dest);
|
||||
} else if (source is Mesh) {
|
||||
exportMesh(id, source as Mesh, dest);
|
||||
} else if (source is Joint) {
|
||||
exportJoint(id, source as Joint, dest);
|
||||
} else if (source is Object3D) {
|
||||
exportObject3D(id, source, dest);
|
||||
} else {
|
||||
trace("Unsupported object type", source);
|
||||
}
|
||||
parents[source] = id;
|
||||
|
||||
for (var child:Object3D = source.childrenList; child != null; child = child.next) {
|
||||
exportHierarchy(child, dest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function exportJoint(id:Long, source:Joint, dest:A3D2):void {
|
||||
|
||||
var a3DObject:A3D2Joint = new A3D2Joint(
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
id,
|
||||
source.name,
|
||||
parents[source.parent is Skin ? source.parent.parent : source.parent],
|
||||
exportTransform(source.transform),
|
||||
source.visible
|
||||
);
|
||||
if (dest.joints == null) dest.joints = new Vector.<A3D2Joint>();
|
||||
dest.joints.push(a3DObject);
|
||||
}
|
||||
|
||||
private function exportObject3D(id:Long, source:Object3D, dest:A3D2):void {
|
||||
var a3DObject:A3D2Object = new A3D2Object(
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
id,
|
||||
source.name,
|
||||
parents[source.parent],
|
||||
exportTransform(source.transform),
|
||||
source.visible
|
||||
);
|
||||
if (dest.objects == null) dest.objects = new Vector.<A3D2Object>();
|
||||
dest.objects.push(a3DObject);
|
||||
}
|
||||
|
||||
private function exportSpotLight(id:Long, source:SpotLight, dest:A3D2):void {
|
||||
var a3DObject:A3D2SpotLight = new A3D2SpotLight(
|
||||
source.attenuationBegin,
|
||||
source.attenuationEnd,
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
source.color,
|
||||
source.falloff,
|
||||
source.hotspot,
|
||||
id,
|
||||
source.intensity,
|
||||
source.name,
|
||||
parents[source.parent],
|
||||
exportTransform(source.transform),
|
||||
source.visible
|
||||
);
|
||||
if (dest.spotLights == null) dest.spotLights = new Vector.<A3D2SpotLight>();
|
||||
dest.spotLights.push(a3DObject);
|
||||
}
|
||||
|
||||
private function exportOmniLight(id:Long, source:OmniLight, dest:A3D2):void {
|
||||
var a3DObject:A3D2OmniLight = new A3D2OmniLight(
|
||||
source.attenuationBegin,
|
||||
source.attenuationEnd,
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
source.color,
|
||||
id,
|
||||
source.intensity,
|
||||
source.name,
|
||||
parents[source.parent],
|
||||
exportTransform(source.transform),
|
||||
source.visible
|
||||
);
|
||||
if (dest.omniLights == null) dest.omniLights = new Vector.<A3D2OmniLight>();
|
||||
dest.omniLights.push(a3DObject);
|
||||
}
|
||||
|
||||
private function exportDirLight(id:Long, source:DirectionalLight, dest:A3D2):void {
|
||||
var a3DObject:A3D2DirectionalLight = new A3D2DirectionalLight(
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
source.color,
|
||||
id,
|
||||
source.intensity,
|
||||
source.name,
|
||||
parents[source.parent],
|
||||
exportTransform(source.transform),
|
||||
source.visible
|
||||
);
|
||||
if (dest.directionalLights == null) dest.directionalLights = new Vector.<A3D2DirectionalLight>();
|
||||
dest.directionalLights.push(a3DObject);
|
||||
|
||||
}
|
||||
|
||||
private function exportAmbientLight(id:Long, source:AmbientLight, dest:A3D2):void {
|
||||
var a3DObject:A3D2AmbientLight = new A3D2AmbientLight(
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
source.color,
|
||||
id,
|
||||
source.intensity,
|
||||
source.name,
|
||||
parents[source.parent],
|
||||
exportTransform(source.transform),
|
||||
source.visible
|
||||
);
|
||||
if (dest.ambientLights == null) dest.ambientLights = new Vector.<A3D2AmbientLight>();
|
||||
dest.ambientLights.push(a3DObject);
|
||||
}
|
||||
|
||||
private function exportMesh(id:Long, source:Mesh, dest:A3D2):void {
|
||||
var geometryData:GeometryData = exportGeometry(source.geometry, dest);
|
||||
var a3DMesh:A3D2Mesh = new A3D2Mesh(
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
id,
|
||||
geometryData.indexBufferID,
|
||||
source.name,
|
||||
parents[source.parent],
|
||||
exportSurfaces(source._surfaces, dest),
|
||||
exportTransform(source.transform),
|
||||
geometryData.vertexBufferIDs,
|
||||
source.visible
|
||||
);
|
||||
if (dest.meshes == null) dest.meshes = new Vector.<A3D2Mesh>();
|
||||
dest.meshes.push(a3DMesh);
|
||||
}
|
||||
|
||||
private function exportSkin(id:Long, source:Skin, dest:A3D2):A3D2Skin {
|
||||
var geometryData:GeometryData = exportGeometry(source.geometry, dest);
|
||||
var a3DSkin:A3D2Skin = new A3D2Skin(
|
||||
exportBoundBox(source.boundBox, dest),
|
||||
id,
|
||||
geometryData.indexBufferID,
|
||||
exportJointsBindTransforms(source._renderedJoints),
|
||||
exportJointsListFromSurfacesJoints(source.surfaceJoints),
|
||||
source.name,
|
||||
exportNumJoitns(source.surfaceJoints),
|
||||
null,
|
||||
exportSurfaces(source._surfaces, dest),
|
||||
exportTransform(source.transform),
|
||||
geometryData.vertexBufferIDs,
|
||||
source.visible
|
||||
);
|
||||
if (dest.skins == null) dest.skins = new Vector.<A3D2Skin>();
|
||||
dest.skins.push(a3DSkin);
|
||||
return a3DSkin;
|
||||
}
|
||||
|
||||
private function exportNumJoitns(surfaceJoints:Vector.<Vector.<Joint>>):Vector.<uint> {
|
||||
var result:Vector.<uint> = new Vector.<uint>();
|
||||
for (var i:int = 0; i < surfaceJoints.length; i++) {
|
||||
result.push(surfaceJoints[i].length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportJointsBindTransforms(joints:Vector.<Joint>):Vector.<A3D2JointBindTransform> {
|
||||
var result:Vector.<A3D2JointBindTransform> = new Vector.<A3D2JointBindTransform>();
|
||||
for each (var joint:Joint in joints) {
|
||||
result.push(new A3D2JointBindTransform(exportTransform(joint.bindPoseTransform), idGenerator.getID(joint)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportJointsListFromSurfacesJoints(surfaceJoints:Vector.<Vector.<Joint>>):Vector.<Long> {
|
||||
var result:Vector.<Long> = new Vector.<Long>();
|
||||
for (var i:int = 0; i < surfaceJoints.length; i++) {
|
||||
var joints:Vector.<Joint> = surfaceJoints[i];
|
||||
for each (var joint:Joint in joints) {
|
||||
result.push(idGenerator.getID(joint));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportSurfaces(surfaces:Vector.<Surface>, dest:A3D2):Vector.<A3D2Surface> {
|
||||
var result:Vector.<A3D2Surface> = new Vector.<A3D2Surface>();
|
||||
for (var i:int = 0, count:int = surfaces.length; i < count; i++) {
|
||||
var surface:Surface = surfaces[i];
|
||||
var resSurface:A3D2Surface = new A3D2Surface(surface.indexBegin, exportMaterial(surface.material, dest), surface.numTriangles);
|
||||
result[i] = resSurface;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportMaterial(source:Material, dest:A3D2):int {
|
||||
if (source == null) return -1;
|
||||
var result:A3D2Material = materialsMap[source];
|
||||
if (result != null) return result.id;
|
||||
if (source is ParserMaterial) {
|
||||
var parserMaterial:ParserMaterial = source as ParserMaterial;
|
||||
result = new A3D2Material(
|
||||
exportMap(parserMaterial.textures["diffuse"], 0, dest),
|
||||
exportMap(parserMaterial.textures["glossiness"], 0, dest),
|
||||
materialID,
|
||||
exportMap(parserMaterial.textures["emission"], 0, dest),
|
||||
exportMap(parserMaterial.textures["bump"], 0, dest),
|
||||
exportMap(parserMaterial.textures["transparent"], 0, dest),
|
||||
-1,
|
||||
exportMap(parserMaterial.textures["specular"], 0, dest)
|
||||
);
|
||||
} else if (source is LightMapMaterial) {
|
||||
var lightMapMaterial:LightMapMaterial = source as LightMapMaterial;
|
||||
result = new A3D2Material(
|
||||
exportMap(lightMapMaterial.diffuseMap, 0, dest),
|
||||
-1,
|
||||
materialID,
|
||||
exportMap(lightMapMaterial.lightMap, lightMapMaterial.lightMapChannel, dest),
|
||||
-1,
|
||||
exportMap(lightMapMaterial.opacityMap, 0, dest),
|
||||
-1,
|
||||
-1);
|
||||
} else if (source is StandardMaterial) {
|
||||
var standardMaterial:StandardMaterial = source as StandardMaterial;
|
||||
result = new A3D2Material(
|
||||
exportMap(standardMaterial.diffuseMap, 0, dest),
|
||||
exportMap(standardMaterial.glossinessMap, 0, dest), materialID,
|
||||
-1,
|
||||
exportMap(standardMaterial.normalMap, 0, dest),
|
||||
exportMap(standardMaterial.opacityMap, 0, dest),
|
||||
-1,
|
||||
exportMap(standardMaterial.specularMap, 0, dest));
|
||||
} else if (source is TextureMaterial) {
|
||||
var textureMaterial:TextureMaterial = source as TextureMaterial;
|
||||
result = new A3D2Material(exportMap(textureMaterial.diffuseMap, 0, dest), -1, materialID, -1, -1, exportMap(textureMaterial.opacityMap, 0, dest), -1, -1);
|
||||
}
|
||||
materialsMap[source] = result;
|
||||
if (dest.materials == null) dest.materials = new Vector.<A3D2Material>();
|
||||
dest.materials[materialID] = result;
|
||||
return materialID++;
|
||||
}
|
||||
|
||||
private function exportMap(source:TextureResource, channel:int, dest:A3D2):int {
|
||||
if (source == null) return -1;
|
||||
var result:A3D2Map = mapsMap[source];
|
||||
if (result != null) return result.id;
|
||||
if (source is ExternalTextureResource) {
|
||||
var resource:ExternalTextureResource = source as ExternalTextureResource;
|
||||
result = new A3D2Map(channel, mapID, exportImage(resource, dest));
|
||||
if (dest.maps == null) dest.maps = new Vector.<A3D2Map>();
|
||||
dest.maps[mapID] = result;
|
||||
mapsMap[source] = result;
|
||||
|
||||
return mapID++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private function exportImage(source:ExternalTextureResource, dest:A3D2):int {
|
||||
var image:Object = images[source];
|
||||
if (image != null) return int(image);
|
||||
var result:A3D2Image = new A3D2Image(imageID, source.url);
|
||||
if (dest.images == null) dest.images = new Vector.<A3D2Image>();
|
||||
dest.images[imageID] = result;
|
||||
return imageID++;
|
||||
}
|
||||
|
||||
private function exportGeometry(geometry:Geometry, dest:A3D2):GeometryData {
|
||||
var result:GeometryData = geometries[geometry];
|
||||
if (result != null) return result;
|
||||
result = new GeometryData();
|
||||
result.vertexBufferIDs = new Vector.<int>();
|
||||
var indicesData:ByteArray = new ByteArray();
|
||||
indicesData.endian = Endian.LITTLE_ENDIAN;
|
||||
var indices:Vector.<uint> = geometry.indices;
|
||||
for (var i:int = 0, count:int = indices.length; i < count; i++) {
|
||||
indicesData.writeShort(indices[i]);
|
||||
}
|
||||
var indexBuffer:A3D2IndexBuffer = new A3D2IndexBuffer(indicesData, indexBufferID, indices.length);
|
||||
result.indexBufferID = indexBufferID;
|
||||
if (dest.indexBuffers == null) dest.indexBuffers = new Vector.<A3D2IndexBuffer>();
|
||||
dest.indexBuffers[indexBufferID] = indexBuffer;
|
||||
indexBufferID++;
|
||||
for (i = 0,count = geometry._vertexStreams.length; i < count; i++) {
|
||||
var stream:VertexStream = geometry._vertexStreams[i];
|
||||
var buffer:A3D2VertexBuffer = new A3D2VertexBuffer(exportAttributes(stream.attributes), stream.data, vertexBufferID, geometry.numVertices);
|
||||
if (dest.vertexBuffers == null) dest.vertexBuffers = new Vector.<A3D2VertexBuffer>();
|
||||
dest.vertexBuffers[vertexBufferID] = buffer;
|
||||
result.vertexBufferIDs[i] = vertexBufferID++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportAttributes(attributes:Array):Vector.<A3D2VertexAttributes> {
|
||||
var prev:int = -1;
|
||||
var result:Vector.<A3D2VertexAttributes> = new Vector.<A3D2VertexAttributes>();
|
||||
for each (var attr:int in attributes) {
|
||||
if (attr == prev) continue;
|
||||
switch (attr) {
|
||||
case VertexAttributes.POSITION:
|
||||
result.push(A3D2VertexAttributes.POSITION);
|
||||
break;
|
||||
case VertexAttributes.NORMAL:
|
||||
result.push(A3D2VertexAttributes.NORMAL);
|
||||
break;
|
||||
case VertexAttributes.TANGENT4:
|
||||
result.push(A3D2VertexAttributes.TANGENT4);
|
||||
break;
|
||||
default:
|
||||
if ((attr >= VertexAttributes.JOINTS[0]) && (attr <= VertexAttributes.JOINTS[3])) {
|
||||
result.push(A3D2VertexAttributes.JOINT);
|
||||
} else if ((attr >= VertexAttributes.TEXCOORDS[0]) && (attr <= VertexAttributes.TEXCOORDS[7])) {
|
||||
result.push(A3D2VertexAttributes.TEXCOORD);
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = attr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private function exportTransform(source:Transform3D):A3D2Transform {
|
||||
return new A3D2Transform(new A3DMatrix(
|
||||
source.a, source.b, source.c, source.d,
|
||||
source.e, source.f, source.g, source.h,
|
||||
source.i, source.j, source.k, source.l
|
||||
));
|
||||
}
|
||||
|
||||
private function exportBoundBox(boundBox:BoundBox, dest:A3D2):int {
|
||||
if (boundBox == null) return -1;
|
||||
if (dest.boxes == null) dest.boxes = new Vector.<A3D2Box>();
|
||||
dest.boxes[boxID] = new A3D2Box(Vector.<Number>([boundBox.minX, boundBox.minY, boundBox.minZ, boundBox.maxX, boundBox.maxY, boundBox.maxZ]), boxID);
|
||||
return boxID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.osgi.service.clientlog.IClientLog;
|
||||
import alternativa.osgi.service.clientlog.IClientLogChannelListener;
|
||||
|
||||
class GeometryData {
|
||||
public var indexBufferID:int;
|
||||
public var vertexBufferIDs:Vector.<int>;
|
||||
|
||||
public function GeometryData(indexBufferID:int = -1, vertexBufferIDs:Vector.<int> = null) {
|
||||
this.indexBufferID = indexBufferID;
|
||||
this.vertexBufferIDs = vertexBufferIDs;
|
||||
}
|
||||
}
|
||||
|
||||
class DummyClientLog implements IClientLog {
|
||||
|
||||
public function logError(channelName:String, text:String, ... vars):void {
|
||||
}
|
||||
|
||||
public function log(channelName:String, text:String, ... rest):void {
|
||||
}
|
||||
|
||||
public function getChannelStrings(channelName:String):Vector.<String> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function addLogListener(listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function removeLogListener(listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function addLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function removeLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function getChannelNames():Vector.<String> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
23
src/alternativa/engine3d/loaders/IIDGenerator.as
Normal file
23
src/alternativa/engine3d/loaders/IIDGenerator.as
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.types.Long;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public interface IIDGenerator {
|
||||
function getID(object:Object3D):Long;
|
||||
|
||||
}
|
||||
}
|
||||
38
src/alternativa/engine3d/loaders/IncrementalIDGenerator.as
Normal file
38
src/alternativa/engine3d/loaders/IncrementalIDGenerator.as
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.types.Long;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class IncrementalIDGenerator implements IIDGenerator {
|
||||
|
||||
private var lastID:uint = 0;
|
||||
private var objects:Dictionary;
|
||||
|
||||
public function IncrementalIDGenerator() {
|
||||
objects = new Dictionary(true);
|
||||
}
|
||||
|
||||
public function getID(object:Object3D):Long {
|
||||
var result:Long = objects[object];
|
||||
if (result == null) {
|
||||
result = objects[object] = Long.fromInt(lastID); lastID++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
1067
src/alternativa/engine3d/loaders/Parser.as
Normal file
1067
src/alternativa/engine3d/loaders/Parser.as
Normal file
File diff suppressed because it is too large
Load Diff
1499
src/alternativa/engine3d/loaders/Parser3DS.as
Normal file
1499
src/alternativa/engine3d/loaders/Parser3DS.as
Normal file
File diff suppressed because it is too large
Load Diff
188
src/alternativa/engine3d/loaders/ParserA3D.as
Normal file
188
src/alternativa/engine3d/loaders/ParserA3D.as
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.osgi.OSGi;
|
||||
import alternativa.osgi.service.clientlog.IClientLog;
|
||||
import alternativa.protocol.ICodec;
|
||||
import alternativa.protocol.IProtocol;
|
||||
import alternativa.protocol.OptionalMap;
|
||||
import alternativa.protocol.ProtocolBuffer;
|
||||
import alternativa.protocol.impl.OptionalMapCodecHelper;
|
||||
import alternativa.protocol.impl.PacketHelper;
|
||||
import alternativa.protocol.impl.Protocol;
|
||||
import alternativa.protocol.info.TypeCodecInfo;
|
||||
import alternativa.protocol.osgi.ProtocolActivator;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
import platform.client.formats.a3d.osgi.Activator;
|
||||
import platform.clients.fp10.libraries.alternativaprotocol.Activator;
|
||||
|
||||
import versions.version1.a3d.A3D;
|
||||
import versions.version2.a3d.A3D2;
|
||||
import versions.version2.a3d.A3D2Extra1;
|
||||
import versions.version2.a3d.A3D2Extra2;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* A parser for loading models of A3D binary format.
|
||||
* A3D format reference you can find <a href="http://alternativaplatform.com/public/A3DFormat_en.pdf">here</a>.
|
||||
*/
|
||||
public class ParserA3D extends Parser {
|
||||
|
||||
// static public const logChannel:String = "ParserLog";
|
||||
|
||||
private var protocol:Protocol;
|
||||
|
||||
private var wasInit:Boolean = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of ParserA3D.
|
||||
*
|
||||
*/
|
||||
public function ParserA3D() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses model of a3d format, that is passed as byteArray to <code>input</code> parameter, then fills the arrays <code>objects</code> and <code>hierarchy</code> by the instances of three-dimensional objects.
|
||||
* @param input <code>ByteArray</code> consists of A3D data.
|
||||
*/
|
||||
public function parse(input:ByteArray):void {
|
||||
try {
|
||||
input.position = 0;
|
||||
var version:int = input.readByte();
|
||||
if (version == 0) {
|
||||
// For the 1st version of format
|
||||
parseVersion1(input);
|
||||
} else {
|
||||
// For the 2nd version of format and above, the first byte contains length of file and flag bits.
|
||||
// Bit of packing. It always equal to 1, because version 2 and above is always packed.
|
||||
parseVersionOver1(input);
|
||||
}
|
||||
} catch (e:Error) {
|
||||
e.message = "Parsing failed: " + e.message;
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function init():void {
|
||||
if (wasInit) return;
|
||||
var osgi:OSGi;
|
||||
if (OSGi.getInstance() == null) {
|
||||
osgi = new OSGi();
|
||||
OSGi.clientLog = new DummyClientLog();
|
||||
osgi.registerService(IClientLog, new DummyClientLog());
|
||||
new ProtocolActivator().start(osgi);
|
||||
new platform.clients.fp10.libraries.alternativaprotocol.Activator().start(osgi);
|
||||
} else {
|
||||
osgi = OSGi.getInstance();
|
||||
}
|
||||
new platform.client.formats.a3d.osgi.Activator().start(osgi);
|
||||
protocol = Protocol(osgi.getService(IProtocol));
|
||||
wasInit = true;
|
||||
}
|
||||
|
||||
private function parseVersion1(input:ByteArray):void {
|
||||
input.position = 4;
|
||||
var nullMap:OptionalMap = OptionalMapCodecHelper.decodeNullMap(input);
|
||||
nullMap.setReadPosition(0);
|
||||
var data:ByteArray = new ByteArray();
|
||||
data.writeBytes(input, input.position);
|
||||
data.position = 0;
|
||||
var buffer:ProtocolBuffer = new ProtocolBuffer(data, data, nullMap);
|
||||
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D, false));
|
||||
var _a3d:A3D = A3D(codec.decode(buffer));
|
||||
complete(_a3d);
|
||||
}
|
||||
|
||||
private function parseVersionOver1(input:ByteArray):void {
|
||||
input.position = 0;
|
||||
var data:ByteArray = new ByteArray();
|
||||
var buffer:ProtocolBuffer = new ProtocolBuffer(data, data, new OptionalMap());
|
||||
PacketHelper.unwrapPacket(input, buffer);
|
||||
input.position = 0;
|
||||
var versionMajor:int = buffer.reader.readUnsignedShort();
|
||||
var versionMinor:int = buffer.reader.readUnsignedShort();
|
||||
switch (versionMajor) {
|
||||
case 2:
|
||||
if (versionMinor >= 6) {
|
||||
compressedBuffers = true;
|
||||
}
|
||||
var parts:Vector.<Object> = new Vector.<Object>();
|
||||
parts.push(parseVersion2_0(buffer));
|
||||
if (versionMinor >= 4) {
|
||||
parts.push(parseVersion2_4(buffer));
|
||||
}
|
||||
if (versionMinor >= 5) {
|
||||
parts.push(parseVersion2_5(buffer));
|
||||
}
|
||||
complete(parts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion2_0(buffer:ProtocolBuffer):Object {
|
||||
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2, false));
|
||||
var a3d:A3D2 = A3D2(codec.decode(buffer));
|
||||
return a3d;
|
||||
}
|
||||
|
||||
private function parseVersion2_5(buffer:ProtocolBuffer):Object {
|
||||
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2Extra2, false));
|
||||
var a3d:A3D2Extra2 = A3D2Extra2(codec.decode(buffer));
|
||||
return a3d;
|
||||
}
|
||||
|
||||
private function parseVersion2_4(buffer:ProtocolBuffer):Object {
|
||||
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2Extra1, false));
|
||||
var a3d:A3D2Extra1 = A3D2Extra1(codec.decode(buffer));
|
||||
return a3d;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.osgi.service.clientlog.IClientLog;
|
||||
import alternativa.osgi.service.clientlog.IClientLogChannelListener;
|
||||
|
||||
class DummyClientLog implements IClientLog {
|
||||
|
||||
public function logError(channelName:String, text:String, ...vars):void {
|
||||
}
|
||||
|
||||
public function log(channelName:String, text:String, ...rest):void {
|
||||
}
|
||||
|
||||
public function getChannelStrings(channelName:String):Vector.<String> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function addLogListener(listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function removeLogListener(listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function addLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function removeLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
|
||||
}
|
||||
|
||||
public function getChannelNames():Vector.<String> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
391
src/alternativa/engine3d/loaders/ParserCollada.as
Normal file
391
src/alternativa/engine3d/loaders/ParserCollada.as
Normal file
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.AnimationClip;
|
||||
import alternativa.engine3d.animation.keys.Track;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.loaders.collada.DaeDocument;
|
||||
import alternativa.engine3d.loaders.collada.DaeMaterial;
|
||||
import alternativa.engine3d.loaders.collada.DaeNode;
|
||||
import alternativa.engine3d.loaders.collada.DaeObject;
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* A parser performs parsing of collada xml.
|
||||
*/
|
||||
public class ParserCollada extends Parser {
|
||||
|
||||
/**
|
||||
* List of the light sources
|
||||
*/
|
||||
public var lights:Vector.<Light3D>;
|
||||
|
||||
/**
|
||||
* Creates a new ParserCollada instance.
|
||||
*/
|
||||
public function ParserCollada() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all links to external objects.
|
||||
*/
|
||||
override public function clean():void {
|
||||
super.clean();
|
||||
lights = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Initialization before the parsing
|
||||
*/
|
||||
override alternativa3d function init():void {
|
||||
super.init();
|
||||
// cameras = new Vector.<Camera3D>();
|
||||
lights = new Vector.<Light3D>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method parses <code>xml</code> of collada and fills arrays <code>objects</code>, <code>hierarchy</code>, <code>materials</code>, <code>animations</code>
|
||||
* If you need to download textures, use class <code>TexturesLoader</code>.
|
||||
* <p>Path to collada file should match with <code>URI</code> specification. E.g., <code>file:///C:/test.dae</code> or <code>/C:/test.dae</code> for the full paths and <code>test.dae</code>, <code>./test.dae</code> in case of relative.</p>
|
||||
*
|
||||
* @param data <code>XML</code> data type of collada.
|
||||
* @param baseURL Path to textures relative to swf-file (Or file name only in case of <code>trimPaths=true</code>).
|
||||
* @param trimPaths Use file names only, without paths.
|
||||
*
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader
|
||||
* @see #objects
|
||||
* @see #hierarchy
|
||||
* @see #materials
|
||||
*/
|
||||
public function parse(data:XML, baseURL:String = null, trimPaths:Boolean = false):void {
|
||||
init();
|
||||
|
||||
var document:DaeDocument = new DaeDocument(data, 0);
|
||||
if (document.scene != null) {
|
||||
parseNodes(document.scene.nodes, null, false);
|
||||
parseMaterials(document.materials, baseURL, trimPaths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds components of animated object to lists objects, parents, hierarchy, cameras, animations and to parent container.
|
||||
*/
|
||||
private function addObject(animatedObject:DaeObject, parent:Object3D, layer:String):Object3D {
|
||||
var object:Object3D = Object3D(animatedObject.object);
|
||||
this.objects.push(object);
|
||||
if (parent == null) {
|
||||
this.hierarchy.push(object);
|
||||
} else {
|
||||
parent.addChild(object);
|
||||
}
|
||||
// if (object is Camera3D) {
|
||||
// this.cameras.push(object as Camera3D);
|
||||
// }
|
||||
if (object is Light3D) {
|
||||
lights.push(Light3D(object));
|
||||
}
|
||||
if (animatedObject.animation != null) {
|
||||
this.animations.push(animatedObject.animation);
|
||||
}
|
||||
if (layer) {
|
||||
layersMap[object] = layer;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds objects to objects, parents, hierarchy, cameras and animations lists and to parent container.
|
||||
*
|
||||
* @return first object
|
||||
*/
|
||||
private function addObjects(animatedObjects:Vector.<DaeObject>, parent:Object3D, layer:String):Object3D {
|
||||
var first:Object3D = addObject(animatedObjects[0], parent, layer);
|
||||
for (var i:int = 1, count:int = animatedObjects.length; i < count; i++) {
|
||||
addObject(animatedObjects[i], parent, layer);
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is there a skin among child objects.
|
||||
*/
|
||||
private function hasSkinsInChildren(node:DaeNode):Boolean {
|
||||
var nodes:Vector.<DaeNode> = node.nodes;
|
||||
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
|
||||
var child:DaeNode = nodes[i];
|
||||
child.parse();
|
||||
if (child.skins != null) {
|
||||
return true;
|
||||
}
|
||||
if (hasSkinsInChildren(child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function parseNodes(nodes:Vector.<DaeNode>, parent:Object3D, skinsOnly:Boolean = false):void {
|
||||
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
|
||||
var node:DaeNode = nodes[i];
|
||||
node.parse();
|
||||
|
||||
// Object to which child objects will be added.
|
||||
var container:Object3D = null;
|
||||
if (node.skins != null) {
|
||||
// Main joint of skin
|
||||
container = addObjects(node.skins, parent, node.layer);
|
||||
} else {
|
||||
if (!skinsOnly && !node.skinOrTopmostJoint) {
|
||||
if (node.objects != null) {
|
||||
container = addObjects(node.objects, parent, node.layer);
|
||||
} else {
|
||||
// Empty Object3D
|
||||
container = new Object3D();
|
||||
container.name = node.cloneString(node.name);
|
||||
addObject(node.applyAnimation(node.applyTransformations(container)), parent, node.layer);
|
||||
container.calculateBoundBox();
|
||||
}
|
||||
} else {
|
||||
// Object or its parent is a skin or joint
|
||||
// Create an object only if there are a child skins
|
||||
if (hasSkinsInChildren(node)) {
|
||||
container = new Object3D();
|
||||
container.name = node.cloneString(node.name);
|
||||
addObject(node.applyAnimation(node.applyTransformations(container)), parent, node.layer);
|
||||
parseNodes(node.nodes, container, skinsOnly || node.skinOrTopmostJoint);
|
||||
container.calculateBoundBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Parse children
|
||||
if (container != null) {
|
||||
parseNodes(node.nodes, container, skinsOnly || node.skinOrTopmostJoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function trimPath(path:String):String {
|
||||
var index:int = path.lastIndexOf("/");
|
||||
return (index < 0) ? path : path.substr(index + 1);
|
||||
}
|
||||
|
||||
private function parseMaterials(materials:Object, baseURL:String, trimPaths:Boolean):void {
|
||||
var tmaterial:ParserMaterial;
|
||||
for each (var material:DaeMaterial in materials) {
|
||||
if (material.used) {
|
||||
material.parse();
|
||||
this.materials.push(material.material);
|
||||
}
|
||||
}
|
||||
var resource:ExternalTextureResource;
|
||||
// Prepare paths
|
||||
if (trimPaths) {
|
||||
for each (tmaterial in this.materials) {
|
||||
for each(resource in tmaterial.textures) {
|
||||
if (resource != null && resource.url != null) {
|
||||
resource.url = trimPath(fixURL(resource.url));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for each (tmaterial in this.materials) {
|
||||
for each(resource in tmaterial.textures) {
|
||||
if (resource != null && resource.url != null) {
|
||||
resource.url = fixURL(resource.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var base:String;
|
||||
if (baseURL != null) {
|
||||
baseURL = fixURL(baseURL);
|
||||
var end:int = baseURL.lastIndexOf("/");
|
||||
base = (end < 0) ? "" : baseURL.substr(0, end);
|
||||
for each (tmaterial in this.materials) {
|
||||
for each(resource in tmaterial.textures) {
|
||||
if (resource != null && resource.url != null) {
|
||||
resource.url = resolveURL(resource.url, base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Prosesses URL with following actions.
|
||||
* Replaces backslashes with slashes, adds three direct slashes after <code>file:</code>
|
||||
*/
|
||||
private function fixURL(url:String):String {
|
||||
var pathStart:int = url.indexOf("://");
|
||||
pathStart = (pathStart < 0) ? 0 : pathStart + 3;
|
||||
var pathEnd:int = url.indexOf("?", pathStart);
|
||||
pathEnd = (pathEnd < 0) ? url.indexOf("#", pathStart) : pathEnd;
|
||||
var path:String = url.substring(pathStart, (pathEnd < 0) ? 0x7FFFFFFF : pathEnd);
|
||||
path = path.replace(/\\/g, "/");
|
||||
var fileIndex:int = url.indexOf("file://");
|
||||
if (fileIndex >= 0) {
|
||||
if (url.charAt(pathStart) == "/") {
|
||||
return "file://" + path + ((pathEnd >= 0) ? url.substring(pathEnd) : "");
|
||||
}
|
||||
return "file:///" + path + ((pathEnd >= 0) ? url.substring(pathEnd) : "");
|
||||
}
|
||||
return url.substring(0, pathStart) + path + ((pathEnd >= 0) ? url.substring(pathEnd) : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function mergePath(path:String, base:String, relative:Boolean = false):String {
|
||||
var baseParts:Array = base.split("/");
|
||||
var parts:Array = path.split("/");
|
||||
for (var i:int = 0, count:int = parts.length; i < count; i++) {
|
||||
var part:String = parts[i];
|
||||
if (part == "..") {
|
||||
var basePart:String = baseParts.pop();
|
||||
while (basePart == "." || basePart == "" && basePart != null) basePart = baseParts.pop();
|
||||
if (relative) {
|
||||
if (basePart == "..") {
|
||||
baseParts.push("..", "..");
|
||||
} else if (basePart == null) {
|
||||
baseParts.push("..");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
baseParts.push(part);
|
||||
}
|
||||
}
|
||||
return baseParts.join("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Converts relative paths to full paths
|
||||
*/
|
||||
private function resolveURL(url:String, base:String):String {
|
||||
if (base == "") {
|
||||
return url;
|
||||
}
|
||||
// http://labs.apache.org/webarch/uri/rfc/rfc3986.html
|
||||
if (url.charAt(0) == "." && url.charAt(1) == "/") {
|
||||
// File at the same folder
|
||||
return base + url.substr(1);
|
||||
} else if (url.charAt(0) == "/") {
|
||||
// Full path
|
||||
return url;
|
||||
} else if (url.charAt(0) == "." && url.charAt(1) == ".") {
|
||||
// Above on level
|
||||
var queryAndFragmentIndex:int = url.indexOf("?");
|
||||
queryAndFragmentIndex = (queryAndFragmentIndex < 0) ? url.indexOf("#") : queryAndFragmentIndex;
|
||||
var path:String;
|
||||
var queryAndFragment:String;
|
||||
if (queryAndFragmentIndex < 0) {
|
||||
queryAndFragment = "";
|
||||
path = url;
|
||||
} else {
|
||||
queryAndFragment = url.substring(queryAndFragmentIndex);
|
||||
path = url.substring(0, queryAndFragmentIndex);
|
||||
}
|
||||
// Split base URL on parts
|
||||
var bPath:String;
|
||||
var bSlashIndex:int = base.indexOf("/");
|
||||
var bShemeIndex:int = base.indexOf(":");
|
||||
var bAuthorityIndex:int = base.indexOf("//");
|
||||
if (bAuthorityIndex < 0 || bAuthorityIndex > bSlashIndex) {
|
||||
if (bShemeIndex >= 0 && bShemeIndex < bSlashIndex) {
|
||||
// Scheme exists, no domain
|
||||
var bSheme:String = base.substring(0, bShemeIndex + 1);
|
||||
bPath = base.substring(bShemeIndex + 1);
|
||||
if (bPath.charAt(0) == "/") {
|
||||
return bSheme + "/" + mergePath(path, bPath.substring(1), false) + queryAndFragment;
|
||||
} else {
|
||||
return bSheme + mergePath(path, bPath, false) + queryAndFragment;
|
||||
}
|
||||
} else {
|
||||
// No Scheme, no domain
|
||||
if (base.charAt(0) == "/") {
|
||||
return "/" + mergePath(path, base.substring(1), false) + queryAndFragment;
|
||||
} else {
|
||||
return mergePath(path, base, true) + queryAndFragment;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bSlashIndex = base.indexOf("/", bAuthorityIndex + 2);
|
||||
var bAuthority:String;
|
||||
if (bSlashIndex >= 0) {
|
||||
bAuthority = base.substring(0, bSlashIndex + 1);
|
||||
bPath = base.substring(bSlashIndex + 1);
|
||||
return bAuthority + mergePath(path, bPath, false) + queryAndFragment;
|
||||
} else {
|
||||
bAuthority = base;
|
||||
return bAuthority + "/" + mergePath(path, "", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
var shemeIndex:int = url.indexOf(":");
|
||||
var slashIndex:int = url.indexOf("/");
|
||||
if (shemeIndex >= 0 && (shemeIndex < slashIndex || slashIndex < 0)) {
|
||||
// Contains the schema
|
||||
return url;
|
||||
}
|
||||
return base + "/" + url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns animation from <code>animations</code> array by object, to which it refers.
|
||||
*/
|
||||
public function getAnimationByObject(object:Object):AnimationClip {
|
||||
for each (var animation:AnimationClip in animations) {
|
||||
var objects:Array = animation._objects;
|
||||
if (objects.indexOf(object) >= 0) {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns animation.
|
||||
*/
|
||||
public static function parseAnimation(data:XML):AnimationClip {
|
||||
|
||||
var document:DaeDocument = new DaeDocument(data, 0);
|
||||
var clip:AnimationClip = new AnimationClip();
|
||||
collectAnimation(clip, document.scene.nodes);
|
||||
return (clip.numTracks > 0) ? clip : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static function collectAnimation(clip:AnimationClip, nodes:Vector.<DaeNode>):void {
|
||||
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
|
||||
var node:DaeNode = nodes[i];
|
||||
var animation:AnimationClip = node.parseAnimation();
|
||||
if (animation != null) {
|
||||
for (var t:int = 0, numTracks:int = animation.numTracks; t < numTracks; t++) {
|
||||
var track:Track = animation.getTrackAt(t);
|
||||
clip.addTrack(track);
|
||||
}
|
||||
} else {
|
||||
clip.addTrack(node.createStaticTransformTrack());
|
||||
}
|
||||
collectAnimation(clip, node.nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/alternativa/engine3d/loaders/ParserMaterial.as
Normal file
107
src/alternativa/engine3d/loaders/ParserMaterial.as
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.materials.*;
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
import alternativa.engine3d.resources.TextureResource;
|
||||
|
||||
import avmplus.getQualifiedClassName;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getDefinitionByName;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* A material which is assigned to each object that we got as a parsing result. This material should be treated as a debugging rather than a production one.
|
||||
* It keeps links to all possible in Alternativa3D maps (such as light map or normal map) but can render only one of them as a diffuse like
|
||||
* <code>TextureMaterial</code>. To make object that you get after parsing using all these maps you should create a new <code>StandardMaterial</code>
|
||||
* and pass to them all textures. Then you can assign this material to the object.
|
||||
* Since <code>ParserMaterial</code> sores only links to textures, you should worry about loading it. You can use <code>TexturesLoader</code> for.
|
||||
* Can draws a Skin with no more than 41 Joints per surface. See Skin.divide() for more details.
|
||||
*
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader
|
||||
* @see alternativa.engine3d.materials
|
||||
* @see alternativa.engine3d.objects.Skin#divide()
|
||||
*/
|
||||
public class ParserMaterial extends Material {
|
||||
/**
|
||||
* List of colors, that can be assigned to each channel instead of texture. Variants: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump.
|
||||
*/
|
||||
public var colors:Object;
|
||||
/**
|
||||
* List of <code>ExternalTextureResource</code>, that you can load with a <code>TexturesLoader</code>. Keys of objects are names of channels. Variants: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump.
|
||||
*
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader
|
||||
* @see alternativa.engine3d.resources.ExternalTextureResource
|
||||
*/
|
||||
public var textures:Object;
|
||||
/**
|
||||
* Transparency of material
|
||||
*/
|
||||
public var transparency:Number = 0;
|
||||
/**
|
||||
* Channel, that will be rendered. Possible options: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump.
|
||||
*/
|
||||
public var renderChannel:String = "diffuse";
|
||||
|
||||
private var textureMaterial:TextureMaterial;
|
||||
private var fillMaterial:FillMaterial;
|
||||
|
||||
public function ParserMaterial() {
|
||||
textures = new Object();
|
||||
colors = new Object();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
|
||||
super.fillResources(resources, resourceType);
|
||||
for each(var texture:TextureResource in textures) {
|
||||
if (texture != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(texture)) as Class, resourceType)) {
|
||||
resources[texture] = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
|
||||
var colorO:Object = colors[renderChannel];
|
||||
var map:ExternalTextureResource;
|
||||
if (colorO != null) {
|
||||
if(fillMaterial == null) {
|
||||
fillMaterial = new FillMaterial(int(colorO));
|
||||
} else {
|
||||
fillMaterial.color = int(colorO);
|
||||
}
|
||||
fillMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, objectRenderPriority);
|
||||
} else if ((map = textures[renderChannel]) != null) {
|
||||
if(textureMaterial == null) {
|
||||
textureMaterial = new TextureMaterial(map);
|
||||
} else {
|
||||
textureMaterial.diffuseMap = map;
|
||||
}
|
||||
textureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, objectRenderPriority);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
186
src/alternativa/engine3d/loaders/ResourceLoader.as
Normal file
186
src/alternativa/engine3d/loaders/ResourceLoader.as
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.resources.BitmapTextureResource;
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Loader;
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DTextureFormat;
|
||||
import flash.display3D.textures.CubeTexture;
|
||||
import flash.display3D.textures.Texture;
|
||||
import flash.display3D.textures.TextureBase;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.net.URLRequest;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class ResourceLoader extends EventDispatcher {
|
||||
|
||||
private var loadSequence:Vector.<ExternalTextureResource> = new Vector.<ExternalTextureResource>();
|
||||
private var context:Context3D;
|
||||
private var currentIndex:int = 0;
|
||||
public var generateMips:Boolean = true;
|
||||
|
||||
private static const atfRegExp:RegExp = new RegExp(/\.atf/i);
|
||||
|
||||
public function ResourceLoader(generateMips:Boolean = true) {
|
||||
this.generateMips = generateMips;
|
||||
}
|
||||
|
||||
public function addResource(resource:ExternalTextureResource):void {
|
||||
loadSequence.push(resource);
|
||||
}
|
||||
|
||||
public function addResources(resources:Vector.<ExternalTextureResource>):void {
|
||||
for each (var resource:ExternalTextureResource in resources) {
|
||||
loadSequence.push(resource);
|
||||
}
|
||||
}
|
||||
|
||||
public function load(context:Context3D):void {
|
||||
this.context = context;
|
||||
currentIndex = 0;
|
||||
loadNext();
|
||||
}
|
||||
|
||||
private function loadNext():void {
|
||||
|
||||
if (currentIndex < loadSequence.length) {
|
||||
var currentResource:ExternalTextureResource = loadSequence[currentIndex];
|
||||
if (currentResource.url.match(atfRegExp)) {
|
||||
var atfLoader:URLLoader = new URLLoader();
|
||||
atfLoader.dataFormat = URLLoaderDataFormat.BINARY;
|
||||
atfLoader.addEventListener(Event.COMPLETE, onATFComplete);
|
||||
atfLoader.addEventListener(IOErrorEvent.IO_ERROR, onFailed);
|
||||
atfLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFailed);
|
||||
atfLoader.load(new URLRequest(currentResource.url));
|
||||
} else {
|
||||
var bitmapLoader:Loader = new Loader();
|
||||
bitmapLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onBitmapLoaded);
|
||||
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.DISK_ERROR, onFailed);
|
||||
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onFailed);
|
||||
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, onFailed);
|
||||
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, onFailed);
|
||||
bitmapLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFailed);
|
||||
bitmapLoader.load(new URLRequest(currentResource.url));
|
||||
}
|
||||
} else {
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
}
|
||||
|
||||
private function getNearPowerOf2For(size:Number):Number {
|
||||
if (size && (size - 1)) {
|
||||
for (var i:int = 11; i > 0; i--) {
|
||||
if (size >= (1 << i)) {
|
||||
return 1 << i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
private function fitTextureToSizeLimits(textureData:BitmapData):BitmapData {
|
||||
var fittedTextureData:BitmapData = textureData;
|
||||
var width:Number, height:Number;
|
||||
width = getNearPowerOf2For(fittedTextureData.width);
|
||||
height = getNearPowerOf2For(fittedTextureData.height);
|
||||
if (width != fittedTextureData.width || height != fittedTextureData.height) {
|
||||
var newBitmap:BitmapData = new BitmapData(width, height,
|
||||
fittedTextureData.transparent);
|
||||
var matrix:Matrix = new Matrix(width/fittedTextureData.width, 0, 0,
|
||||
height/fittedTextureData.height);
|
||||
newBitmap.draw(fittedTextureData, matrix);
|
||||
fittedTextureData = newBitmap;
|
||||
}
|
||||
return fittedTextureData;
|
||||
}
|
||||
|
||||
private function onBitmapLoaded(e:Event):void {
|
||||
var resized:BitmapData = fitTextureToSizeLimits(e.target.content.bitmapData);
|
||||
var texture:Texture = context.createTexture(resized.width, resized.height, Context3DTextureFormat.BGRA, false);
|
||||
texture.uploadFromBitmapData(resized, 0);
|
||||
if (generateMips) {
|
||||
BitmapTextureResource.createMips(texture, resized);
|
||||
}
|
||||
|
||||
var currentResource:ExternalTextureResource = loadSequence[currentIndex];
|
||||
currentResource.data = texture;
|
||||
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS));
|
||||
currentIndex++;
|
||||
loadNext();
|
||||
}
|
||||
|
||||
private function onFailed(e:Event):void {
|
||||
trace("Failed to load texture :", loadSequence[currentIndex].url);
|
||||
//dispatchEvent(e);
|
||||
currentIndex++;
|
||||
loadNext();
|
||||
}
|
||||
|
||||
private function onATFComplete(e:Event):void {
|
||||
var value:ByteArray = e.target.data;
|
||||
value.endian = Endian.LITTLE_ENDIAN;
|
||||
value.position = 6;
|
||||
var texture:TextureBase
|
||||
var type:uint = value.readByte();
|
||||
var format:String;
|
||||
switch (type & 0x7F) {
|
||||
case 0:
|
||||
format = Context3DTextureFormat.BGRA;
|
||||
break;
|
||||
case 1:
|
||||
format = Context3DTextureFormat.BGRA;
|
||||
break;
|
||||
case 2:
|
||||
format = Context3DTextureFormat.COMPRESSED;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((type & ~0x7F) == 0) {
|
||||
texture = context.createTexture(1 << value.readByte(), 1 << value.readByte(), format, false);
|
||||
texture.addEventListener("textureReady", onTextureUploaded);
|
||||
Texture(texture).uploadCompressedTextureFromByteArray(value, 0, true);
|
||||
} else {
|
||||
texture = context.createCubeTexture(1 << value.readByte(), format, false);
|
||||
texture.addEventListener("textureReady", onTextureUploaded);
|
||||
CubeTexture(texture).uploadCompressedTextureFromByteArray(value, 0, true)
|
||||
}
|
||||
}
|
||||
|
||||
private function onTextureUploaded(e:Event):void {
|
||||
var currentResource:ExternalTextureResource = loadSequence[currentIndex];
|
||||
currentResource.data = TextureBase(e.target);
|
||||
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS));
|
||||
currentIndex++;
|
||||
loadNext();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
328
src/alternativa/engine3d/loaders/TexturesLoader.as
Normal file
328
src/alternativa/engine3d/loaders/TexturesLoader.as
Normal file
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.loaders.events.TexturesLoaderEvent;
|
||||
import alternativa.engine3d.resources.BitmapTextureResource;
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Loader;
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DTextureFormat;
|
||||
import flash.display3D.textures.CubeTexture;
|
||||
import flash.display3D.textures.Texture;
|
||||
import flash.display3D.textures.TextureBase;
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.net.URLRequest;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Dispatches after complete loading of all textures.
|
||||
* @eventType flash.events.TexturesLoaderEvent.COMPLETE
|
||||
*/
|
||||
[Event(name="complete",type="alternativa.engine3d.loaders.events.TexturesLoaderEvent")]
|
||||
|
||||
/**
|
||||
* An object that downloads textures by their reference and upload them into the <code>Context3D</code>
|
||||
*/
|
||||
public class TexturesLoader extends EventDispatcher {
|
||||
|
||||
/**
|
||||
* A <code>Context3D</code> to which resources wil be loaded.
|
||||
*/
|
||||
public var context:Context3D;
|
||||
|
||||
private var textures:Object = new Object();
|
||||
private var bitmapDatas:Object = new Object();
|
||||
private var byteArrays:Object = new Object();
|
||||
|
||||
private var currentBitmapDatas:Vector.<BitmapData>;
|
||||
private var currentUrl:String;
|
||||
|
||||
private var resources:Vector.<ExternalTextureResource>;
|
||||
private var counter:int = 0;
|
||||
private var createTexture3D:Boolean;
|
||||
private var needBitmapData:Boolean;
|
||||
|
||||
private var loaderCompressed:URLLoader;
|
||||
private var isATF:Boolean;
|
||||
private var atfRegExp:RegExp = new RegExp(/\.atf/i);
|
||||
|
||||
/**
|
||||
* Creates a new TexturesLoader instance.
|
||||
* @param context – A <code>Context3D</code> to which resources wil be loaded.
|
||||
*/
|
||||
public function TexturesLoader(context:Context3D) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function getTexture(url:String):TextureBase {
|
||||
return textures[url];
|
||||
}
|
||||
|
||||
private function loadCompressed(url:String):void {
|
||||
loaderCompressed = new URLLoader();
|
||||
loaderCompressed.dataFormat = URLLoaderDataFormat.BINARY;
|
||||
loaderCompressed.addEventListener(Event.COMPLETE, loadNext);
|
||||
loaderCompressed.addEventListener(IOErrorEvent.IO_ERROR, loadNext);
|
||||
loaderCompressed.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext);
|
||||
loaderCompressed.load(new URLRequest(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a resource.
|
||||
* @param resource
|
||||
* @param createTexture3D Create texture on uploading
|
||||
* @param needBitmapData If <code>true</code>, keeps <code>BitmapData</code> after uploading textures into a context.
|
||||
*/
|
||||
public function loadResource(resource:ExternalTextureResource, createTexture3D:Boolean = true, needBitmapData:Boolean = true):void {
|
||||
if (resources != null) {
|
||||
throw new Error("Cannot start new load while loading");
|
||||
}
|
||||
this.createTexture3D = createTexture3D;
|
||||
this.needBitmapData = needBitmapData;
|
||||
resources = Vector.<ExternalTextureResource>([resource]);
|
||||
currentBitmapDatas = new Vector.<BitmapData>(1);
|
||||
//currentTextures3D = new Vector.<Texture>(1);
|
||||
loadNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads list of textures
|
||||
* @param resources List of <code>ExternalTextureResource</code> each of them has link to texture file which needs to be downloaded.
|
||||
* @param createTexture3D Create texture on uploading.
|
||||
* @param needBitmapData If <code>true</code>, keeps <code>BitmapData</code> after uploading textures into a context.
|
||||
*/
|
||||
public function loadResources(resources:Vector.<ExternalTextureResource>, createTexture3D:Boolean = true, needBitmapData:Boolean = true):void {
|
||||
if (this.resources != null) {
|
||||
throw new Error("Cannot start new load while loading");
|
||||
}
|
||||
this.createTexture3D = createTexture3D;
|
||||
this.needBitmapData = needBitmapData;
|
||||
this.resources = resources;
|
||||
currentBitmapDatas = new Vector.<BitmapData>(resources.length);
|
||||
loadNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears links to all data stored in this <code>TexturesLoader</code> instance. (List of downloaded textures)
|
||||
*/
|
||||
public function clean():void {
|
||||
if (resources != null) {
|
||||
throw new Error("Cannot clean while loading");
|
||||
}
|
||||
textures = new Object();
|
||||
bitmapDatas = new Object();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears links to all data stored in this <code>TexturesLoader</code> instance and removes it from the context.
|
||||
*/
|
||||
public function cleanAndDispose():void {
|
||||
if (resources != null) {
|
||||
throw new Error("Cannot clean while loading");
|
||||
}
|
||||
textures = new Object();
|
||||
for each (var b:BitmapData in bitmapDatas) {
|
||||
b.dispose();
|
||||
}
|
||||
bitmapDatas = new Object();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes texture resources from <code>Context3D</code>.
|
||||
* @param urls List of links to resources, that should be removed.
|
||||
*/
|
||||
public function dispose(urls:Vector.<String>):void {
|
||||
for (var i:int = 0; i < urls.length; i++) {
|
||||
var url:String = urls[i];
|
||||
var bmd:BitmapData = bitmapDatas[url] as BitmapData;
|
||||
//if (bmd) {
|
||||
delete bitmapDatas[url];
|
||||
bmd.dispose();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private function loadNext(e:Event = null):void {
|
||||
// trace("[NEXT]", e);
|
||||
var bitmapData:BitmapData;
|
||||
var byteArray:ByteArray;
|
||||
var texture3D:TextureBase;
|
||||
|
||||
if (e != null && !(e is ErrorEvent)) {
|
||||
if (isATF) {
|
||||
byteArray = e.target.data;
|
||||
byteArrays[currentUrl] = byteArray;
|
||||
try {
|
||||
texture3D = addCompressedTexture(byteArray);
|
||||
resources[counter - 1].data = texture3D;
|
||||
} catch (err:Error) {
|
||||
// throw new Error("loadNext:: " + err.message + " " + currentUrl);
|
||||
trace("loadNext:: " + err.message + " " + currentUrl);
|
||||
}
|
||||
} else {
|
||||
bitmapData = e.target.content.bitmapData;
|
||||
bitmapDatas[currentUrl] = bitmapData;
|
||||
currentBitmapDatas[counter - 1] = bitmapData;
|
||||
if (createTexture3D) {
|
||||
try {
|
||||
texture3D = addTexture(bitmapData);
|
||||
resources[counter - 1].data = texture3D;
|
||||
} catch (err:Error) {
|
||||
throw new Error("loadNext:: " + err.message + " " + currentUrl);
|
||||
}
|
||||
}
|
||||
if (!needBitmapData) {
|
||||
bitmapData.dispose();
|
||||
}
|
||||
}
|
||||
resources[counter - 1].data = texture3D;
|
||||
} else if (e is ErrorEvent) {
|
||||
trace("Missing: " + currentUrl);
|
||||
}
|
||||
|
||||
if (counter < resources.length) {
|
||||
currentUrl = resources[counter++].url;
|
||||
if (currentUrl != null) {
|
||||
atfRegExp.lastIndex = currentUrl.lastIndexOf(".");
|
||||
isATF = currentUrl.match(atfRegExp) != null;
|
||||
}
|
||||
|
||||
if (isATF) {
|
||||
if (createTexture3D) {
|
||||
texture3D = textures[currentUrl];
|
||||
if (texture3D == null) {
|
||||
byteArray = byteArrays[currentUrl];
|
||||
if (byteArray) {
|
||||
texture3D = addCompressedTexture(byteArray);
|
||||
resources[counter - 1].data = texture3D;
|
||||
//bitmapDatas[currentUrl] = bitmapData;
|
||||
loadNext();
|
||||
} else {
|
||||
loadCompressed(currentUrl);
|
||||
}
|
||||
} else {
|
||||
resources[counter - 1].data = texture3D;
|
||||
loadNext();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (needBitmapData) {
|
||||
bitmapData = bitmapDatas[currentUrl];
|
||||
if (bitmapData) {
|
||||
currentBitmapDatas[counter - 1] = bitmapData;
|
||||
if (createTexture3D) {
|
||||
texture3D = textures[currentUrl];
|
||||
if (texture3D == null) {
|
||||
texture3D = addTexture(bitmapData);
|
||||
}
|
||||
resources[counter - 1].data = texture3D;
|
||||
}
|
||||
loadNext();
|
||||
} else {
|
||||
load(currentUrl);
|
||||
}
|
||||
} else if (createTexture3D) {
|
||||
texture3D = textures[currentUrl];
|
||||
if (texture3D == null) {
|
||||
bitmapData = bitmapDatas[currentUrl];
|
||||
if (bitmapData) {
|
||||
texture3D = addTexture(bitmapData);
|
||||
resources[counter - 1].data = texture3D;
|
||||
loadNext();
|
||||
} else {
|
||||
load(currentUrl);
|
||||
}
|
||||
} else {
|
||||
resources[counter - 1].data = texture3D;
|
||||
loadNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onTexturesLoad();
|
||||
}
|
||||
}
|
||||
|
||||
private function load(url:String):void {
|
||||
var loader:Loader = new Loader();
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNext);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.DISK_ERROR, loadNext);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNext);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNext);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNext);
|
||||
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext);
|
||||
loader.load(new URLRequest(url));
|
||||
}
|
||||
|
||||
private function onTexturesLoad():void {
|
||||
// trace("[LOADED]");
|
||||
counter = 0;
|
||||
var bmds:Vector.<BitmapData> = currentBitmapDatas;
|
||||
var reses:Vector.<ExternalTextureResource> = resources;
|
||||
currentBitmapDatas = null;
|
||||
resources = null;
|
||||
dispatchEvent(new TexturesLoaderEvent(TexturesLoaderEvent.COMPLETE, bmds, reses));
|
||||
}
|
||||
|
||||
private function addTexture(value:BitmapData):Texture {
|
||||
var texture:Texture = context.createTexture(value.width, value.height, Context3DTextureFormat.BGRA, false);
|
||||
texture.uploadFromBitmapData(value, 0);
|
||||
BitmapTextureResource.createMips(texture, value);
|
||||
textures[currentUrl] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
private function addCompressedTexture(value:ByteArray):TextureBase {
|
||||
value.endian = Endian.LITTLE_ENDIAN;
|
||||
value.position = 6;
|
||||
var texture:TextureBase
|
||||
var type:uint = value.readByte();
|
||||
var format:String;
|
||||
switch (type & 0x7F) {
|
||||
case 0:
|
||||
format = Context3DTextureFormat.BGRA;
|
||||
break;
|
||||
case 1:
|
||||
format = Context3DTextureFormat.BGRA;
|
||||
break;
|
||||
case 2:
|
||||
format = Context3DTextureFormat.COMPRESSED;
|
||||
break;
|
||||
}
|
||||
if ((type & ~0x7F) == 0) {
|
||||
texture = context.createTexture(1 << value.readByte(), 1 << value.readByte(), format, false);
|
||||
Texture(texture).uploadCompressedTextureFromByteArray(value, 0);
|
||||
} else {
|
||||
texture = context.createCubeTexture(1 << value.readByte(), format, false);
|
||||
CubeTexture(texture).uploadCompressedTextureFromByteArray(value, 0)
|
||||
}
|
||||
textures[currentUrl] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
51
src/alternativa/engine3d/loaders/collada/DaeArray.as
Normal file
51
src/alternativa/engine3d/loaders/collada/DaeArray.as
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeArray extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
/**
|
||||
* Array of String values.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var array:Array;
|
||||
|
||||
public function DaeArray(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
public function get type():String {
|
||||
return String(data.localName());
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
array = parseStringArray(data);
|
||||
var countXML:XML = data.@count[0];
|
||||
if (countXML != null) {
|
||||
var count:int = parseInt(countXML.toString(), 10);
|
||||
if (array.length < count) {
|
||||
document.logger.logNotEnoughDataError(data.@count[0]);
|
||||
return false;
|
||||
} else {
|
||||
array.length = count;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
213
src/alternativa/engine3d/loaders/collada/DaeChannel.as
Normal file
213
src/alternativa/engine3d/loaders/collada/DaeChannel.as
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.animation.keys.NumberKey;
|
||||
import alternativa.engine3d.animation.keys.NumberTrack;
|
||||
import alternativa.engine3d.animation.keys.Track;
|
||||
|
||||
use namespace alternativa3d;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeChannel extends DaeElement {
|
||||
|
||||
static public const PARAM_UNDEFINED:String = "undefined";
|
||||
static public const PARAM_TRANSLATE_X:String = "x";
|
||||
static public const PARAM_TRANSLATE_Y:String = "y";
|
||||
static public const PARAM_TRANSLATE_Z:String = "z";
|
||||
static public const PARAM_SCALE_X:String = "scaleX";
|
||||
static public const PARAM_SCALE_Y:String = "scaleY";
|
||||
static public const PARAM_SCALE_Z:String = "scaleZ";
|
||||
static public const PARAM_ROTATION_X:String = "rotationX";
|
||||
static public const PARAM_ROTATION_Y:String = "rotationY";
|
||||
static public const PARAM_ROTATION_Z:String = "rotationZ";
|
||||
static public const PARAM_TRANSLATE:String = "translate";
|
||||
static public const PARAM_SCALE:String = "scale";
|
||||
static public const PARAM_MATRIX:String = "matrix";
|
||||
|
||||
/**
|
||||
* Animation track with keys.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var tracks:Vector.<Track>;
|
||||
|
||||
/**
|
||||
* Type of animated parameter. It can be one of DaeChannel.PARAM_*. values.
|
||||
* * Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var animatedParam:String = PARAM_UNDEFINED;
|
||||
/**
|
||||
* Key of animated object.
|
||||
*/
|
||||
public var animName:String;
|
||||
|
||||
public function DaeChannel(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a node for which the animation is destined.
|
||||
*/
|
||||
public function get node():DaeNode {
|
||||
var targetXML:XML = data.@target[0];
|
||||
if (targetXML != null) {
|
||||
var targetParts:Array = targetXML.toString().split("/");
|
||||
// First part of item id
|
||||
var node:DaeNode = document.findNodeByID(targetParts[0]);
|
||||
if (node != null) {
|
||||
// Last part is transformed item
|
||||
targetParts.pop();
|
||||
for (var i:int = 1, count:int = targetParts.length; i < count; i++) {
|
||||
var sid:String = targetParts[i];
|
||||
node = node.getNodeBySid(sid);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
parseTransformationType();
|
||||
parseSampler();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function parseTransformationType():void {
|
||||
var targetXML:XML = data.@target[0];
|
||||
if (targetXML == null) return;
|
||||
|
||||
// Split the path on parts
|
||||
var targetParts:Array = targetXML.toString().split("/");
|
||||
var sid:String = targetParts.pop();
|
||||
var sidParts:Array = sid.split(".");
|
||||
var sidPartsCount:int = sidParts.length;
|
||||
|
||||
//Define the type of property
|
||||
var transformationXML:XML;
|
||||
var node:DaeNode = this.node;
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
animName = node.animName;
|
||||
var children:XMLList = node.data.children();
|
||||
for (var i:int = 0, count:int = children.length(); i < count; i++) {
|
||||
var child:XML = children[i];
|
||||
var attr:XML = child.@sid[0];
|
||||
if (attr != null && attr.toString() == sidParts[0]) {
|
||||
transformationXML = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO:: case with brackets (just in case)
|
||||
var transformationName:String = (transformationXML != null) ? transformationXML.localName() as String : null;
|
||||
if (sidPartsCount > 1) {
|
||||
var componentName:String = sidParts[1];
|
||||
switch (transformationName) {
|
||||
case "translate":
|
||||
switch (componentName) {
|
||||
case "X":
|
||||
animatedParam = PARAM_TRANSLATE_X;
|
||||
break;
|
||||
case "Y":
|
||||
animatedParam = PARAM_TRANSLATE_Y;
|
||||
break;
|
||||
case "Z":
|
||||
animatedParam = PARAM_TRANSLATE_Z;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "rotate": {
|
||||
var axis:Array = parseNumbersArray(transformationXML);
|
||||
// TODO:: look for the maximum value
|
||||
switch (axis.indexOf(1)) {
|
||||
case 0:
|
||||
animatedParam = PARAM_ROTATION_X;
|
||||
break;
|
||||
case 1:
|
||||
animatedParam = PARAM_ROTATION_Y;
|
||||
break;
|
||||
case 2:
|
||||
animatedParam = PARAM_ROTATION_Z;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "scale":
|
||||
switch (componentName) {
|
||||
case "X":
|
||||
animatedParam = PARAM_SCALE_X;
|
||||
break;
|
||||
case "Y":
|
||||
animatedParam = PARAM_SCALE_Y;
|
||||
break;
|
||||
case "Z":
|
||||
animatedParam = PARAM_SCALE_Z;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (transformationName) {
|
||||
case "translate":
|
||||
animatedParam = PARAM_TRANSLATE;
|
||||
break;
|
||||
case "scale":
|
||||
animatedParam = PARAM_SCALE;
|
||||
break;
|
||||
case "matrix":
|
||||
animatedParam = PARAM_MATRIX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSampler():void {
|
||||
var sampler:DaeSampler = document.findSampler(data.@source[0]);
|
||||
if (sampler != null) {
|
||||
sampler.parse();
|
||||
if (animatedParam == PARAM_MATRIX) {
|
||||
tracks = Vector.<Track>([sampler.parseTransformationTrack(animName)]);
|
||||
return;
|
||||
}
|
||||
if (animatedParam == PARAM_TRANSLATE) {
|
||||
tracks = sampler.parsePointsTracks(animName, "x", "y", "z");
|
||||
return;
|
||||
}
|
||||
if (animatedParam == PARAM_SCALE) {
|
||||
tracks = sampler.parsePointsTracks(animName, "scaleX", "scaleY", "scaleZ");
|
||||
return;
|
||||
}
|
||||
if (animatedParam == PARAM_ROTATION_X || animatedParam == PARAM_ROTATION_Y || animatedParam == PARAM_ROTATION_Z) {
|
||||
var track:NumberTrack = sampler.parseNumbersTrack(animName, animatedParam);
|
||||
// Convert degrees to radians
|
||||
var toRad:Number = Math.PI/180;
|
||||
for (var key:NumberKey = track.keyList; key != null; key = key.next) {
|
||||
key._value *= toRad;
|
||||
}
|
||||
tracks = Vector.<Track>([track]);
|
||||
return;
|
||||
}
|
||||
if (animatedParam == PARAM_TRANSLATE_X || animatedParam == PARAM_TRANSLATE_Y || animatedParam == PARAM_TRANSLATE_Z || animatedParam == PARAM_SCALE_X || animatedParam == PARAM_SCALE_Y || animatedParam == PARAM_SCALE_Z) {
|
||||
tracks = Vector.<Track>([sampler.parseNumbersTrack(animName, animatedParam)]);
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(data.@source[0]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
542
src/alternativa/engine3d/loaders/collada/DaeController.as
Normal file
542
src/alternativa/engine3d/loaders/collada/DaeController.as
Normal file
@@ -0,0 +1,542 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.animation.AnimationClip;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.VertexAttributes;
|
||||
import alternativa.engine3d.loaders.ParserMaterial;
|
||||
import alternativa.engine3d.objects.Joint;
|
||||
import alternativa.engine3d.objects.Skin;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace collada;
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeController extends DaeElement {
|
||||
|
||||
private var jointsBindMatrices:Vector.<Vector.<Number> >;
|
||||
private var vcounts:Array;
|
||||
private var indices:Array;
|
||||
private var jointsInput:DaeInput;
|
||||
private var weightsInput:DaeInput;
|
||||
private var inputsStride:int;
|
||||
private var geometry:Geometry;
|
||||
private var primitives:Vector.<DaePrimitive>;
|
||||
private var maxJointsPerVertex:int = 0;
|
||||
private var bindShapeMatrix:Vector.<Number>;
|
||||
|
||||
public function DaeController(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
|
||||
// sources creates inside the DaeDocument. It should not be here.
|
||||
}
|
||||
|
||||
private function get daeGeometry():DaeGeometry {
|
||||
var geom:DaeGeometry = document.findGeometry(data.skin.@source[0]);
|
||||
if (geom == null) {
|
||||
document.logger.logNotFoundError(data.@source[0]);
|
||||
}
|
||||
return geom;
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
var vertexWeightsXML:XML = this.data.skin.vertex_weights[0];
|
||||
if (vertexWeightsXML == null) {
|
||||
return false;
|
||||
}
|
||||
var vcountsXML:XML = vertexWeightsXML.vcount[0];
|
||||
if (vcountsXML == null) {
|
||||
return false;
|
||||
}
|
||||
vcounts = parseIntsArray(vcountsXML);
|
||||
var indicesXML:XML = vertexWeightsXML.v[0];
|
||||
if (indicesXML == null) {
|
||||
return false;
|
||||
}
|
||||
indices = parseIntsArray(indicesXML);
|
||||
parseInputs();
|
||||
parseJointsBindMatrices();
|
||||
var i:int, j:int;
|
||||
for (i = 0; i < vcounts.length; i++) {
|
||||
var count:int = vcounts[i];
|
||||
if (maxJointsPerVertex < count) maxJointsPerVertex = count;
|
||||
}
|
||||
|
||||
var geom:DaeGeometry = this.daeGeometry;
|
||||
bindShapeMatrix = getBindShapeMatrix();
|
||||
if (geom != null) {
|
||||
geom.parse();
|
||||
var vertices:Vector.<DaeVertex> = geom.geometryVertices;
|
||||
var source:Geometry = geom.geometry;
|
||||
var localMaxJointsPerVertex:int = (maxJointsPerVertex%2 != 0) ? maxJointsPerVertex + 1 : maxJointsPerVertex;
|
||||
|
||||
// Create geometry
|
||||
this.geometry = new Geometry();
|
||||
this.geometry._indices = source._indices.slice();
|
||||
var attributes:Array = source.getVertexStreamAttributes(0);
|
||||
var numSourceAttributes:int = attributes.length;
|
||||
|
||||
var index:int = numSourceAttributes;
|
||||
for (i = 0; i < localMaxJointsPerVertex; i += 2) {
|
||||
var attribute:int = VertexAttributes.JOINTS[int(i/2)];
|
||||
attributes[int(index++)] = attribute;
|
||||
attributes[int(index++)] = attribute;
|
||||
attributes[int(index++)] = attribute;
|
||||
attributes[int(index++)] = attribute;
|
||||
}
|
||||
|
||||
var numMappings:int = attributes.length;
|
||||
|
||||
var sourceData:ByteArray = source._vertexStreams[0].data;
|
||||
var data:ByteArray = new ByteArray();
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
data.length = 4*numMappings*source._numVertices;
|
||||
// Copy source data
|
||||
sourceData.position = 0;
|
||||
for (i = 0; i < source._numVertices; i++) {
|
||||
data.position = 4*numMappings*i;
|
||||
for (j = 0; j < numSourceAttributes; j++) {
|
||||
data.writeFloat(sourceData.readFloat());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy weights and joints
|
||||
var byteArray:ByteArray = createVertexBuffer(vertices, localMaxJointsPerVertex);
|
||||
byteArray.position = 0;
|
||||
for (i = 0; i < source._numVertices; i++) {
|
||||
data.position = 4*(numMappings*i + numSourceAttributes);
|
||||
for (j = 0; j < localMaxJointsPerVertex; j++) {
|
||||
data.writeFloat(byteArray.readFloat());
|
||||
data.writeFloat(byteArray.readFloat());
|
||||
}
|
||||
}
|
||||
|
||||
this.geometry.addVertexStream(attributes);
|
||||
this.geometry._vertexStreams[0].data = data;
|
||||
this.geometry._numVertices = source._numVertices;
|
||||
transformVertices(this.geometry);
|
||||
primitives = geom.primitives;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function transformVertices(geometry:Geometry):void {
|
||||
var data:ByteArray = geometry._vertexStreams[0].data;
|
||||
var numMappings:int = geometry._vertexStreams[0].attributes.length;
|
||||
for (var i:int = 0; i < geometry._numVertices; i++) {
|
||||
data.position = 4*numMappings*i;
|
||||
var x:Number = data.readFloat();
|
||||
var y:Number = data.readFloat();
|
||||
var z:Number = data.readFloat();
|
||||
data.position -= 12;
|
||||
data.writeFloat(x*bindShapeMatrix[0] + y*bindShapeMatrix[1] + z*bindShapeMatrix[2] + bindShapeMatrix[3]);
|
||||
data.writeFloat(x*bindShapeMatrix[4] + y*bindShapeMatrix[5] + z*bindShapeMatrix[6] + bindShapeMatrix[7]);
|
||||
data.writeFloat(x*bindShapeMatrix[8] + y*bindShapeMatrix[9] + z*bindShapeMatrix[10] + bindShapeMatrix[11]);
|
||||
}
|
||||
}
|
||||
|
||||
private function createVertexBuffer(vertices:Vector.<DaeVertex>, localMaxJointsPerVertex:int):ByteArray {
|
||||
var jointsOffset:int = jointsInput.offset;
|
||||
var weightsOffset:int = weightsInput.offset;
|
||||
var weightsSource:DaeSource = weightsInput.prepareSource(1);
|
||||
var weights:Vector.<Number> = weightsSource.numbers;
|
||||
var weightsStride:int = weightsSource.stride;
|
||||
var i:int, count:int;
|
||||
var verticesDict:Dictionary = new Dictionary();
|
||||
var byteArray:ByteArray = new ByteArray();
|
||||
// Reserve required number of bytes
|
||||
byteArray.length = vertices.length*localMaxJointsPerVertex*8;
|
||||
byteArray.endian = Endian.LITTLE_ENDIAN;
|
||||
|
||||
for (i = 0,count = vertices.length; i < count; i++) {
|
||||
var vertex:DaeVertex = vertices[i];
|
||||
if (vertex == null) continue;
|
||||
var vec:Vector.<uint> = verticesDict[vertex.vertexInIndex];
|
||||
if (vec == null) {
|
||||
vec = verticesDict[vertex.vertexInIndex] = new Vector.<uint>();
|
||||
}
|
||||
vec.push(vertex.vertexOutIndex);
|
||||
}
|
||||
|
||||
var vertexIndex:int = 0;
|
||||
var vertexOutIndices:Vector.<uint>;
|
||||
for (i = 0,count = vcounts.length; i < count; i++) {
|
||||
var jointsPerVertex:int = vcounts[i];
|
||||
vertexOutIndices = verticesDict[i];
|
||||
for (var j:int = 0; j < vertexOutIndices.length; j++) {
|
||||
byteArray.position = vertexOutIndices[j]*localMaxJointsPerVertex*8;
|
||||
// Loop on joints
|
||||
for (var k:int = 0; k < jointsPerVertex; k++) {
|
||||
var index:int = inputsStride*(vertexIndex + k);
|
||||
var jointIndex:int = indices[int(index + jointsOffset)];
|
||||
if (jointIndex >= 0) {
|
||||
// Save joint index, multiplied with three
|
||||
byteArray.writeFloat(jointIndex*3);
|
||||
var weightIndex:int = indices[int(index + weightsOffset)];
|
||||
byteArray.writeFloat(weights[int(weightsStride*weightIndex)]);
|
||||
} else {
|
||||
byteArray.position += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
vertexIndex += jointsPerVertex;
|
||||
}
|
||||
byteArray.position = 0;
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
private function parseInputs():void {
|
||||
var inputsList:XMLList = data.skin.vertex_weights.input;
|
||||
var maxInputOffset:int = 0;
|
||||
for (var i:int = 0, count:int = inputsList.length(); i < count; i++) {
|
||||
var input:DaeInput = new DaeInput(inputsList[i], document);
|
||||
var semantic:String = input.semantic;
|
||||
if (semantic != null) {
|
||||
switch (semantic) {
|
||||
case "JOINT" :
|
||||
if (jointsInput == null) {
|
||||
jointsInput = input;
|
||||
}
|
||||
break;
|
||||
case "WEIGHT" :
|
||||
if (weightsInput == null) {
|
||||
weightsInput = input;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
var offset:int = input.offset;
|
||||
maxInputOffset = (offset > maxInputOffset) ? offset : maxInputOffset;
|
||||
}
|
||||
inputsStride = maxInputOffset + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses inverse matrices for joints and saves them to a vector.
|
||||
*/
|
||||
private function parseJointsBindMatrices():void {
|
||||
var jointsXML:XML = data.skin.joints.input.(@semantic == "INV_BIND_MATRIX")[0];
|
||||
if (jointsXML != null) {
|
||||
var jointsSource:DaeSource = document.findSource(jointsXML.@source[0]);
|
||||
if (jointsSource != null) {
|
||||
if (jointsSource.parse() && jointsSource.numbers != null && jointsSource.stride >= 16) {
|
||||
var stride:int = jointsSource.stride;
|
||||
var count:int = jointsSource.numbers.length/stride;
|
||||
jointsBindMatrices = new Vector.<Vector.<Number> >(count);
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var index:int = stride*i;
|
||||
var matrix:Vector.<Number> = new Vector.<Number>(16);
|
||||
jointsBindMatrices[i] = matrix;
|
||||
for (var j:int = 0; j < 16; j++) {
|
||||
matrix[j] = jointsSource.numbers[int(index + j)];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(jointsXML.@source[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns geometry with the joints and controller for the joints.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public function parseSkin(materials:Object, topmostJoints:Vector.<DaeNode>, skeletons:Vector.<DaeNode>):DaeObject {
|
||||
var skinXML:XML = data.skin[0];
|
||||
if (skinXML != null) {
|
||||
bindShapeMatrix = getBindShapeMatrix();
|
||||
var numJoints:int = jointsBindMatrices.length;
|
||||
var skin:Skin = new Skin(maxJointsPerVertex);
|
||||
skin.geometry = this.geometry;
|
||||
var joints:Vector.<DaeObject> = addJointsToSkin(skin, topmostJoints, findNodes(skeletons));
|
||||
setJointsBindMatrices(joints);
|
||||
|
||||
skin.renderedJoints = collectRenderedJoints(joints, numJoints);
|
||||
|
||||
if (primitives != null) {
|
||||
for (var i:int = 0; i < primitives.length; i++) {
|
||||
var p:DaePrimitive = primitives[i];
|
||||
var instanceMaterial:DaeInstanceMaterial = materials[p.materialSymbol];
|
||||
var material:ParserMaterial;
|
||||
if (instanceMaterial != null) {
|
||||
var daeMaterial:DaeMaterial = instanceMaterial.material;
|
||||
if (daeMaterial != null) {
|
||||
daeMaterial.parse();
|
||||
material = daeMaterial.material;
|
||||
daeMaterial.used = true;
|
||||
}
|
||||
}
|
||||
skin.addSurface(material, p.indexBegin, p.numTriangles);
|
||||
}
|
||||
}
|
||||
skin.calculateBoundBox();
|
||||
return new DaeObject(skin, mergeJointsClips(skin, joints));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function collectRenderedJoints(joints:Vector.<DaeObject>, numJoints:int):Vector.<Joint> {
|
||||
var result:Vector.<Joint> = new Vector.<Joint>();
|
||||
for (var i:int = 0; i < numJoints; i++) {
|
||||
result[i] = Joint(joints[i].object);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unites animations of joints into the single animation, if required.
|
||||
*/
|
||||
private function mergeJointsClips(skin:Skin, joints:Vector.<DaeObject>):AnimationClip {
|
||||
if (!hasJointsAnimation(joints)) {
|
||||
return null;
|
||||
}
|
||||
var result:AnimationClip = new AnimationClip();
|
||||
var resultObjects:Array = [skin];
|
||||
for (var i:int = 0, count:int = joints.length; i < count; i++) {
|
||||
var animatedObject:DaeObject = joints[i];
|
||||
var clip:AnimationClip = animatedObject.animation;
|
||||
if (clip != null) {
|
||||
for (var t:int = 0; t < clip.numTracks; t++) {
|
||||
result.addTrack(clip.getTrackAt(t));
|
||||
}
|
||||
} else {
|
||||
// Creates empty track for the joint.
|
||||
result.addTrack(animatedObject.jointNode.createStaticTransformTrack());
|
||||
}
|
||||
var object:Object3D = animatedObject.object;
|
||||
object.name = animatedObject.jointNode.animName;
|
||||
resultObjects.push(object);
|
||||
}
|
||||
result._objects = resultObjects;
|
||||
return result;
|
||||
}
|
||||
|
||||
private function hasJointsAnimation(joints:Vector.<DaeObject>):Boolean {
|
||||
for (var i:int = 0, count:int = joints.length; i < count; i++) {
|
||||
var object:DaeObject = joints[i];
|
||||
if (object.animation != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set inverse matrices to joints.
|
||||
*/
|
||||
private function setJointsBindMatrices(animatedJoints:Vector.<DaeObject>):void {
|
||||
for (var i:int = 0, count:int = jointsBindMatrices.length; i < count; i++) {
|
||||
var animatedJoint:DaeObject = animatedJoints[i];
|
||||
var bindMatrix:Vector.<Number> = jointsBindMatrices[i];
|
||||
// bindMatrix[3]; //*= document.unitScaleFactor;
|
||||
// bindMatrix[7];// *= document.unitScaleFactor;
|
||||
// bindMatrix[11];// *= document.unitScaleFactor;
|
||||
Joint(animatedJoint.object).setBindPoseMatrix(bindMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hierarchy of joints and adds them to skin.
|
||||
* @return vector of joints with animation, which was added to skin.
|
||||
* If you have added the auxiliary joint, then length of vector will differ from length of nodes vector.
|
||||
*/
|
||||
private function addJointsToSkin(skin:Skin, topmostJoints:Vector.<DaeNode>, nodes:Vector.<DaeNode>):Vector.<DaeObject> {
|
||||
// Dictionary, in which key is a node and value is a position in nodes vector
|
||||
var nodesDictionary:Dictionary = new Dictionary();
|
||||
var count:int = nodes.length;
|
||||
var i:int;
|
||||
for (i = 0; i < count; i++) {
|
||||
nodesDictionary[nodes[i]] = i;
|
||||
}
|
||||
var animatedJoints:Vector.<DaeObject> = new Vector.<DaeObject>(count);
|
||||
var numTopmostJoints:int = topmostJoints.length;
|
||||
for (i = 0; i < numTopmostJoints; i++) {
|
||||
var topmostJoint:DaeNode = topmostJoints[i];
|
||||
var animatedJoint:DaeObject = addRootJointToSkin(skin, topmostJoint, animatedJoints, nodesDictionary);
|
||||
addJointChildren(Joint(animatedJoint.object), animatedJoints, topmostJoint, nodesDictionary);
|
||||
}
|
||||
return animatedJoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds root joint to skin.
|
||||
*/
|
||||
private function addRootJointToSkin(skin:Skin, node:DaeNode, animatedJoints:Vector.<DaeObject>, nodes:Dictionary):DaeObject {
|
||||
var joint:Joint = new Joint();
|
||||
joint.name = cloneString(node.name);
|
||||
skin.addChild(joint);
|
||||
var animatedJoint:DaeObject = node.applyAnimation(node.applyTransformations(joint));
|
||||
animatedJoint.jointNode = node;
|
||||
if (node in nodes) {
|
||||
animatedJoints[nodes[node]] = animatedJoint;
|
||||
} else {
|
||||
// Add at the end
|
||||
animatedJoints.push(animatedJoint);
|
||||
}
|
||||
return animatedJoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hierarchy of child joints and add them to parent joint.
|
||||
* @param parent Parent joint.
|
||||
* @param animatedJoints Vector of joints to which created joints will added.
|
||||
* Auxiliary joints will be added to the end of the vector, if it's necessary.
|
||||
* @param parentNode Node of parent joint
|
||||
* @param nodes Dictionary. Key is a node of joint. And value is an index of joint in animatedJoints vector
|
||||
*
|
||||
*/
|
||||
private function addJointChildren(parent:Joint, animatedJoints:Vector.<DaeObject>, parentNode:DaeNode, nodes:Dictionary):void {
|
||||
var object:DaeObject;
|
||||
var children:Vector.<DaeNode> = parentNode.nodes;
|
||||
for (var i:int = 0, count:int = children.length; i < count; i++) {
|
||||
var child:DaeNode = children[i];
|
||||
var joint:Joint;
|
||||
if (child in nodes) {
|
||||
joint = new Joint();
|
||||
joint.name = cloneString(child.name);
|
||||
object = child.applyAnimation(child.applyTransformations(joint));
|
||||
object.jointNode = child;
|
||||
animatedJoints[nodes[child]] = object;
|
||||
parent.addChild(joint);
|
||||
addJointChildren(joint, animatedJoints, child, nodes);
|
||||
} else {
|
||||
// If node is not a joint
|
||||
if (hasJointInDescendants(child, nodes)) {
|
||||
// If one of the children is a joint, there is need to create auxiliary joint instead of this node.
|
||||
joint = new Joint();
|
||||
joint.name = cloneString(child.name);
|
||||
object = child.applyAnimation(child.applyTransformations(joint));
|
||||
object.jointNode = child;
|
||||
// Add new joint to the end
|
||||
animatedJoints.push(object);
|
||||
parent.addChild(joint);
|
||||
addJointChildren(joint, animatedJoints, child, nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function hasJointInDescendants(parentNode:DaeNode, nodes:Dictionary):Boolean {
|
||||
var children:Vector.<DaeNode> = parentNode.nodes;
|
||||
for (var i:int = 0, count:int = children.length; i < count; i++) {
|
||||
var child:DaeNode = children[i];
|
||||
if (child in nodes || hasJointInDescendants(child, nodes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms all object vertices with the BindShapeMatrix from collada.
|
||||
*/
|
||||
private function getBindShapeMatrix():Vector.<Number> {
|
||||
var matrixXML:XML = data.skin.bind_shape_matrix[0];
|
||||
var res:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
if (matrixXML != null) {
|
||||
var matrix:Array = parseStringArray(matrixXML);
|
||||
for (var i:int = 0; i < matrix.length; i++) {
|
||||
res[i] = Number(matrix[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if joint hasn't parent joint.
|
||||
* @param node Joint node
|
||||
* @param nodes Dictionary. It items are the nodes keys.
|
||||
*
|
||||
*/
|
||||
private function isRootJointNode(node:DaeNode, nodes:Dictionary):Boolean {
|
||||
for (var parent:DaeNode = node.parent; parent != null; parent = parent.parent) {
|
||||
if (parent in nodes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function findRootJointNodes(skeletons:Vector.<DaeNode>):Vector.<DaeNode> {
|
||||
var nodes:Vector.<DaeNode> = findNodes(skeletons);
|
||||
var i:int = 0;
|
||||
var count:int = nodes.length;
|
||||
if (count > 0) {
|
||||
var nodesDictionary:Dictionary = new Dictionary();
|
||||
for (i = 0; i < count; i++) {
|
||||
nodesDictionary[nodes[i]] = i;
|
||||
}
|
||||
var rootNodes:Vector.<DaeNode> = new Vector.<DaeNode>();
|
||||
for (i = 0; i < count; i++) {
|
||||
var node:DaeNode = nodes[i];
|
||||
if (isRootJointNode(node, nodesDictionary)) {
|
||||
rootNodes.push(node);
|
||||
}
|
||||
}
|
||||
return rootNodes;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find node by Sid on sceletons vector.
|
||||
*/
|
||||
private function findNode(nodeName:String, skeletons:Vector.<DaeNode>):DaeNode {
|
||||
var count:int = skeletons.length;
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var node:DaeNode = skeletons[i].getNodeBySid(nodeName);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vector of joint nodes.
|
||||
*/
|
||||
private function findNodes(skeletons:Vector.<DaeNode>):Vector.<DaeNode> {
|
||||
var jointsXML:XML = data.skin.joints.input.(@semantic == "JOINT")[0];
|
||||
if (jointsXML != null) {
|
||||
var jointsSource:DaeSource = document.findSource(jointsXML.@source[0]);
|
||||
if (jointsSource != null) {
|
||||
if (jointsSource.parse() && jointsSource.names != null) {
|
||||
var stride:int = jointsSource.stride;
|
||||
var count:int = jointsSource.names.length/stride;
|
||||
var nodes:Vector.<DaeNode> = new Vector.<DaeNode>(count);
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var node:DaeNode = findNode(jointsSource.names[int(stride*i)], skeletons);
|
||||
if (node == null) {
|
||||
// Error: no node.
|
||||
}
|
||||
nodes[i] = node;
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(jointsXML.@source[0]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
248
src/alternativa/engine3d/loaders/collada/DaeDocument.as
Normal file
248
src/alternativa/engine3d/loaders/collada/DaeDocument.as
Normal file
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeDocument {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public var scene:DaeVisualScene;
|
||||
|
||||
/**
|
||||
* Collada file
|
||||
*/
|
||||
private var data:XML;
|
||||
|
||||
// Dictionaries to store matchings id-> DaeElement
|
||||
internal var sources:Object;
|
||||
internal var arrays:Object;
|
||||
internal var vertices:Object;
|
||||
public var geometries:Object;
|
||||
internal var nodes:Object;
|
||||
internal var lights:Object;
|
||||
internal var images:Object;
|
||||
internal var effects:Object;
|
||||
internal var controllers:Object;
|
||||
internal var samplers:Object;
|
||||
|
||||
public var materials:Object;
|
||||
|
||||
internal var logger:DaeLogger;
|
||||
|
||||
public var versionMajor:uint;
|
||||
public var versionMinor:uint;
|
||||
public var unitScaleFactor:Number = 1;
|
||||
public function DaeDocument(document:XML, units:Number) {
|
||||
this.data = document;
|
||||
|
||||
var versionComponents:Array = data.@version[0].toString().split(/[.,]/);
|
||||
versionMajor = parseInt(versionComponents[1], 10);
|
||||
versionMinor = parseInt(versionComponents[2], 10);
|
||||
|
||||
var colladaUnit:Number = parseFloat(data.asset[0].unit[0].@meter);
|
||||
if (units > 0) {
|
||||
unitScaleFactor = colladaUnit/units;
|
||||
} else {
|
||||
unitScaleFactor = 1;
|
||||
}
|
||||
|
||||
logger = new DaeLogger();
|
||||
|
||||
constructStructures();
|
||||
constructScenes();
|
||||
registerInstanceControllers();
|
||||
constructAnimations();
|
||||
}
|
||||
|
||||
private function getLocalID(url:XML):String {
|
||||
var path:String = url.toString();
|
||||
if (path.charAt(0) == "#") {
|
||||
return path.substr(1);
|
||||
} else {
|
||||
logger.logExternalError(url);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for the declarations of items and fill the dictionaries.
|
||||
private function constructStructures():void {
|
||||
var element:XML;
|
||||
|
||||
sources = new Object();
|
||||
arrays = new Object();
|
||||
for each (element in data..source) {
|
||||
// Collect all <source>. Dictionary <code>arrays</code> is filled at constructors.
|
||||
var source:DaeSource = new DaeSource(element, this);
|
||||
if (source.id != null) {
|
||||
sources[source.id] = source;
|
||||
}
|
||||
}
|
||||
|
||||
lights = new Object();
|
||||
for each (element in data.library_lights.light) {
|
||||
// Collect all <image>.
|
||||
var light:DaeLight = new DaeLight(element, this);
|
||||
if (light.id != null) {
|
||||
lights[light.id] = light;
|
||||
}
|
||||
}
|
||||
images = new Object();
|
||||
for each (element in data.library_images.image) {
|
||||
// Collect all <image>.
|
||||
var image:DaeImage = new DaeImage(element, this);
|
||||
if (image.id != null) {
|
||||
images[image.id] = image;
|
||||
}
|
||||
}
|
||||
effects = new Object();
|
||||
for each (element in data.library_effects.effect) {
|
||||
// Collect all <effect>. Dictionary <code>images</code> is filled at constructors.
|
||||
var effect:DaeEffect = new DaeEffect(element, this);
|
||||
if (effect.id != null) {
|
||||
effects[effect.id] = effect;
|
||||
}
|
||||
}
|
||||
materials = new Object();
|
||||
for each (element in data.library_materials.material) {
|
||||
// Collect all <material>.
|
||||
var material:DaeMaterial = new DaeMaterial(element, this);
|
||||
if (material.id != null) {
|
||||
materials[material.id] = material;
|
||||
}
|
||||
}
|
||||
geometries = new Object();
|
||||
vertices = new Object();
|
||||
for each (element in data.library_geometries.geometry) {
|
||||
// Collect all <geometry>. Dictionary <code>vertices</code> is filled at constructors.
|
||||
var geom:DaeGeometry = new DaeGeometry(element, this);
|
||||
if (geom.id != null) {
|
||||
geometries[geom.id] = geom;
|
||||
}
|
||||
}
|
||||
|
||||
controllers = new Object();
|
||||
for each (element in data.library_controllers.controller) {
|
||||
// Collect all <controllers>.
|
||||
var controller:DaeController = new DaeController(element, this);
|
||||
if (controller.id != null) {
|
||||
controllers[controller.id] = controller;
|
||||
}
|
||||
}
|
||||
|
||||
nodes = new Object();
|
||||
for each (element in data.library_nodes.node) {
|
||||
// Create only root nodes. Others are created recursively at constructors.
|
||||
var node:DaeNode = new DaeNode(element, this);
|
||||
if (node.id != null) {
|
||||
nodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function constructScenes():void {
|
||||
var vsceneURL:XML = data.scene.instance_visual_scene.@url[0];
|
||||
var vsceneID:String = getLocalID(vsceneURL);
|
||||
for each (var element:XML in data.library_visual_scenes.visual_scene) {
|
||||
// Create visual_scene. node's are created at constructors.
|
||||
var vscene:DaeVisualScene = new DaeVisualScene(element, this);
|
||||
if (vscene.id == vsceneID) {
|
||||
this.scene = vscene;
|
||||
}
|
||||
}
|
||||
if (vsceneID != null && scene == null) {
|
||||
logger.logNotFoundError(vsceneURL);
|
||||
}
|
||||
}
|
||||
|
||||
private function registerInstanceControllers():void {
|
||||
if (scene != null) {
|
||||
for (var i:int = 0, count:int = scene.nodes.length; i < count; i++) {
|
||||
scene.nodes[i].registerInstanceControllers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function constructAnimations():void {
|
||||
var element:XML;
|
||||
samplers = new Object();
|
||||
for each (element in data.library_animations..sampler) {
|
||||
// Collect all <sampler>.
|
||||
var sampler:DaeSampler = new DaeSampler(element, this);
|
||||
if (sampler.id != null) {
|
||||
samplers[sampler.id] = sampler;
|
||||
}
|
||||
}
|
||||
|
||||
for each (element in data.library_animations..channel) {
|
||||
var channel:DaeChannel = new DaeChannel(element, this);
|
||||
var node:DaeNode = channel.node;
|
||||
if (node != null) {
|
||||
node.addChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function findArray(url:XML):DaeArray {
|
||||
return arrays[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findSource(url:XML):DaeSource {
|
||||
return sources[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findLight(url:XML):DaeLight {
|
||||
return lights[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findImage(url:XML):DaeImage {
|
||||
return images[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findImageByID(id:String):DaeImage {
|
||||
return images[id];
|
||||
}
|
||||
|
||||
public function findEffect(url:XML):DaeEffect {
|
||||
return effects[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findMaterial(url:XML):DaeMaterial {
|
||||
return materials[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findVertices(url:XML):DaeVertices {
|
||||
return vertices[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findGeometry(url:XML):DaeGeometry {
|
||||
return geometries[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findNode(url:XML):DaeNode {
|
||||
return nodes[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findNodeByID(id:String):DaeNode {
|
||||
return nodes[id];
|
||||
}
|
||||
|
||||
public function findController(url:XML):DaeController {
|
||||
return controllers[getLocalID(url)];
|
||||
}
|
||||
|
||||
public function findSampler(url:XML):DaeSampler {
|
||||
return samplers[getLocalID(url)];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
215
src/alternativa/engine3d/loaders/collada/DaeEffect.as
Normal file
215
src/alternativa/engine3d/loaders/collada/DaeEffect.as
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.loaders.ParserMaterial;
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeEffect extends DaeElement {
|
||||
|
||||
public static var commonAlways:Boolean = false;
|
||||
|
||||
use namespace collada;
|
||||
|
||||
private var effectParams:Object;
|
||||
private var commonParams:Object;
|
||||
private var techniqueParams:Object;
|
||||
|
||||
private var diffuse:DaeEffectParam;
|
||||
private var ambient:DaeEffectParam;
|
||||
private var transparent:DaeEffectParam;
|
||||
private var transparency:DaeEffectParam;
|
||||
private var bump:DaeEffectParam;
|
||||
private var reflective:DaeEffectParam;
|
||||
private var emission:DaeEffectParam;
|
||||
private var specular:DaeEffectParam;
|
||||
|
||||
public function DaeEffect(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
|
||||
// image's are declared at <effect>
|
||||
constructImages();
|
||||
}
|
||||
|
||||
private function constructImages():void {
|
||||
var list:XMLList = data..image;
|
||||
for each (var element:XML in list) {
|
||||
var image:DaeImage = new DaeImage(element, document);
|
||||
if (image.id != null) {
|
||||
document.images[image.id] = image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
var element:XML;
|
||||
var param:DaeParam;
|
||||
effectParams = new Object();
|
||||
for each (element in data.newparam) {
|
||||
param = new DaeParam(element, document);
|
||||
effectParams[param.sid] = param;
|
||||
}
|
||||
commonParams = new Object();
|
||||
for each (element in data.profile_COMMON.newparam) {
|
||||
param = new DaeParam(element, document);
|
||||
commonParams[param.sid] = param;
|
||||
}
|
||||
techniqueParams = new Object();
|
||||
var technique:XML = data.profile_COMMON.technique[0];
|
||||
if (technique != null) {
|
||||
for each (element in technique.newparam) {
|
||||
param = new DaeParam(element, document);
|
||||
techniqueParams[param.sid] = param;
|
||||
}
|
||||
}
|
||||
var shader:XML = data.profile_COMMON.technique.*.(localName() == "constant" || localName() == "lambert" || localName() == "phong" || localName() == "blinn")[0];
|
||||
if (shader != null) {
|
||||
var diffuseXML:XML = null;
|
||||
if (shader.localName() == "constant") {
|
||||
diffuseXML = shader.emission[0];
|
||||
} else {
|
||||
diffuseXML = shader.diffuse[0];
|
||||
var emissionXML:XML = shader.emission[0];
|
||||
if (emissionXML != null) {
|
||||
emission = new DaeEffectParam(emissionXML, this);
|
||||
}
|
||||
}
|
||||
if (diffuseXML != null) {
|
||||
diffuse = new DaeEffectParam(diffuseXML, this);
|
||||
}
|
||||
if (shader.localName() == "phong" || shader.localName() == "blinn") {
|
||||
var specularXML:XML = shader.specular[0];
|
||||
if (specularXML != null) {
|
||||
specular = new DaeEffectParam(specularXML, this);
|
||||
}
|
||||
}
|
||||
var transparentXML:XML = shader.transparent[0];
|
||||
if (transparentXML != null) {
|
||||
transparent = new DaeEffectParam(transparentXML, this);
|
||||
}
|
||||
var transparencyXML:XML = shader.transparency[0];
|
||||
if (transparencyXML != null) {
|
||||
transparency = new DaeEffectParam(transparencyXML, this);
|
||||
}
|
||||
var ambientXML:XML = shader.ambient[0];
|
||||
if(ambientXML != null) {
|
||||
ambient = new DaeEffectParam(ambientXML, this);
|
||||
}
|
||||
var reflectiveXML:XML = shader.reflective[0];
|
||||
if(reflectiveXML != null) {
|
||||
reflective = new DaeEffectParam(reflectiveXML, this);
|
||||
}
|
||||
}
|
||||
var bumpXML:XML = data.profile_COMMON.technique.extra.technique.(hasOwnProperty("@profile") && @profile == "OpenCOLLADA3dsMax").bump[0];
|
||||
if (bumpXML != null) {
|
||||
bump = new DaeEffectParam(bumpXML, this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal function getParam(name:String, setparams:Object):DaeParam {
|
||||
var param:DaeParam = setparams[name];
|
||||
if (param != null) {
|
||||
return param;
|
||||
}
|
||||
param = techniqueParams[name];
|
||||
if (param != null) {
|
||||
return param;
|
||||
}
|
||||
param = commonParams[name];
|
||||
if (param != null) {
|
||||
return param;
|
||||
}
|
||||
return effectParams[name];
|
||||
}
|
||||
|
||||
private function float4ToUint(value:Array, alpha:Boolean = true):uint {
|
||||
var r:uint = (value[0] * 255);
|
||||
var g:uint = (value[1] * 255);
|
||||
var b:uint = (value[2] * 255);
|
||||
if (alpha) {
|
||||
var a:uint = (value[3] * 255);
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
} else {
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns material of the engine with given parameters.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public function getMaterial(setparams:Object):ParserMaterial {
|
||||
if (diffuse != null) {
|
||||
var material:ParserMaterial = new ParserMaterial();
|
||||
if (diffuse) {
|
||||
pushMap(material, diffuse, setparams);
|
||||
}
|
||||
if (specular != null) {
|
||||
pushMap(material, specular, setparams);
|
||||
}
|
||||
|
||||
if (emission != null) {
|
||||
pushMap(material, emission, setparams);
|
||||
}
|
||||
if (transparency != null) {
|
||||
material.transparency = transparency.getFloat(setparams);
|
||||
}
|
||||
if (transparent != null) {
|
||||
pushMap(material, transparent, setparams);
|
||||
}
|
||||
if (bump != null) {
|
||||
pushMap(material, bump, setparams);
|
||||
}
|
||||
if (ambient) {
|
||||
pushMap(material, ambient, setparams);
|
||||
}
|
||||
if (reflective) {
|
||||
pushMap(material, reflective, setparams);
|
||||
}
|
||||
return material;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function pushMap(material:ParserMaterial, param:DaeEffectParam, setparams:Object):void {
|
||||
var color:Array = param.getColor(setparams);
|
||||
|
||||
if(color != null){
|
||||
material.colors[cloneString(param.data.localName())] = float4ToUint(color, true);
|
||||
}
|
||||
else {
|
||||
var image:DaeImage = param.getImage(setparams);
|
||||
if(image != null){
|
||||
material.textures[cloneString(param.data.localName())] = new ExternalTextureResource(cloneString(image.init_from));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of texture channel for main map of object.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public function get mainTexCoords():String {
|
||||
var channel:String = null;
|
||||
channel = (channel == null && diffuse != null) ? diffuse.texCoord : channel;
|
||||
channel = (channel == null && transparent != null) ? transparent.texCoord : channel;
|
||||
channel = (channel == null && bump != null) ? bump.texCoord : channel;
|
||||
channel = (channel == null && emission != null) ? emission.texCoord : channel;
|
||||
channel = (channel == null && specular != null) ? specular.texCoord : channel;
|
||||
return channel;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
95
src/alternativa/engine3d/loaders/collada/DaeEffectParam.as
Normal file
95
src/alternativa/engine3d/loaders/collada/DaeEffectParam.as
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeEffectParam extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
private var effect:DaeEffect;
|
||||
|
||||
public function DaeEffectParam(data:XML, effect:DaeEffect) {
|
||||
super(data, effect.document);
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
public function getFloat(setparams:Object):Number {
|
||||
var floatXML:XML = data.float[0];
|
||||
if (floatXML != null) {
|
||||
return parseNumber(floatXML);
|
||||
}
|
||||
var paramRef:XML = data.param.@ref[0];
|
||||
if (paramRef != null) {
|
||||
var param:DaeParam = effect.getParam(paramRef.toString(), setparams);
|
||||
if (param != null) {
|
||||
return param.getFloat();
|
||||
}
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
|
||||
public function getColor(setparams:Object):Array {
|
||||
var colorXML:XML = data.color[0];
|
||||
if (colorXML != null) {
|
||||
return parseNumbersArray(colorXML);
|
||||
}
|
||||
var paramRef:XML = data.param.@ref[0];
|
||||
if (paramRef != null) {
|
||||
var param:DaeParam = effect.getParam(paramRef.toString(), setparams);
|
||||
if (param != null) {
|
||||
return param.getFloat4();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function get texture():String {
|
||||
var attr:XML = data.texture.@texture[0];
|
||||
return (attr == null) ? null : attr.toString();
|
||||
}
|
||||
|
||||
public function getSampler(setparams:Object):DaeParam {
|
||||
var sid:String = texture;
|
||||
if (sid != null) {
|
||||
return effect.getParam(sid, setparams);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getImage(setparams:Object):DaeImage {
|
||||
var sampler:DaeParam = getSampler(setparams);
|
||||
if (sampler != null) {
|
||||
var surfaceSID:String = sampler.surfaceSID;
|
||||
if (surfaceSID != null) {
|
||||
var surface:DaeParam = effect.getParam(surfaceSID, setparams);
|
||||
if (surface != null) {
|
||||
return surface.image;
|
||||
}
|
||||
} else {
|
||||
return sampler.image;
|
||||
}
|
||||
} else {
|
||||
// case of 3ds mas default export or so was used, it ignores spec and srores direct link to image
|
||||
return document.findImageByID(texture);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function get texCoord():String {
|
||||
var attr:XML = data.texture.@texcoord[0];
|
||||
return (attr == null) ? null : attr.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
115
src/alternativa/engine3d/loaders/collada/DaeElement.as
Normal file
115
src/alternativa/engine3d/loaders/collada/DaeElement.as
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public var document:DaeDocument;
|
||||
|
||||
public var data:XML;
|
||||
|
||||
/**
|
||||
* -1 - not parsed, 0 - parsed with error, 1 - parsed without error.
|
||||
*/
|
||||
private var _parsed:int = -1;
|
||||
|
||||
private static var _byteArray:ByteArray = new ByteArray();
|
||||
|
||||
public function DaeElement(data:XML, document:DaeDocument) {
|
||||
this.document = document;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public function cloneString(str:String):String {
|
||||
if(str == null) return null;
|
||||
_byteArray.position = 0;
|
||||
_byteArray.writeUTF(str);
|
||||
_byteArray.position = 0;
|
||||
var res:String = _byteArray.readUTF();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs pre-setting of object.
|
||||
* @return <code>false</code> on error.
|
||||
*/
|
||||
public function parse():Boolean {
|
||||
// -1 - not parsed, 0 - parsed with error, 1 - parsed without error.
|
||||
if (_parsed < 0) {
|
||||
_parsed = parseImplementation() ? 1 : 0;
|
||||
return _parsed != 0;
|
||||
}
|
||||
return _parsed != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden method <code>parse()</code>.
|
||||
*/
|
||||
protected function parseImplementation():Boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of String values.
|
||||
*/
|
||||
protected function parseStringArray(element:XML):Array {
|
||||
return element.text().toString().split(/\s+/);
|
||||
}
|
||||
|
||||
protected function parseNumbersArray(element:XML):Array {
|
||||
var arr:Array = element.text().toString().split(/\s+/);
|
||||
for (var i:int = 0, count:int = arr.length; i < count; i++) {
|
||||
var value:String = arr[i];
|
||||
if (value.indexOf(",") != -1) {
|
||||
value = value.replace(/,/, ".");
|
||||
}
|
||||
arr[i] = parseFloat(value);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
protected function parseIntsArray(element:XML):Array {
|
||||
var arr:Array = element.text().toString().split(/\s+/);
|
||||
for (var i:int = 0, count:int = arr.length; i < count; i++) {
|
||||
var value:String = arr[i];
|
||||
arr[i] = parseInt(value, 10);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
protected function parseNumber(element:XML):Number {
|
||||
var value:String = element.toString().replace(/,/, ".");
|
||||
return parseFloat(value);
|
||||
}
|
||||
|
||||
public function get id():String {
|
||||
var idXML:XML = data.@id[0];
|
||||
return (idXML == null) ? null : idXML.toString();
|
||||
}
|
||||
|
||||
public function get sid():String {
|
||||
var attr:XML = data.@sid[0];
|
||||
return (attr == null) ? null : attr.toString();
|
||||
}
|
||||
|
||||
public function get name():String {
|
||||
var nameXML:XML = data.@name[0];
|
||||
return (nameXML == null) ? null : nameXML.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
187
src/alternativa/engine3d/loaders/collada/DaeGeometry.as
Normal file
187
src/alternativa/engine3d/loaders/collada/DaeGeometry.as
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.VertexAttributes;
|
||||
import alternativa.engine3d.loaders.ParserMaterial;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeGeometry extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
use namespace alternativa3d;
|
||||
|
||||
internal var geometryVertices:Vector.<DaeVertex>;
|
||||
internal var primitives:Vector.<DaePrimitive>;
|
||||
internal var geometry:Geometry;
|
||||
|
||||
private var vertices:DaeVertices;
|
||||
|
||||
public function DaeGeometry(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
|
||||
/**
|
||||
* Items sources, vertices are declared in the <geometry>.
|
||||
* You should create sources in DaeDocument, not here.
|
||||
*/
|
||||
constructVertices();
|
||||
}
|
||||
|
||||
private function constructVertices():void {
|
||||
var verticesXML:XML = data.mesh.vertices[0];
|
||||
if (verticesXML != null) {
|
||||
vertices = new DaeVertices(verticesXML, document);
|
||||
document.vertices[vertices.id] = vertices;
|
||||
}
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
if (vertices != null) {
|
||||
parsePrimitives();
|
||||
|
||||
vertices.parse();
|
||||
var numVertices:int = vertices.positions.numbers.length/vertices.positions.stride;
|
||||
geometry = new Geometry();
|
||||
geometryVertices = new Vector.<DaeVertex>(numVertices);
|
||||
var i:int;
|
||||
var p:DaePrimitive;
|
||||
var channels:uint = 0;
|
||||
for (i = 0; i < primitives.length; i++) {
|
||||
p = primitives[i];
|
||||
p.parse();
|
||||
if (p.verticesEquals(vertices)) {
|
||||
numVertices = geometryVertices.length;
|
||||
channels |= p.fillGeometry(geometry, geometryVertices);
|
||||
} else {
|
||||
// Error, Vertices of another geometry can not be used
|
||||
}
|
||||
}
|
||||
|
||||
var attributes:Array = new Array(3);
|
||||
attributes[0] = VertexAttributes.POSITION;
|
||||
attributes[1] = VertexAttributes.POSITION;
|
||||
attributes[2] = VertexAttributes.POSITION;
|
||||
var index:int = 3;
|
||||
if (channels & DaePrimitive.NORMALS) {
|
||||
attributes[index++] = VertexAttributes.NORMAL;
|
||||
attributes[index++] = VertexAttributes.NORMAL;
|
||||
attributes[index++] = VertexAttributes.NORMAL;
|
||||
}
|
||||
if (channels & DaePrimitive.TANGENT4) {
|
||||
attributes[index++] = VertexAttributes.TANGENT4;
|
||||
attributes[index++] = VertexAttributes.TANGENT4;
|
||||
attributes[index++] = VertexAttributes.TANGENT4;
|
||||
attributes[index++] = VertexAttributes.TANGENT4;
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (channels & DaePrimitive.TEXCOORDS[i]) {
|
||||
attributes[index++] = VertexAttributes.TEXCOORDS[i];
|
||||
attributes[index++] = VertexAttributes.TEXCOORDS[i];
|
||||
}
|
||||
}
|
||||
|
||||
geometry.addVertexStream(attributes);
|
||||
|
||||
numVertices = geometryVertices.length;
|
||||
|
||||
var data:ByteArray = new ByteArray();
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
|
||||
var numMappings:int = attributes.length;
|
||||
data.length = 4*numMappings*numVertices;
|
||||
|
||||
for (i = 0; i < numVertices; i++) {
|
||||
var vertex:DaeVertex = geometryVertices[i];
|
||||
if (vertex != null) {
|
||||
data.position = 4*numMappings*i;
|
||||
data.writeFloat(vertex.x);
|
||||
data.writeFloat(vertex.y);
|
||||
data.writeFloat(vertex.z);
|
||||
if (vertex.normal != null) {
|
||||
data.writeFloat(vertex.normal.x);
|
||||
data.writeFloat(vertex.normal.y);
|
||||
data.writeFloat(vertex.normal.z);
|
||||
}
|
||||
if (vertex.tangent != null) {
|
||||
data.writeFloat(vertex.tangent.x);
|
||||
data.writeFloat(vertex.tangent.y);
|
||||
data.writeFloat(vertex.tangent.z);
|
||||
data.writeFloat(vertex.tangent.w);
|
||||
}
|
||||
for (var j:int = 0; j < vertex.uvs.length; j++) {
|
||||
data.writeFloat(vertex.uvs[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
geometry._vertexStreams[0].data = data;
|
||||
geometry._numVertices = numVertices;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function parsePrimitives():void {
|
||||
primitives = new Vector.<DaePrimitive>();
|
||||
var children:XMLList = data.mesh.children();
|
||||
|
||||
for (var i:int = 0, count:int = children.length(); i < count; i++) {
|
||||
var child:XML = children[i];
|
||||
switch (child.localName()) {
|
||||
case "polygons":
|
||||
case "polylist":
|
||||
case "triangles":
|
||||
case "trifans":
|
||||
case "tristrips":
|
||||
var p:DaePrimitive = new DaePrimitive(child, document);
|
||||
primitives.push(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates geometry and returns it as mesh.
|
||||
* Call <code>parse()</code> before using.
|
||||
* @param materials Dictionary of materials
|
||||
*/
|
||||
public function parseMesh(materials:Object):Mesh {
|
||||
if (data.mesh.length() > 0) {
|
||||
var mesh:Mesh = new Mesh();
|
||||
mesh.geometry = geometry;
|
||||
for (var i:int = 0; i < primitives.length; i++) {
|
||||
var p:DaePrimitive = primitives[i];
|
||||
var instanceMaterial:DaeInstanceMaterial = materials[p.materialSymbol];
|
||||
var material:ParserMaterial;
|
||||
if (instanceMaterial != null) {
|
||||
var daeMaterial:DaeMaterial = instanceMaterial.material;
|
||||
if (daeMaterial != null) {
|
||||
daeMaterial.parse();
|
||||
material = daeMaterial.material;
|
||||
daeMaterial.used = true;
|
||||
}
|
||||
}
|
||||
mesh.addSurface(material, p.indexBegin, p.numTriangles);
|
||||
}
|
||||
mesh.calculateBoundBox();
|
||||
return mesh;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/alternativa/engine3d/loaders/collada/DaeImage.as
Normal file
37
src/alternativa/engine3d/loaders/collada/DaeImage.as
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeImage extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public function DaeImage(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
public function get init_from():String {
|
||||
var element:XML = data.init_from[0];
|
||||
if (element != null) {
|
||||
if (document.versionMajor > 4) {
|
||||
var refXML:XML = element.ref[0];
|
||||
return (refXML == null) ? null : refXML.text().toString();
|
||||
}
|
||||
return element.text().toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
63
src/alternativa/engine3d/loaders/collada/DaeInput.as
Normal file
63
src/alternativa/engine3d/loaders/collada/DaeInput.as
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeInput extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public function DaeInput(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
public function get semantic():String {
|
||||
var attribute:XML = data.@semantic[0];
|
||||
return (attribute == null) ? null : attribute.toString();
|
||||
}
|
||||
|
||||
public function get source():XML {
|
||||
return data.@source[0];
|
||||
}
|
||||
|
||||
public function get offset():int {
|
||||
var attr:XML = data.@offset[0];
|
||||
return (attr == null) ? 0 : parseInt(attr.toString(), 10);
|
||||
}
|
||||
|
||||
public function get setNum():int {
|
||||
var attr:XML = data.@set[0];
|
||||
return (attr == null) ? 0 : parseInt(attr.toString(), 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* If DaeSource, located at the link source, is type of Number and
|
||||
* number of components is not less than specified number, then this method will return it.
|
||||
*
|
||||
*/
|
||||
public function prepareSource(minComponents:int):DaeSource {
|
||||
var source:DaeSource = document.findSource(this.source);
|
||||
if (source != null) {
|
||||
source.parse();
|
||||
if (source.numbers != null && source.stride >= minComponents) {
|
||||
return source;
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(data.@source[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeInstanceController extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public var node:DaeNode;
|
||||
|
||||
/**
|
||||
* List of top-level joints, which have common parent. (List of top-level joints, that have the common parent)
|
||||
* Call <code>parse()</code> befire using.
|
||||
*/
|
||||
public var topmostJoints:Vector.<DaeNode>;
|
||||
|
||||
public function DaeInstanceController(data:XML, document:DaeDocument, node:DaeNode) {
|
||||
super(data, document);
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
var controller:DaeController = this.controller;
|
||||
if (controller != null) {
|
||||
topmostJoints = controller.findRootJointNodes(this.skeletons);
|
||||
if (topmostJoints != null && topmostJoints.length > 1) {
|
||||
replaceNodesByTopmost(topmostJoints);
|
||||
}
|
||||
}
|
||||
return topmostJoints != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces each node in the list with its parent (the parent must be the same for all others node's or be a scene)
|
||||
* @param nodes not empty array of nodes.
|
||||
*/
|
||||
private function replaceNodesByTopmost(nodes:Vector.<DaeNode>):void {
|
||||
var i:int;
|
||||
var node:DaeNode, parent:DaeNode;
|
||||
var numNodes:int = nodes.length;
|
||||
var parents:Dictionary = new Dictionary();
|
||||
for (i = 0; i < numNodes; i++) {
|
||||
node = nodes[i];
|
||||
for (parent = node.parent; parent != null; parent = parent.parent) {
|
||||
if (parents[parent]) {
|
||||
parents[parent]++;
|
||||
} else {
|
||||
parents[parent] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Replase node with its parent if it has the same parent with each other node or has no parent at all
|
||||
for (i = 0; i < numNodes; i++) {
|
||||
node = nodes[i];
|
||||
while ((parent = node.parent) != null && (parents[parent] != numNodes)) {
|
||||
node = node.parent;
|
||||
}
|
||||
nodes[i] = node;
|
||||
}
|
||||
}
|
||||
|
||||
private function get controller():DaeController {
|
||||
var controller:DaeController = document.findController(data.@url[0]);
|
||||
if (controller == null) {
|
||||
document.logger.logNotFoundError(data.@url[0]);
|
||||
}
|
||||
return controller;
|
||||
}
|
||||
|
||||
private function get skeletons():Vector.<DaeNode> {
|
||||
var list:XMLList = data.skeleton;
|
||||
if (list.length() > 0) {
|
||||
var skeletons:Vector.<DaeNode> = new Vector.<DaeNode>();
|
||||
for (var i:int = 0, count:int = list.length(); i < count; i++) {
|
||||
var skeletonXML:XML = list[i];
|
||||
var skel:DaeNode = document.findNode(skeletonXML.text()[0]);
|
||||
if (skel != null) {
|
||||
skeletons.push(skel);
|
||||
} else {
|
||||
document.logger.logNotFoundError(skeletonXML);
|
||||
}
|
||||
}
|
||||
return skeletons;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function parseSkin(materials:Object):DaeObject {
|
||||
var controller:DaeController = this.controller;
|
||||
if (controller != null) {
|
||||
controller.parse();
|
||||
return controller.parseSkin(materials, topmostJoints, this.skeletons);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeInstanceMaterial extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public function DaeInstanceMaterial(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
public function get symbol():String {
|
||||
var attribute:XML = data.@symbol[0];
|
||||
return (attribute == null) ? null : attribute.toString();
|
||||
}
|
||||
|
||||
private function get target():XML {
|
||||
return data.@target[0];
|
||||
}
|
||||
|
||||
public function get material():DaeMaterial {
|
||||
var mat:DaeMaterial = document.findMaterial(target);
|
||||
if (mat == null) {
|
||||
document.logger.logNotFoundError(target);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
public function getBindVertexInputSetNum(semantic:String):int {
|
||||
var bindVertexInputXML:XML = data.bind_vertex_input.(@semantic == semantic)[0];
|
||||
if (bindVertexInputXML == null) return 0;
|
||||
var setNumXML:XML = bindVertexInputXML.@input_set[0];
|
||||
return (setNumXML == null) ? 0 : parseInt(setNumXML.toString(), 10);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
119
src/alternativa/engine3d/loaders/collada/DaeLight.as
Normal file
119
src/alternativa/engine3d/loaders/collada/DaeLight.as
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.lights.AmbientLight;
|
||||
import alternativa.engine3d.lights.DirectionalLight;
|
||||
import alternativa.engine3d.lights.OmniLight;
|
||||
import alternativa.engine3d.lights.SpotLight;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeLight extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public function DaeLight(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
private function float4ToUint(value:Array):uint {
|
||||
var r:uint = (value[0] * 255);
|
||||
var g:uint = (value[1] * 255);
|
||||
var b:uint = (value[2] * 255);
|
||||
return (r << 16) | (g << 8) | b | 0xFF000000;
|
||||
}
|
||||
|
||||
public function get revertDirection():Boolean {
|
||||
var info:XML = data.technique_common.children()[0];
|
||||
return (info == null) ? false : (info.localName() == "directional" || info.localName() == "spot");
|
||||
}
|
||||
|
||||
public function parseLight():Light3D {
|
||||
var info:XML = data.technique_common.children()[0];
|
||||
var extra:XML = data.extra.technique.(@profile[0] == "OpenCOLLADA3dsMax").light[0];
|
||||
var light:Light3D = null;
|
||||
if (info != null) {
|
||||
var color:uint = float4ToUint(parseNumbersArray(info.color[0]));
|
||||
var constantAttenuationXML:XML;
|
||||
var linearAttenuationXML:XML;
|
||||
var linearAttenuation:Number = 0;
|
||||
var attenuationStart:Number = 0;
|
||||
var attenuationEnd:Number = 1;
|
||||
switch (info.localName()) {
|
||||
case "ambient":
|
||||
light = new AmbientLight(color);
|
||||
break;
|
||||
case "directional":
|
||||
var dLight:DirectionalLight = new DirectionalLight(color);
|
||||
light = dLight;
|
||||
break;
|
||||
case "point":
|
||||
if (extra != null) {
|
||||
attenuationStart = parseNumber(extra.attenuation_far_start[0]);
|
||||
attenuationEnd = parseNumber(extra.attenuation_far_end[0]);
|
||||
} else {
|
||||
constantAttenuationXML = info.constant_attenuation[0];
|
||||
linearAttenuationXML = info.linear_attenuation[0];
|
||||
if (constantAttenuationXML != null) {
|
||||
attenuationStart = -parseNumber(constantAttenuationXML);
|
||||
}
|
||||
if (linearAttenuationXML != null) {
|
||||
linearAttenuation = parseNumber(linearAttenuationXML);
|
||||
}
|
||||
if (linearAttenuation > 0) {
|
||||
attenuationEnd = 1/linearAttenuation + attenuationStart;
|
||||
} else {
|
||||
attenuationEnd = attenuationStart + 1;
|
||||
}
|
||||
}
|
||||
var oLight:OmniLight = new OmniLight(color, attenuationStart, attenuationEnd);
|
||||
light = oLight;
|
||||
break;
|
||||
case "spot":
|
||||
var hotspot:Number = 0;
|
||||
var fallof:Number = Math.PI/4;
|
||||
const DEG2RAD:Number = Math.PI/180;
|
||||
if (extra != null) {
|
||||
attenuationStart = parseNumber(extra.attenuation_far_start[0]);
|
||||
attenuationEnd = parseNumber(extra.attenuation_far_end[0]);
|
||||
hotspot = DEG2RAD * parseNumber(extra.hotspot_beam[0]);
|
||||
fallof = DEG2RAD * parseNumber(extra.falloff[0]);
|
||||
} else {
|
||||
constantAttenuationXML = info.constant_attenuation[0];
|
||||
linearAttenuationXML = info.linear_attenuation[0];
|
||||
if (constantAttenuationXML != null) {
|
||||
attenuationStart = -parseNumber(constantAttenuationXML);
|
||||
}
|
||||
if (linearAttenuationXML != null) {
|
||||
linearAttenuation = parseNumber(linearAttenuationXML);
|
||||
}
|
||||
if (linearAttenuation > 0) {
|
||||
attenuationEnd = 1/linearAttenuation + attenuationStart;
|
||||
} else {
|
||||
attenuationEnd = attenuationStart + 1;
|
||||
}
|
||||
}
|
||||
var sLight:SpotLight = new SpotLight(color, attenuationStart, attenuationEnd, hotspot, fallof);
|
||||
light = sLight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (extra != null) {
|
||||
light.intensity = parseNumber(extra.multiplier[0]);
|
||||
}
|
||||
return light;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
62
src/alternativa/engine3d/loaders/collada/DaeLogger.as
Normal file
62
src/alternativa/engine3d/loaders/collada/DaeLogger.as
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeLogger {
|
||||
|
||||
public function DaeLogger() {
|
||||
}
|
||||
|
||||
private function logMessage(message:String, element:XML):void {
|
||||
var index:int = 0;
|
||||
var name:String = (element.nodeKind() == "attribute") ? "@" + element.localName() : element.localName() + ((index > 0) ? "[" + index + "]" : "");
|
||||
var parent:* = element.parent();
|
||||
while (parent != null) {
|
||||
// index = parent.childIndex();
|
||||
name = parent.localName() + ((index > 0) ? "[" + index + "]" : "") + "." + name;
|
||||
parent = parent.parent();
|
||||
}
|
||||
trace(message, '| "' + name + '"');
|
||||
}
|
||||
|
||||
private function logError(message:String, element:XML):void {
|
||||
logMessage("[ERROR] " + message, element);
|
||||
}
|
||||
|
||||
public function logExternalError(element:XML):void {
|
||||
logError("External urls don't supported", element);
|
||||
}
|
||||
|
||||
public function logSkewError(element:XML):void {
|
||||
logError("<skew> don't supported", element);
|
||||
}
|
||||
|
||||
public function logJointInAnotherSceneError(element:XML):void {
|
||||
logError("Joints in different scenes don't supported", element);
|
||||
}
|
||||
|
||||
public function logInstanceNodeError(element:XML):void {
|
||||
logError("<instance_node> don't supported", element);
|
||||
}
|
||||
|
||||
public function logNotFoundError(element:XML):void {
|
||||
logError("Element with url \"" + element.toString() + "\" not found", element);
|
||||
}
|
||||
|
||||
public function logNotEnoughDataError(element:XML):void {
|
||||
logError("Not enough data", element);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
72
src/alternativa/engine3d/loaders/collada/DaeMaterial.as
Normal file
72
src/alternativa/engine3d/loaders/collada/DaeMaterial.as
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.loaders.ParserMaterial;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeMaterial extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
/**
|
||||
* Material of engine.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var material:ParserMaterial;
|
||||
|
||||
/**
|
||||
* Name of texture channel for color map of object.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var mainTexCoords:String;
|
||||
|
||||
/**
|
||||
* If <code>true</code>material is in use.
|
||||
*/
|
||||
public var used:Boolean = false;
|
||||
|
||||
public function DaeMaterial(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
private function parseSetParams():Object {
|
||||
var params:Object = new Object();
|
||||
var list:XMLList = data.instance_effect.setparam;
|
||||
for each (var element:XML in list) {
|
||||
var param:DaeParam = new DaeParam(element, document);
|
||||
params[param.ref] = param;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private function get effectURL():XML {
|
||||
return data.instance_effect.@url[0];
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
var effect:DaeEffect = document.findEffect(effectURL);
|
||||
if (effect != null) {
|
||||
effect.parse();
|
||||
material = effect.getMaterial(parseSetParams());
|
||||
mainTexCoords = effect.mainTexCoords;
|
||||
if (material != null) {
|
||||
material.name = cloneString(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
517
src/alternativa/engine3d/loaders/collada/DaeNode.as
Normal file
517
src/alternativa/engine3d/loaders/collada/DaeNode.as
Normal file
@@ -0,0 +1,517 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.animation.AnimationClip;
|
||||
import alternativa.engine3d.animation.keys.NumberTrack;
|
||||
import alternativa.engine3d.animation.keys.Track;
|
||||
import alternativa.engine3d.animation.keys.TransformTrack;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
import alternativa.engine3d.objects.Skin;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeNode extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public var scene:DaeVisualScene;
|
||||
public var parent:DaeNode;
|
||||
|
||||
// Skin or top-level joint.
|
||||
public var skinOrTopmostJoint:Boolean = false;
|
||||
|
||||
/**
|
||||
* Animation channels of this node.
|
||||
*/
|
||||
private var channels:Vector.<DaeChannel>;
|
||||
|
||||
/**
|
||||
* Vector of controllers, which have reference to this node.
|
||||
*/
|
||||
private var instanceControllers:Vector.<DaeInstanceController>;
|
||||
|
||||
/**
|
||||
* Array of nodes at this node.
|
||||
*/
|
||||
public var nodes:Vector.<DaeNode>;
|
||||
|
||||
/**
|
||||
* Array of objects at this node.
|
||||
* Call <code>parse()</code> before using.
|
||||
*
|
||||
*/
|
||||
public var objects:Vector.<DaeObject>;
|
||||
|
||||
/**
|
||||
* Vector of skins at this node.
|
||||
* Call <code>parse()</code> before using.
|
||||
*
|
||||
*/
|
||||
public var skins:Vector.<DaeObject>;
|
||||
|
||||
/**
|
||||
* Name of object for animation
|
||||
*/
|
||||
public function get animName():String {
|
||||
var n:String = this.name;
|
||||
return (n == null) ? this.id : n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create node from xml. Child nodes are created recursively.
|
||||
*/
|
||||
public function DaeNode(data:XML, document:DaeDocument, scene:DaeVisualScene = null, parent:DaeNode = null) {
|
||||
super(data, document);
|
||||
|
||||
this.scene = scene;
|
||||
this.parent = parent;
|
||||
|
||||
// Others node's declares inside <node>
|
||||
constructNodes();
|
||||
}
|
||||
|
||||
private function constructNodes():void {
|
||||
var nodesList:XMLList = data.node;
|
||||
var count:int = nodesList.length();
|
||||
nodes = new Vector.<DaeNode>(count);
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var node:DaeNode = new DaeNode(nodesList[i], document, scene, this);
|
||||
if (node.id != null) {
|
||||
document.nodes[node.id] = node;
|
||||
}
|
||||
nodes[i] = node;
|
||||
}
|
||||
}
|
||||
|
||||
internal function registerInstanceControllers():void {
|
||||
var instanceControllerXMLs:XMLList = data.instance_controller;
|
||||
var i:int;
|
||||
var count:int = instanceControllerXMLs.length()
|
||||
for (i = 0; i < count; i++) {
|
||||
skinOrTopmostJoint = true;
|
||||
var instanceControllerXML:XML = instanceControllerXMLs[i];
|
||||
var instanceController:DaeInstanceController = new DaeInstanceController(instanceControllerXML, document, this);
|
||||
if (instanceController.parse()) {
|
||||
var jointNodes:Vector.<DaeNode> = instanceController.topmostJoints;
|
||||
var numNodes:int = jointNodes.length;
|
||||
if (numNodes > 0) {
|
||||
var jointNode:DaeNode = jointNodes[0];
|
||||
jointNode.addInstanceController(instanceController);
|
||||
for (var j:int = 0; j < numNodes; j++) {
|
||||
jointNodes[j].skinOrTopmostJoint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
count = nodes.length;
|
||||
for (i = 0; i < count; i++) {
|
||||
nodes[i].registerInstanceControllers();
|
||||
}
|
||||
}
|
||||
|
||||
public function addChannel(channel:DaeChannel):void {
|
||||
if (channels == null) {
|
||||
channels = new Vector.<DaeChannel>();
|
||||
}
|
||||
channels.push(channel);
|
||||
}
|
||||
|
||||
public function addInstanceController(controller:DaeInstanceController):void {
|
||||
if (instanceControllers == null) {
|
||||
instanceControllers = new Vector.<DaeInstanceController>();
|
||||
}
|
||||
instanceControllers.push(controller);
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
this.skins = parseSkins();
|
||||
this.objects = parseObjects();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function parseInstanceMaterials(geometry:XML):Object {
|
||||
var instances:Object = new Object();
|
||||
var list:XMLList = geometry.bind_material.technique_common.instance_material;
|
||||
for (var i:int = 0, count:int = list.length(); i < count; i++) {
|
||||
var instance:DaeInstanceMaterial = new DaeInstanceMaterial(list[i], document);
|
||||
instances[instance.symbol] = instance;
|
||||
}
|
||||
return instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node by Sid.
|
||||
*/
|
||||
public function getNodeBySid(sid:String):DaeNode {
|
||||
if (sid == this.sid) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var levelNodes:Vector.<Vector.<DaeNode> > = new Vector.<Vector.<DaeNode> >;
|
||||
var levelNodes2:Vector.<Vector.<DaeNode> > = new Vector.<Vector.<DaeNode> >;
|
||||
|
||||
levelNodes.push(nodes);
|
||||
var len:int = levelNodes.length;
|
||||
while (len > 0) {
|
||||
for (var i:int = 0; i < len; i++) {
|
||||
var children:Vector.<DaeNode> = levelNodes[i];
|
||||
var count:int = children.length;
|
||||
for (var j:int = 0; j < count; j++) {
|
||||
var node:DaeNode = children[j];
|
||||
if (node.sid == sid) {
|
||||
return node;
|
||||
}
|
||||
if (node.nodes.length > 0) {
|
||||
levelNodes2.push(node.nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
var temp:Vector.<Vector.<DaeNode> > = levelNodes;
|
||||
levelNodes = levelNodes2;
|
||||
levelNodes2 = temp;
|
||||
levelNodes2.length = 0;
|
||||
|
||||
len = levelNodes.length;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns array of skins, associated with this node.
|
||||
*/
|
||||
public function parseSkins():Vector.<DaeObject> {
|
||||
if (instanceControllers == null) {
|
||||
return null;
|
||||
}
|
||||
var skins:Vector.<DaeObject> = new Vector.<DaeObject>();
|
||||
for (var i:int = 0, count:int = instanceControllers.length; i < count; i++) {
|
||||
var instanceController:DaeInstanceController = instanceControllers[i];
|
||||
instanceController.parse();
|
||||
var skinAndAnimatedJoints:DaeObject = instanceController.parseSkin(parseInstanceMaterials(instanceController.data));
|
||||
if (skinAndAnimatedJoints != null) {
|
||||
var skin:Skin = Skin(skinAndAnimatedJoints.object);
|
||||
// Name is got from node, that contains instance_controller.
|
||||
skin.name = cloneString(instanceController.node.name);
|
||||
// Not apply transformation and animation for skin. It specifies at root joints.
|
||||
skins.push(skinAndAnimatedJoints);
|
||||
}
|
||||
}
|
||||
return (skins.length > 0) ? skins : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns array of objects, associated with this node.
|
||||
* Can be Mesh or Object3D, if type of object is unknown.
|
||||
*/
|
||||
public function parseObjects():Vector.<DaeObject> {
|
||||
var objects:Vector.<DaeObject> = new Vector.<DaeObject>();
|
||||
var children:XMLList = data.children();
|
||||
var i:int, count:int;
|
||||
|
||||
for (i = 0, count = children.length(); i < count; i++) {
|
||||
var child:XML = children[i];
|
||||
switch (child.localName()) {
|
||||
case "instance_light":
|
||||
var lightInstance:DaeLight = document.findLight(child.@url[0]);
|
||||
if (lightInstance != null) {
|
||||
var light:Light3D = lightInstance.parseLight();
|
||||
if (light != null) {
|
||||
light.name = cloneString(name);
|
||||
if (lightInstance.revertDirection) {
|
||||
// Rotate 180 degrees along the x-axis, for correspondence to engine
|
||||
var rotXMatrix:Matrix3D = new Matrix3D();
|
||||
rotXMatrix.appendRotation(180, Vector3D.X_AXIS);
|
||||
// Not upload animations yet for these light sources
|
||||
objects.push(new DaeObject(applyTransformations(light, rotXMatrix)));
|
||||
} else {
|
||||
objects.push(applyAnimation(applyTransformations(light)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(child.@url[0]);
|
||||
}
|
||||
break;
|
||||
case "instance_geometry":
|
||||
var geom:DaeGeometry = document.findGeometry(child.@url[0]);
|
||||
if (geom != null) {
|
||||
geom.parse();
|
||||
var mesh:Mesh = geom.parseMesh(parseInstanceMaterials(child));
|
||||
if(mesh != null){
|
||||
mesh.name = cloneString(name);
|
||||
objects.push(applyAnimation(applyTransformations(mesh)));
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(child.@url[0]);
|
||||
}
|
||||
break;
|
||||
case "instance_node":
|
||||
document.logger.logInstanceNodeError(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (objects.length > 0) ? objects : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns transformation of node as a matrix.
|
||||
* @param initialMatrix To this matrix tranformation will appended.
|
||||
*/
|
||||
private function getMatrix(initialMatrix:Matrix3D = null):Matrix3D {
|
||||
var matrix:Matrix3D = (initialMatrix == null) ? new Matrix3D() : initialMatrix;
|
||||
var components:Array;
|
||||
var children:XMLList = data.children();
|
||||
for (var i:int = children.length() - 1; i >= 0; i--) {
|
||||
//Transformations are append from the end to begin
|
||||
var child:XML = children[i];
|
||||
var sid:XML = child.@sid[0];
|
||||
if (sid != null && sid.toString() == "post-rotationY") {
|
||||
// Default 3dsmax exporter writes some trash which ignores
|
||||
continue;
|
||||
}
|
||||
switch (child.localName()) {
|
||||
case "scale" : {
|
||||
components = parseNumbersArray(child);
|
||||
matrix.appendScale(components[0], components[1], components[2]);
|
||||
break;
|
||||
}
|
||||
case "rotate" : {
|
||||
components = parseNumbersArray(child);
|
||||
matrix.appendRotation(components[3], new Vector3D(components[0], components[1], components[2]));
|
||||
break;
|
||||
}
|
||||
case "translate" : {
|
||||
components = parseNumbersArray(child);
|
||||
matrix.appendTranslation(components[0]*document.unitScaleFactor,
|
||||
components[1]*document.unitScaleFactor, components[2]*document.unitScaleFactor);
|
||||
break;
|
||||
}
|
||||
case "matrix" : {
|
||||
components = parseNumbersArray(child);
|
||||
matrix.append(new Matrix3D(Vector.<Number>([components[0], components[4], components[8], components[12],
|
||||
components[1], components[5], components[9], components[13],
|
||||
components[2], components[6], components[10], components[14],
|
||||
components[3]*document.unitScaleFactor ,components[7]*document.unitScaleFactor, components[11]*document.unitScaleFactor, components[15]])));
|
||||
break;
|
||||
}
|
||||
case "lookat" : {
|
||||
break;
|
||||
}
|
||||
case "skew" : {
|
||||
document.logger.logSkewError(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply transformation to object.
|
||||
* @param prepend If is not <code>null</code> transformation will added to this matrix.
|
||||
*/
|
||||
public function applyTransformations(object:Object3D, prepend:Matrix3D = null, append:Matrix3D = null):Object3D {
|
||||
var matrix:Matrix3D = getMatrix(prepend);
|
||||
if (append != null) {
|
||||
matrix.append(append);
|
||||
}
|
||||
var vs:Vector.<Vector3D> = matrix.decompose();
|
||||
var t:Vector3D = vs[0];
|
||||
var r:Vector3D = vs[1];
|
||||
var s:Vector3D = vs[2];
|
||||
object.x = t.x;
|
||||
object.y = t.y;
|
||||
object.z = t.z;
|
||||
object.rotationX = r.x;
|
||||
object.rotationY = r.y;
|
||||
object.rotationZ = r.z;
|
||||
object.scaleX = s.x;
|
||||
object.scaleY = s.y;
|
||||
object.scaleZ = s.z;
|
||||
return object;
|
||||
}
|
||||
|
||||
public function applyAnimation(object:Object3D):DaeObject {
|
||||
var animation:AnimationClip = parseAnimation(object);
|
||||
if (animation == null) {
|
||||
return new DaeObject(object);
|
||||
}
|
||||
object.name = animName;
|
||||
animation.attach(object, false);
|
||||
return new DaeObject(object, animation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns animation of node.
|
||||
*/
|
||||
public function parseAnimation(object:Object3D = null):AnimationClip {
|
||||
if (channels == null || !hasTransformationAnimation()) {
|
||||
return null;
|
||||
}
|
||||
var channel:DaeChannel = getChannel(DaeChannel.PARAM_MATRIX);
|
||||
if (channel != null) {
|
||||
return createClip(channel.tracks);
|
||||
}
|
||||
var clip:AnimationClip = new AnimationClip();
|
||||
var components:Vector.<Vector3D> = (object != null) ? null : getMatrix().decompose();
|
||||
|
||||
// Translation
|
||||
channel = getChannel(DaeChannel.PARAM_TRANSLATE);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
channel = getChannel(DaeChannel.PARAM_TRANSLATE_X);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("x", (object == null) ? components[0].x : object.x));
|
||||
}
|
||||
channel = getChannel(DaeChannel.PARAM_TRANSLATE_Y);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("y", (object == null) ? components[0].y : object.y));
|
||||
}
|
||||
channel = getChannel(DaeChannel.PARAM_TRANSLATE_Z);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("z", (object == null) ? components[0].z : object.z));
|
||||
}
|
||||
}
|
||||
// Rotation
|
||||
channel = getChannel(DaeChannel.PARAM_ROTATION_X);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("rotationX", (object == null) ? components[1].x : object.rotationX));
|
||||
}
|
||||
channel = getChannel(DaeChannel.PARAM_ROTATION_Y);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("rotationY", (object == null) ? components[1].y : object.rotationY));
|
||||
}
|
||||
channel = getChannel(DaeChannel.PARAM_ROTATION_Z);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("rotationZ", (object == null) ? components[1].z : object.rotationZ));
|
||||
}
|
||||
// Scale
|
||||
channel = getChannel(DaeChannel.PARAM_SCALE);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
channel = getChannel(DaeChannel.PARAM_SCALE_X);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("scaleX", (object == null) ? components[2].x : object.scaleX));
|
||||
}
|
||||
channel = getChannel(DaeChannel.PARAM_SCALE_Y);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("scaleY", (object == null) ? components[2].y : object.scaleY));
|
||||
}
|
||||
channel = getChannel(DaeChannel.PARAM_SCALE_Z);
|
||||
if (channel != null) {
|
||||
addTracksToClip(clip, channel.tracks);
|
||||
} else {
|
||||
clip.addTrack(createValueStaticTrack("scaleZ", (object == null) ? components[2].z : object.scaleZ));
|
||||
}
|
||||
}
|
||||
if (clip.numTracks > 0) {
|
||||
return clip;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function createClip(tracks:Vector.<Track>):AnimationClip {
|
||||
var clip:AnimationClip = new AnimationClip();
|
||||
for (var i:int = 0, count:int = tracks.length; i < count; i++) {
|
||||
clip.addTrack(tracks[i]);
|
||||
}
|
||||
return clip;
|
||||
}
|
||||
|
||||
private function addTracksToClip(clip:AnimationClip, tracks:Vector.<Track>):void {
|
||||
for (var i:int = 0, count:int = tracks.length; i < count; i++) {
|
||||
clip.addTrack(tracks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private function hasTransformationAnimation():Boolean {
|
||||
for (var i:int = 0, count:int = channels.length; i < count; i++) {
|
||||
var channel:DaeChannel = channels[i];
|
||||
channel.parse();
|
||||
var result:Boolean = channel.animatedParam == DaeChannel.PARAM_MATRIX;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE_X;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE_Y;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE_Z;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_ROTATION_X;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_ROTATION_Y;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_ROTATION_Z;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE_X;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE_Y;
|
||||
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE_Z;
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getChannel(param:String):DaeChannel {
|
||||
for (var i:int = 0, count:int = channels.length; i < count; i++) {
|
||||
var channel:DaeChannel = channels[i];
|
||||
channel.parse();
|
||||
if (channel.animatedParam == param) {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function concatTracks(source:Vector.<Track>, dest:Vector.<Track>):void {
|
||||
for (var i:int = 0, count:int = source.length; i < count; i++) {
|
||||
dest.push(source[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private function createValueStaticTrack(property:String, value:Number):Track {
|
||||
var track:NumberTrack = new NumberTrack(animName, property);
|
||||
track.addKey(0, value);
|
||||
return track;
|
||||
}
|
||||
|
||||
public function createStaticTransformTrack():TransformTrack {
|
||||
var track:TransformTrack = new TransformTrack(animName);
|
||||
track.addKey(0, getMatrix());
|
||||
return track;
|
||||
}
|
||||
|
||||
public function get layer():String {
|
||||
var layerXML:XML = data.@layer[0];
|
||||
return (layerXML == null) ? null : layerXML.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
31
src/alternativa/engine3d/loaders/collada/DaeObject.as
Normal file
31
src/alternativa/engine3d/loaders/collada/DaeObject.as
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.animation.AnimationClip;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeObject {
|
||||
|
||||
public var object:Object3D;
|
||||
public var animation:AnimationClip;
|
||||
public var jointNode:DaeNode;
|
||||
|
||||
public function DaeObject(object:Object3D, animation:AnimationClip = null) {
|
||||
this.object = object;
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
89
src/alternativa/engine3d/loaders/collada/DaeParam.as
Normal file
89
src/alternativa/engine3d/loaders/collada/DaeParam.as
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeParam extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public function DaeParam(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
public function get ref():String {
|
||||
var attribute:XML = data.@ref[0];
|
||||
return (attribute == null) ? null : attribute.toString();
|
||||
}
|
||||
|
||||
public function getFloat():Number {
|
||||
var floatXML:XML = data.float[0];
|
||||
if (floatXML != null) {
|
||||
return parseNumber(floatXML);
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
|
||||
public function getFloat4():Array {
|
||||
var element:XML = data.float4[0];
|
||||
var components:Array;
|
||||
if (element == null) {
|
||||
element = data.float3[0];
|
||||
if (element != null) {
|
||||
components = parseNumbersArray(element);
|
||||
components[3] = 1.0;
|
||||
}
|
||||
} else {
|
||||
components = parseNumbersArray(element);
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Sid of a parameter type of surface. Only for sampler2D and Collada ver. 1.4
|
||||
*/
|
||||
public function get surfaceSID():String {
|
||||
var element:XML = data.sampler2D.source[0];
|
||||
return (element == null) ? null : element.text().toString();
|
||||
}
|
||||
|
||||
public function get wrap_s():String {
|
||||
var element:XML = data.sampler2D.wrap_s[0];
|
||||
return (element == null) ? null : element.text().toString();
|
||||
}
|
||||
|
||||
public function get image():DaeImage {
|
||||
var surface:XML = data.surface[0];
|
||||
var image:DaeImage;
|
||||
if (surface != null) {
|
||||
// Collada 1.4
|
||||
var init_from:XML = surface.init_from[0];
|
||||
if (init_from == null) {
|
||||
// Error
|
||||
return null;
|
||||
}
|
||||
image = document.findImageByID(init_from.text().toString());
|
||||
} else {
|
||||
// Collada 1.5
|
||||
var imageIDXML:XML = data.instance_image.@url[0];
|
||||
if (imageIDXML == null) {
|
||||
// error
|
||||
return null;
|
||||
}
|
||||
image = document.findImage(imageIDXML);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
331
src/alternativa/engine3d/loaders/collada/DaePrimitive.as
Normal file
331
src/alternativa/engine3d/loaders/collada/DaePrimitive.as
Normal file
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
|
||||
use namespace collada;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaePrimitive extends DaeElement {
|
||||
|
||||
internal static const NORMALS:int = 1;
|
||||
internal static const TANGENT4:int = 2;
|
||||
internal static const TEXCOORDS:Vector.<uint> = Vector.<uint>([8, 16, 32, 64, 128, 256, 512, 1024]);
|
||||
|
||||
internal var verticesInput:DaeInput;
|
||||
internal var texCoordsInputs:Vector.<DaeInput>;
|
||||
internal var normalsInput:DaeInput;
|
||||
internal var biNormalsInputs:Vector.<DaeInput>;
|
||||
internal var tangentsInputs:Vector.<DaeInput>;
|
||||
|
||||
internal var indices:Vector.<uint>;
|
||||
internal var inputsStride:int;
|
||||
|
||||
public var indexBegin:int;
|
||||
public var numTriangles:int;
|
||||
|
||||
public function DaePrimitive(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
parseInputs();
|
||||
parseIndices();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function get type():String {
|
||||
return data.localName() as String;
|
||||
}
|
||||
|
||||
private function parseInputs():void {
|
||||
texCoordsInputs = new Vector.<DaeInput>();
|
||||
tangentsInputs = new Vector.<DaeInput>();
|
||||
biNormalsInputs = new Vector.<DaeInput>();
|
||||
var inputsList:XMLList = data.input;
|
||||
var maxInputOffset:int = 0;
|
||||
for (var i:int = 0, count:int = inputsList.length(); i < count; i++) {
|
||||
var input:DaeInput = new DaeInput(inputsList[i], document);
|
||||
var semantic:String = input.semantic;
|
||||
if (semantic != null) {
|
||||
switch (semantic) {
|
||||
case "VERTEX" :
|
||||
if (verticesInput == null) {
|
||||
verticesInput = input;
|
||||
}
|
||||
break;
|
||||
case "TEXCOORD" :
|
||||
texCoordsInputs.push(input);
|
||||
break;
|
||||
case "NORMAL":
|
||||
if (normalsInput == null) {
|
||||
normalsInput = input;
|
||||
}
|
||||
break;
|
||||
case "TEXTANGENT":
|
||||
tangentsInputs.push(input);
|
||||
break;
|
||||
case "TEXBINORMAL":
|
||||
biNormalsInputs.push(input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var offset:int = input.offset;
|
||||
maxInputOffset = (offset > maxInputOffset) ? offset : maxInputOffset;
|
||||
}
|
||||
inputsStride = maxInputOffset + 1;
|
||||
}
|
||||
|
||||
private function parseIndices():void {
|
||||
indices = new Vector.<uint>();
|
||||
var array:Array;
|
||||
var vcount:Vector.<int> = new Vector.<int>();
|
||||
var i:int = 0;
|
||||
var count:int = 0;
|
||||
switch (data.localName()) {
|
||||
case "polylist":
|
||||
case "polygons":
|
||||
var vcountXML:XMLList = data.vcount;
|
||||
array = parseStringArray(vcountXML[0]);
|
||||
for (i = 0,count = array.length; i < count; i++) {
|
||||
vcount.push(parseInt(array[i]));
|
||||
}
|
||||
case "triangles":
|
||||
var pList:XMLList = data.p;
|
||||
for (i = 0,count = pList.length(); i < count; i++) {
|
||||
array = parseStringArray(pList[i]);
|
||||
for (var j:int = 0; j < array.length; j++) {
|
||||
indices.push(parseInt(array[j], 10));
|
||||
}
|
||||
if (vcount.length > 0) {
|
||||
indices = triangulate(indices, vcount);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function triangulate(input:Vector.<uint>, vcount:Vector.<int>):Vector.<uint> {
|
||||
var res:Vector.<uint> = new Vector.<uint>();
|
||||
var indexIn:uint, indexOut:uint = 0;
|
||||
var i:int, j:int, k:int, count:int;
|
||||
for (i = 0,count = vcount.length; i < count; i++) {
|
||||
var verticesCount:int = vcount[i];
|
||||
var attributesCount:int = verticesCount*inputsStride;
|
||||
if (verticesCount == 3) {
|
||||
for (j = 0; j < attributesCount; j++,indexIn++,indexOut++) {
|
||||
res[indexOut] = input[indexIn];
|
||||
}
|
||||
} else {
|
||||
for (j = 1; j < verticesCount - 1; j++) {
|
||||
// 0 - vertex
|
||||
for (k = 0; k < inputsStride; k++,indexOut++) {
|
||||
res[indexOut] = input[int(indexIn + k)];
|
||||
}
|
||||
// 1 - vertex
|
||||
for (k = 0; k < inputsStride; k++,indexOut++) {
|
||||
res[indexOut] = input[int(indexIn + inputsStride*j + k)];
|
||||
}
|
||||
// 2 - vertex
|
||||
for (k = 0; k < inputsStride; k++,indexOut++) {
|
||||
res[indexOut] = input[int(indexIn + inputsStride*(j + 1) + k)];
|
||||
}
|
||||
}
|
||||
indexIn += inputsStride*verticesCount;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public function fillGeometry(geometry:Geometry, vertices:Vector.<DaeVertex>):uint {
|
||||
if (verticesInput == null) {
|
||||
// Error
|
||||
return 0;
|
||||
}
|
||||
verticesInput.parse();
|
||||
|
||||
var numIndices:int = indices.length;
|
||||
|
||||
var daeVertices:DaeVertices = document.findVertices(verticesInput.source);
|
||||
if (daeVertices == null) {
|
||||
document.logger.logNotFoundError(verticesInput.source);
|
||||
return 0;
|
||||
}
|
||||
daeVertices.parse();
|
||||
|
||||
var positionSource:DaeSource = daeVertices.positions;
|
||||
var vertexStride:int = 3; // XYZ
|
||||
|
||||
var mainSource:DaeSource = positionSource;
|
||||
var mainInput:DaeInput = verticesInput;
|
||||
|
||||
var tangentSource:DaeSource;
|
||||
var binormalSource:DaeSource;
|
||||
|
||||
var channels:uint = 0;
|
||||
var normalSource:DaeSource;
|
||||
var inputOffsets:Vector.<int> = new Vector.<int>();
|
||||
inputOffsets.push(verticesInput.offset);
|
||||
if (normalsInput != null) {
|
||||
normalSource = normalsInput.prepareSource(3);
|
||||
inputOffsets.push(normalsInput.offset);
|
||||
vertexStride += 3;
|
||||
channels |= NORMALS;
|
||||
if (tangentsInputs.length > 0 && biNormalsInputs.length > 0) {
|
||||
tangentSource = tangentsInputs[0].prepareSource(3);
|
||||
inputOffsets.push(tangentsInputs[0].offset);
|
||||
binormalSource = biNormalsInputs[0].prepareSource(3);
|
||||
inputOffsets.push(biNormalsInputs[0].offset);
|
||||
vertexStride += 4;
|
||||
channels |= TANGENT4;
|
||||
}
|
||||
}
|
||||
var textureSources:Vector.<DaeSource> = new Vector.<DaeSource>();
|
||||
var numTexCoordsInputs:int = texCoordsInputs.length;
|
||||
if (numTexCoordsInputs > 8) {
|
||||
// TODO: Warning
|
||||
numTexCoordsInputs = 8;
|
||||
}
|
||||
for (var i:int = 0; i < numTexCoordsInputs; i++) {
|
||||
var s:DaeSource = texCoordsInputs[i].prepareSource(2);
|
||||
textureSources.push(s);
|
||||
inputOffsets.push(texCoordsInputs[i].offset);
|
||||
vertexStride += 2;
|
||||
channels |= TEXCOORDS[i];
|
||||
}
|
||||
|
||||
var verticesLength:int = vertices.length;
|
||||
|
||||
// Make geometry data
|
||||
var index:uint;
|
||||
var vertex:DaeVertex;
|
||||
|
||||
indexBegin = geometry._indices.length;
|
||||
for (i = 0; i < numIndices; i += inputsStride) {
|
||||
index = indices[int(i + mainInput.offset)];
|
||||
|
||||
vertex = vertices[index];
|
||||
if (vertex == null || !isEqual(vertex, indices, i, inputOffsets)) {
|
||||
if (vertex != null) {
|
||||
// Add to end
|
||||
index = verticesLength++;
|
||||
}
|
||||
vertex = new DaeVertex();
|
||||
vertices[index] = vertex;
|
||||
vertex.vertexInIndex = indices[int(i + verticesInput.offset)];
|
||||
vertex.addPosition(positionSource.numbers, vertex.vertexInIndex, positionSource.stride, document.unitScaleFactor);
|
||||
|
||||
if (normalSource != null) {
|
||||
vertex.addNormal(normalSource.numbers, indices[int(i + normalsInput.offset)], normalSource.stride);
|
||||
|
||||
}
|
||||
if (tangentSource != null) {
|
||||
vertex.addTangentBiDirection(tangentSource.numbers, indices[int(i + tangentsInputs[0].offset)], tangentSource.stride, binormalSource.numbers, indices[int(i + biNormalsInputs[0].offset)], binormalSource.stride);
|
||||
}
|
||||
for (var j:int = 0; j < textureSources.length; j++) {
|
||||
vertex.appendUV(textureSources[j].numbers, indices[int(i + texCoordsInputs[j].offset)], textureSources[j].stride);
|
||||
}
|
||||
}
|
||||
vertex.vertexOutIndex = index;
|
||||
geometry._indices.push(index);
|
||||
}
|
||||
numTriangles = (geometry._indices.length - indexBegin)/3;
|
||||
return channels;
|
||||
}
|
||||
|
||||
private function isEqual(vertex:DaeVertex, indices:Vector.<uint>, index:int, offsets:Vector.<int>):Boolean {
|
||||
var numOffsets:int = offsets.length;
|
||||
for (var j:int = 0; j < numOffsets; j++) {
|
||||
if (vertex.indices[j] != indices[int(index + offsets[j])]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function findInputBySet(inputs:Vector.<DaeInput>, setNum:int):DaeInput {
|
||||
for (var i:int = 0, numInputs:int = inputs.length; i < numInputs; i++) {
|
||||
var input:DaeInput = inputs[i];
|
||||
if (input.setNum == setNum) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of texture channels data. First element stores channel with mainSetNum.
|
||||
*/
|
||||
private function getTexCoordsDatas(mainSetNum:int):Vector.<VertexChannelData> {
|
||||
var mainInput:DaeInput = findInputBySet(texCoordsInputs, mainSetNum);
|
||||
var i:int;
|
||||
var numInputs:int = texCoordsInputs.length;
|
||||
var datas:Vector.<VertexChannelData> = new Vector.<VertexChannelData>();
|
||||
for (i = 0; i < numInputs; i++) {
|
||||
var texCoordsInput:DaeInput = texCoordsInputs[i];
|
||||
var texCoordsSource:DaeSource = texCoordsInput.prepareSource(2);
|
||||
if (texCoordsSource != null) {
|
||||
var data:VertexChannelData = new VertexChannelData(texCoordsSource.numbers, texCoordsSource.stride, texCoordsInput.offset, texCoordsInput.setNum);
|
||||
if (texCoordsInput == mainInput) {
|
||||
datas.unshift(data);
|
||||
} else {
|
||||
datas.push(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (datas.length > 0) ? datas : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare vertices of the privitive with given at <code>otherVertices</code> parameter vertices.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public function verticesEquals(otherVertices:DaeVertices):Boolean {
|
||||
var vertices:DaeVertices = document.findVertices(verticesInput.source);
|
||||
if (vertices == null) {
|
||||
document.logger.logNotFoundError(verticesInput.source);
|
||||
}
|
||||
return vertices == otherVertices;
|
||||
}
|
||||
|
||||
public function get materialSymbol():String {
|
||||
var attr:XML = data.@material[0];
|
||||
return (attr == null) ? null : attr.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import flash.geom.Point;
|
||||
|
||||
class VertexChannelData {
|
||||
public var values:Vector.<Number>;
|
||||
public var stride:int;
|
||||
public var offset:int;
|
||||
public var index:int;
|
||||
public var channel:Vector.<Point>;
|
||||
public var inputSet:int;
|
||||
|
||||
public function VertexChannelData(values:Vector.<Number>, stride:int, offset:int, inputSet:int = 0) {
|
||||
this.values = values;
|
||||
this.stride = stride;
|
||||
this.offset = offset;
|
||||
this.inputSet = inputSet;
|
||||
}
|
||||
|
||||
}
|
||||
118
src/alternativa/engine3d/loaders/collada/DaeSampler.as
Normal file
118
src/alternativa/engine3d/loaders/collada/DaeSampler.as
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.animation.keys.NumberTrack;
|
||||
import alternativa.engine3d.animation.keys.Track;
|
||||
import alternativa.engine3d.animation.keys.TransformTrack;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace collada;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeSampler extends DaeElement {
|
||||
|
||||
private var times:Vector.<Number>;
|
||||
private var values:Vector.<Number>;
|
||||
private var timesStride:int;
|
||||
private var valuesStride:int;
|
||||
|
||||
public function DaeSampler(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
var inputsList:XMLList = data.input;
|
||||
|
||||
var inputSource:DaeSource;
|
||||
var outputSource:DaeSource;
|
||||
for (var i:int = 0, count:int = inputsList.length(); i < count; i++) {
|
||||
var input:DaeInput = new DaeInput(inputsList[i], document);
|
||||
var semantic:String = input.semantic;
|
||||
if (semantic != null) {
|
||||
switch (semantic) {
|
||||
case "INPUT" :
|
||||
inputSource = input.prepareSource(1);
|
||||
if (inputSource != null) {
|
||||
times = inputSource.numbers;
|
||||
timesStride = inputSource.stride;
|
||||
}
|
||||
break;
|
||||
case "OUTPUT" :
|
||||
outputSource = input.prepareSource(1);
|
||||
if (outputSource != null) {
|
||||
values = outputSource.numbers;
|
||||
valuesStride = outputSource.stride;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function parseNumbersTrack(objectName:String, property:String):NumberTrack {
|
||||
if (times != null && values != null && timesStride > 0) {
|
||||
var track:NumberTrack = new NumberTrack(objectName, property);
|
||||
var count:int = times.length/timesStride;
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
track.addKey(times[int(timesStride*i)], values[int(valuesStride*i)]);
|
||||
}
|
||||
// TODO:: Exceptions with indices
|
||||
return track;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function parseTransformationTrack(objectName:String):Track {
|
||||
if (times != null && values != null && timesStride != 0) {
|
||||
var track:TransformTrack = new TransformTrack(objectName);
|
||||
var count:int = times.length/timesStride;
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var index:int = valuesStride*i;
|
||||
var matrix:Matrix3D = new Matrix3D(Vector.<Number>([values[index], values[index + 4], values[index + 8], values[index + 12],
|
||||
values[index + 1], values[index + 5], values[index + 9], values[index + 13],
|
||||
values[index + 2], values[index + 6], values[index + 10], values[index + 14],
|
||||
values[index + 3] ,values[index + 7], values[index + 11], values[index + 15]]));
|
||||
track.addKey(times[i*timesStride], matrix);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function parsePointsTracks(objectName:String, xProperty:String, yProperty:String, zProperty:String):Vector.<Track> {
|
||||
if (times != null && values != null && timesStride != 0) {
|
||||
var xTrack:NumberTrack = new NumberTrack(objectName, xProperty);
|
||||
xTrack.object = objectName;
|
||||
var yTrack:NumberTrack = new NumberTrack(objectName, yProperty);
|
||||
yTrack.object = objectName;
|
||||
var zTrack:NumberTrack = new NumberTrack(objectName, zProperty);
|
||||
zTrack.object = objectName;
|
||||
var count:int = times.length/timesStride;
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var index:int = i*valuesStride;
|
||||
var time:Number = times[i*timesStride];
|
||||
xTrack.addKey(time, values[index]);
|
||||
yTrack.addKey(time, values[index + 1]);
|
||||
zTrack.addKey(time, values[index + 2]);
|
||||
}
|
||||
return Vector.<Track>([xTrack, yTrack, zTrack]);
|
||||
// TODO:: Exceptions with indices
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
164
src/alternativa/engine3d/loaders/collada/DaeSource.as
Normal file
164
src/alternativa/engine3d/loaders/collada/DaeSource.as
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeSource extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
/**
|
||||
* Types of arrays.
|
||||
*/
|
||||
private const FLOAT_ARRAY:String = "float_array";
|
||||
private const INT_ARRAY:String = "int_array";
|
||||
private const NAME_ARRAY:String = "Name_array";
|
||||
|
||||
/**
|
||||
* Array of Number items.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var numbers:Vector.<Number>;
|
||||
/**
|
||||
* Array of int items.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var ints:Vector.<int>;
|
||||
/**
|
||||
* Array of string items.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
|
||||
public var names:Vector.<String>;
|
||||
/**
|
||||
* Size in bytes of one number or int element
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var stride:int;
|
||||
|
||||
public function DaeSource(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
|
||||
// array declares within in <source>.
|
||||
constructArrays();
|
||||
}
|
||||
|
||||
private function constructArrays():void {
|
||||
var children:XMLList = data.children();
|
||||
for (var i:int = 0, count:int = children.length(); i < count; i++) {
|
||||
var child:XML = children[i];
|
||||
switch (child.localName()) {
|
||||
case FLOAT_ARRAY :
|
||||
case INT_ARRAY :
|
||||
case NAME_ARRAY :
|
||||
var array:DaeArray = new DaeArray(child, document);
|
||||
if (array.id != null) {
|
||||
document.arrays[array.id] = array;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get accessor():XML {
|
||||
return data.technique_common.accessor[0];
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
var accessor:XML = this.accessor;
|
||||
if (accessor != null) {
|
||||
var arrayXML:XML = accessor.@source[0];
|
||||
var array:DaeArray = (arrayXML == null) ? null : document.findArray(arrayXML);
|
||||
if (array != null) {
|
||||
var countXML:String = accessor.@count[0];
|
||||
if (countXML != null) {
|
||||
var count:int = parseInt(countXML.toString(), 10);
|
||||
var offsetXML:XML = accessor.@offset[0];
|
||||
var strideXML:XML = accessor.@stride[0];
|
||||
var offset:int = (offsetXML == null) ? 0 : parseInt(offsetXML.toString(), 10);
|
||||
var stride:int = (strideXML == null) ? 1 : parseInt(strideXML.toString(), 10);
|
||||
array.parse();
|
||||
if (array.array.length < (offset + (count*stride))) {
|
||||
document.logger.logNotEnoughDataError(accessor);
|
||||
return false;
|
||||
}
|
||||
this.stride = parseArray(offset, count, stride, array.array, array.type);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
document.logger.logNotFoundError(arrayXML);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function numValidParams(params:XMLList):int {
|
||||
var res:int = 0;
|
||||
for (var i:int = 0, count:int = params.length(); i < count; i++) {
|
||||
if (params[i].@name[0] != null) {
|
||||
res++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function parseArray(offset:int, count:int, stride:int, array:Array, type:String):int {
|
||||
var params:XMLList = this.accessor.param;
|
||||
var arrStride:int = Math.max(numValidParams(params), stride);
|
||||
switch (type) {
|
||||
case FLOAT_ARRAY:
|
||||
numbers = new Vector.<Number>(int(arrStride*count));
|
||||
break;
|
||||
case INT_ARRAY:
|
||||
ints = new Vector.<int>(int(arrStride*count));
|
||||
break;
|
||||
case NAME_ARRAY:
|
||||
names = new Vector.<String>(int(arrStride*count));
|
||||
break;
|
||||
}
|
||||
var curr:int = 0;
|
||||
for (var i:int = 0; i < arrStride; i++) {
|
||||
// Only parameters which have name field should be read
|
||||
var param:XML = params[i];
|
||||
if (param == null || param.hasOwnProperty("@name")) {
|
||||
var j:int;
|
||||
switch (type) {
|
||||
case FLOAT_ARRAY:
|
||||
for (j = 0; j < count; j++) {
|
||||
var value:String = array[int(offset + stride*j + i)];
|
||||
if (value.indexOf(",") != -1) {
|
||||
value = value.replace(/,/, ".");
|
||||
}
|
||||
numbers[int(arrStride*j + curr)] = parseFloat(value);
|
||||
}
|
||||
break;
|
||||
case INT_ARRAY:
|
||||
for (j = 0; j < count; j++) {
|
||||
ints[int(arrStride*j + curr)] = parseInt(array[int(offset + stride*j + i)], 10);
|
||||
}
|
||||
break;
|
||||
case NAME_ARRAY:
|
||||
for (j = 0; j < count; j++) {
|
||||
names[int(arrStride*j + curr)] = array[int(offset + stride*j + i)];
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
curr++;
|
||||
}
|
||||
}
|
||||
return arrStride;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
26
src/alternativa/engine3d/loaders/collada/DaeUnits.as
Normal file
26
src/alternativa/engine3d/loaders/collada/DaeUnits.as
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeUnits {
|
||||
public static const METERS:Number = 1;
|
||||
public static const DECIMETERS:Number = 0.1;
|
||||
public static const CENTIMETERS:Number = 0.01;
|
||||
public static const MILIMETERS:Number = 0.001;
|
||||
public static const KILOMETERS:Number = 1000;
|
||||
public static const INCHES:Number = 0.0254;
|
||||
public static const FEET:Number = 0.3048;
|
||||
public static const YARDS:Number = 0.9144;
|
||||
public static const MILES:Number = 1609.347219;
|
||||
}
|
||||
}
|
||||
76
src/alternativa/engine3d/loaders/collada/DaeVertex.as
Normal file
76
src/alternativa/engine3d/loaders/collada/DaeVertex.as
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeVertex {
|
||||
|
||||
public var vertexInIndex:int;
|
||||
public var vertexOutIndex:int;
|
||||
|
||||
public var indices:Vector.<int> = new Vector.<int>();
|
||||
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
public var z:Number;
|
||||
|
||||
public var uvs:Vector.<Number> = new Vector.<Number>();
|
||||
|
||||
public var normal:Vector3D;
|
||||
public var tangent:Vector3D;
|
||||
|
||||
public function addPosition(data:Vector.<Number>, dataIndex:int, stride:int, unitScaleFactor:Number):void {
|
||||
indices.push(dataIndex);
|
||||
var offset:int = stride*dataIndex;
|
||||
x = data[int(offset)]*unitScaleFactor;
|
||||
y = data[int(offset + 1)]*unitScaleFactor;
|
||||
z = data[int(offset + 2)]*unitScaleFactor;
|
||||
}
|
||||
|
||||
public function addNormal(data:Vector.<Number>, dataIndex:int, stride:int):void {
|
||||
indices.push(dataIndex);
|
||||
var offset:int = stride*dataIndex;
|
||||
normal = new Vector3D();
|
||||
normal.x = data[int(offset++)];
|
||||
normal.y = data[int(offset++)];
|
||||
normal.z = data[offset];
|
||||
}
|
||||
|
||||
public function addTangentBiDirection(tangentData:Vector.<Number>, tangentDataIndex:int, tangentStride:int, biNormalData:Vector.<Number>, biNormalDataIndex:int, biNormalStride:int):void {
|
||||
indices.push(tangentDataIndex);
|
||||
indices.push(biNormalDataIndex);
|
||||
var tangentOffset:int = tangentStride*tangentDataIndex;
|
||||
var biNormalOffset:int = biNormalStride*biNormalDataIndex;
|
||||
|
||||
var biNormalX:Number = biNormalData[int(biNormalOffset++)];
|
||||
var biNormalY:Number = biNormalData[int(biNormalOffset++)];
|
||||
var biNormalZ:Number = biNormalData[biNormalOffset];
|
||||
|
||||
tangent = new Vector3D(tangentData[int(tangentOffset++)], tangentData[int(tangentOffset++)], tangentData[tangentOffset]);
|
||||
|
||||
var crossX:Number = normal.y*tangent.z - normal.z*tangent.y;
|
||||
var crossY:Number = normal.z*tangent.x - normal.x*tangent.z;
|
||||
var crossZ:Number = normal.x*tangent.y - normal.y*tangent.x;
|
||||
var dot:Number = crossX*biNormalX + crossY*biNormalY + crossZ*biNormalZ;
|
||||
tangent.w = dot < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
public function appendUV(data:Vector.<Number>, dataIndex:int, stride:int):void {
|
||||
indices.push(dataIndex);
|
||||
uvs.push(data[int(dataIndex*stride)]);
|
||||
uvs.push(1 - data[int(dataIndex*stride + 1)]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeVertexChannels {
|
||||
|
||||
public var uvChannels:Vector.<int> = new Vector.<int>();
|
||||
public var normalIndex:int;
|
||||
public var tangentIndex:int;
|
||||
public var biNormalIndex:int;
|
||||
|
||||
public function DaeVertexChannels() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
49
src/alternativa/engine3d/loaders/collada/DaeVertices.as
Normal file
49
src/alternativa/engine3d/loaders/collada/DaeVertices.as
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeVertices extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
/**
|
||||
* Source of vertex coordinates data. Stores coordinates in <code>numbers</code> array.
|
||||
*<code>stride</code> property of source is not less than three.
|
||||
* Call <code>parse()</code> before using.
|
||||
*/
|
||||
public var positions:DaeSource;
|
||||
//private var texCoords:Vector.<DaeSource>;
|
||||
|
||||
public function DaeVertices(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
}
|
||||
|
||||
override protected function parseImplementation():Boolean {
|
||||
// Get array of vertex coordinates.
|
||||
var inputXML:XML = data.input.(@semantic == "POSITION")[0];
|
||||
if (inputXML != null) {
|
||||
positions = (new DaeInput(inputXML, document)).prepareSource(3);
|
||||
if (positions != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
43
src/alternativa/engine3d/loaders/collada/DaeVisualScene.as
Normal file
43
src/alternativa/engine3d/loaders/collada/DaeVisualScene.as
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class DaeVisualScene extends DaeElement {
|
||||
|
||||
use namespace collada;
|
||||
|
||||
public var nodes:Vector.<DaeNode>;
|
||||
|
||||
public function DaeVisualScene(data:XML, document:DaeDocument) {
|
||||
super(data, document);
|
||||
|
||||
// nodes are declared in <visual_scene>.
|
||||
constructNodes();
|
||||
}
|
||||
|
||||
public function constructNodes():void {
|
||||
var nodesList:XMLList = data.node;
|
||||
var count:int = nodesList.length();
|
||||
nodes = new Vector.<DaeNode>(count);
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
var node:DaeNode = new DaeNode(nodesList[i], document, this);
|
||||
if (node.id != null) {
|
||||
document.nodes[node.id] = node;
|
||||
}
|
||||
nodes[i] = node;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
17
src/alternativa/engine3d/loaders/collada/collada.as
Normal file
17
src/alternativa/engine3d/loaders/collada/collada.as
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public namespace collada = "http://www.collada.org/2005/11/COLLADASchema";
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.loaders.events {
|
||||
|
||||
import alternativa.engine3d.resources.ExternalTextureResource;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* The Event is dispatched when <code>TexturesLoader</code> complete loading of textures.
|
||||
*
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader
|
||||
*/
|
||||
public class TexturesLoaderEvent extends Event {
|
||||
/**
|
||||
* Value for <code>type</code> property .
|
||||
*/
|
||||
public static const COMPLETE:String = "complete";
|
||||
|
||||
private var bitmapDatas:Vector.<BitmapData>;
|
||||
private var textures:Vector.<ExternalTextureResource>;
|
||||
|
||||
public function TexturesLoaderEvent(type:String, bitmapDatas:Vector.<BitmapData>, textures:Vector.<ExternalTextureResource>) {
|
||||
this.bitmapDatas = bitmapDatas;
|
||||
this.textures = textures;
|
||||
super(type, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of loaded images. Method returns the list only when method <code>TexturesLoader.loadResource()</code> is called
|
||||
* and <code>TexturesLoader.loadResources()</code> with <code>needBitmapData</code> is set to <code>true</code>.
|
||||
*
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader#loadResource()
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader#loadResources()
|
||||
*/
|
||||
public function getBitmapDatas():Vector.<BitmapData> {
|
||||
return bitmapDatas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of loaded textures. Method returns the list only when method <code>TexturesLoader.loadResource()</code> is called
|
||||
* and <code>TexturesLoader.loadResources()</code> with <code>needBitmapData</code> is set to <code>true</code>.
|
||||
*
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader#loadResource()
|
||||
* @see alternativa.engine3d.loaders.TexturesLoader#loadResources()
|
||||
* @see alternativa.engine3d.resources.ExternalTextureResource
|
||||
*/
|
||||
public function getTextures():Vector.<ExternalTextureResource> {
|
||||
return textures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns copy of object.
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new TexturesLoaderEvent(type, bitmapDatas, textures);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
343
src/alternativa/engine3d/materials/A3DUtils.as
Normal file
343
src/alternativa/engine3d/materials/A3DUtils.as
Normal file
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.materials {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.materials.compiler.CommandType;
|
||||
import alternativa.engine3d.materials.compiler.VariableType;
|
||||
|
||||
import avmplus.getQualifiedSuperclassName;
|
||||
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DTextureFormat;
|
||||
import flash.display3D.IndexBuffer3D;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.display3D.textures.Texture;
|
||||
import flash.geom.Point;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Endian;
|
||||
import flash.utils.getDefinitionByName;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class A3DUtils {
|
||||
|
||||
public static const NONE:int = 0;
|
||||
public static const DXT1:int = 1;
|
||||
public static const ETC1:int = 2;
|
||||
public static const PVRTC:int = 3;
|
||||
|
||||
private static const DXT1Data:ByteArray = getDXT1();
|
||||
private static const PVRTCData:ByteArray = getPVRTC();
|
||||
private static const ETC1Data:ByteArray = getETC1();
|
||||
|
||||
private static function getDXT1():ByteArray {
|
||||
var DXT1Data:Vector.<int> = Vector.<int>([65,84,70,0,2,71,2,2,2,3,0,0,12,0,0,0,16,0,0,85,105,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,10,87,77,80,72,79,84,79,0,25,0,192,122,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,224,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,85,105,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,10,87,77,80,72,79,84,79,0,25,0,192,122,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,224,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,85,105,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,10,87,77,80,72,79,84,79,0,25,0,192,122,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,224,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,7,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
|
||||
return getData(DXT1Data);
|
||||
}
|
||||
|
||||
private static function getETC1():ByteArray {
|
||||
var ETC1Data:Vector.<int> = Vector.<int>([65,84,70,0,2,104,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,16,0,0,0,255,252,0,0,0,0,12,0,0,0,16,0,0,127,233,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,208,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,7,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,16,0,0,0,255,252,0,0,0,0,12,0,0,0,16,0,0,127,233,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,208,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,16,0,0,0,255,252,0,0,0,0,12,0,0,0,16,0,0,127,233,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,208,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,4,0]);
|
||||
return getData(ETC1Data);
|
||||
}
|
||||
|
||||
private static function getPVRTC():ByteArray {
|
||||
var PVRTCData:Vector.<int> = Vector.<int>([65,84,70,0,2,173,2,2,2,3,0,0,0,0,0,0,0,0,13,0,0,0,16,0,0,0,104,190,153,255,0,0,0,0,15,91,0,0,16,0,0,102,12,228,2,255,225,0,0,0,0,0,223,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,2,0,0,0,129,188,4,0,1,0,0,0,4,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,132,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,1,0,3,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,165,192,0,7,227,99,186,53,197,40,185,134,182,32,130,98,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,192,120,64,6,16,34,52,192,196,65,132,90,98,68,16,17,68,60,91,8,48,76,35,192,97,132,71,76,33,164,97,1,2,194,12,19,8,240,29,132,24,38,17,224,48,194,35,166,16,210,48,128,128,24,68,121,132,52,204,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,0,233,56,90,0,0,0,0,12,0,0,0,16,0,0,127,237,210,0,0,0,0,0,155,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,2,0,0,0,129,188,4,0,1,0,0,0,4,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,64,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,1,0,3,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,0,233,56,90,0,0,0,0,12,0,0,0,16,0,0,127,237,210,0,0,0,0,0,155,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,2,0,0,0,129,188,4,0,1,0,0,0,4,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,64,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,1,0,3,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,200,0,0,0,0,0,0,0,0,0,0]);
|
||||
return getData(PVRTCData);
|
||||
}
|
||||
|
||||
private static function getData(source:Vector.<int>):ByteArray {
|
||||
var result:ByteArray = new ByteArray();
|
||||
for (var i:int = 0, length:int = source.length; i < length; i++) {
|
||||
result.writeByte(source[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static function getSizeFromATF(byteArray:ByteArray, size:Point):void {
|
||||
byteArray.position = 7;
|
||||
var w:int = byteArray.readByte();
|
||||
var h:int = byteArray.readByte();
|
||||
size.x = 1 << w;
|
||||
size.y = 1 << h;
|
||||
byteArray.position = 0;
|
||||
}
|
||||
|
||||
public static function getSupportedTextureFormat(context3D:Context3D):int {
|
||||
var testTexture:Texture = context3D.createTexture(4, 4, Context3DTextureFormat.COMPRESSED, false);
|
||||
var result:int = NONE;
|
||||
try {
|
||||
testTexture.uploadCompressedTextureFromByteArray(DXT1Data, 0);
|
||||
result = DXT1;
|
||||
} catch(e:Error) {
|
||||
result = NONE;
|
||||
}
|
||||
if (result == NONE) {
|
||||
try {
|
||||
testTexture.uploadCompressedTextureFromByteArray(PVRTCData, 0);
|
||||
result = PVRTC;
|
||||
} catch(e:Error) {
|
||||
result = NONE;
|
||||
}
|
||||
}
|
||||
if (result == NONE) {
|
||||
try {
|
||||
testTexture.uploadCompressedTextureFromByteArray(ETC1Data, 0);
|
||||
result = ETC1;
|
||||
} catch(e:Error) {
|
||||
result = NONE;
|
||||
}
|
||||
}
|
||||
testTexture.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static function vectorNumberToByteArray(vector:Vector.<Number>):ByteArray {
|
||||
var result:ByteArray = new ByteArray();
|
||||
result.endian = Endian.LITTLE_ENDIAN;
|
||||
for (var i:int = 0; i < vector.length; i++) {
|
||||
result.writeFloat(vector[i]);
|
||||
}
|
||||
result.position = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static function byteArrayToVectorUint(byteArray:ByteArray):Vector.<uint> {
|
||||
var result:Vector.<uint> = new Vector.<uint>();
|
||||
var length:uint = 0;
|
||||
byteArray.position = 0;
|
||||
byteArray.endian = Endian.LITTLE_ENDIAN;
|
||||
while (byteArray.bytesAvailable > 0) {
|
||||
result[length++] = byteArray.readUnsignedShort();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static function createVertexBufferFromByteArray(context:Context3D, byteArray:ByteArray, numVertices:uint, stride:uint = 3):VertexBuffer3D {
|
||||
if (context == null) {
|
||||
throw new ReferenceError("context is not set");
|
||||
}
|
||||
var buffer:VertexBuffer3D = context.createVertexBuffer(numVertices, stride);
|
||||
buffer.uploadFromByteArray(byteArray, 0, 0, numVertices);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static function createVertexBufferFromVector(context:Context3D, vector:Vector.<Number>, numVertices:uint, stride:uint = 3):VertexBuffer3D {
|
||||
if (context == null) {
|
||||
throw new ReferenceError("context is not set");
|
||||
}
|
||||
var buffer:VertexBuffer3D = context.createVertexBuffer(numVertices, stride);
|
||||
|
||||
var byteArray:ByteArray = A3DUtils.vectorNumberToByteArray(vector);
|
||||
buffer.uploadFromByteArray(byteArray, 0, 0, numVertices);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static function createTextureFromByteArray(context:Context3D, byteArray:ByteArray, width:Number, height:Number, format:String):Texture {
|
||||
if (context == null) {
|
||||
throw new ReferenceError("context is not set");
|
||||
}
|
||||
var texture:Texture = context.createTexture(width, height, format, false);
|
||||
texture.uploadCompressedTextureFromByteArray(byteArray, 0);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public static function createIndexBufferFromByteArray(context:Context3D, byteArray:ByteArray, numIndices:uint):IndexBuffer3D {
|
||||
if (context == null) {
|
||||
throw new ReferenceError("context is not set");
|
||||
}
|
||||
var buffer:IndexBuffer3D = context.createIndexBuffer(numIndices);
|
||||
buffer.uploadFromByteArray(byteArray, 0, 0, numIndices);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static function createIndexBufferFromVector(context:Context3D, vector:Vector.<uint>, numIndices:int = -1):IndexBuffer3D {
|
||||
if (context == null) {
|
||||
throw new ReferenceError("context is not set");
|
||||
}
|
||||
var count:uint = numIndices > 0 ? numIndices : vector.length;
|
||||
var buffer:IndexBuffer3D = context.createIndexBuffer(count);
|
||||
buffer.uploadFromVector(vector, 0, count);
|
||||
|
||||
var byteArray:ByteArray = new ByteArray();
|
||||
byteArray.endian = Endian.LITTLE_ENDIAN;
|
||||
for (var i:int = 0; i < count; i++) {
|
||||
byteArray.writeInt(vector[i]);
|
||||
}
|
||||
byteArray.position = 0;
|
||||
|
||||
buffer.uploadFromVector(vector, 0, count);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Disassembler
|
||||
private static var programType:Vector.<String> = Vector.<String>(["VERTEX", "FRAGMENT"]);
|
||||
private static var samplerDimension:Vector.<String> = Vector.<String>(["2D", "cube", "3D"]);
|
||||
private static var samplerWraping:Vector.<String> = Vector.<String>(["clamp", "repeat"]);
|
||||
private static var samplerMipmap:Vector.<String> = Vector.<String>(["mipnone", "mipnearest", "miplinear"]);
|
||||
private static var samplerFilter:Vector.<String> = Vector.<String>(["nearest", "linear"]);
|
||||
private static var swizzleType:Vector.<String> = Vector.<String>(["x", "y", "z", "w"]);
|
||||
private static var twoOperandsCommands:Dictionary;
|
||||
private static const O_CODE:uint = "o".charCodeAt(0);
|
||||
|
||||
public static function disassemble(byteCode:ByteArray):String {
|
||||
if (!twoOperandsCommands) {
|
||||
twoOperandsCommands = new Dictionary();
|
||||
twoOperandsCommands[0x1] = true;
|
||||
twoOperandsCommands[0x2] = true;
|
||||
twoOperandsCommands[0x3] = true;
|
||||
twoOperandsCommands[0x4] = true;
|
||||
twoOperandsCommands[0x6] = true;
|
||||
twoOperandsCommands[0xb] = true;
|
||||
twoOperandsCommands[0x11] = true;
|
||||
twoOperandsCommands[0x12] = true;
|
||||
twoOperandsCommands[0x13] = true;
|
||||
twoOperandsCommands[0x17] = true;
|
||||
twoOperandsCommands[0x18] = true;
|
||||
twoOperandsCommands[0x19] = true;
|
||||
twoOperandsCommands[0x28] = true;
|
||||
twoOperandsCommands[0x29] = true;
|
||||
twoOperandsCommands[0x2a] = true;
|
||||
twoOperandsCommands[0x2c] = true;
|
||||
twoOperandsCommands[0x2d] = true;
|
||||
}
|
||||
var res:String = "";
|
||||
byteCode.position = 0;
|
||||
if (byteCode.bytesAvailable < 7) {
|
||||
return "error in byteCode header";
|
||||
}
|
||||
|
||||
res += "magic = " + byteCode.readUnsignedByte().toString(16);
|
||||
res += "\nversion = " + byteCode.readInt().toString(10);
|
||||
res += "\nshadertypeid = " + byteCode.readUnsignedByte().toString(16);
|
||||
var pType:String = programType[byteCode.readByte()];
|
||||
res += "\nshadertype = " + pType;
|
||||
res += "\nsource\n";
|
||||
pType = pType.substring(0, 1).toLowerCase();
|
||||
var lineNumber:uint = 1;
|
||||
while (byteCode.bytesAvailable - 24 >= 0) {
|
||||
res += (lineNumber++).toString() + ": " + getCommand(byteCode, pType) + "\n";
|
||||
}
|
||||
if (byteCode.bytesAvailable > 0) {
|
||||
res += "\nunexpected byteCode length. extra bytes:" + byteCode.bytesAvailable;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static function getCommand(byteCode:ByteArray, programType:String):String {
|
||||
|
||||
var cmd:uint = byteCode.readUnsignedInt();
|
||||
var command:String = CommandType.COMMAND_NAMES[cmd];
|
||||
var result:String;
|
||||
var destNumber:uint = byteCode.readUnsignedShort();
|
||||
var swizzle:uint = byteCode.readByte();
|
||||
var s:String = "";
|
||||
var destSwizzle:uint = 0;
|
||||
if (swizzle < 15) {
|
||||
s += ".";
|
||||
s += ((swizzle & 0x1) > 0) ? "x" : "";
|
||||
s += ((swizzle & 0x2) > 0) ? "y" : "";
|
||||
s += ((swizzle & 0x4) > 0) ? "z" : "";
|
||||
s += ((swizzle & 0x8) > 0) ? "w" : "";
|
||||
destSwizzle = s.length;
|
||||
}
|
||||
|
||||
var destType:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0);
|
||||
if (destType.charCodeAt(0) == O_CODE) {
|
||||
result = command + " " + attachProgramPrefix(destType, programType) + s + ", ";
|
||||
} else {
|
||||
result = command + " " + attachProgramPrefix(destType, programType) + destNumber.toString() + s + ", ";
|
||||
}
|
||||
|
||||
result += attachProgramPrefix(getSourceVariable(byteCode, destSwizzle), programType);
|
||||
|
||||
if (twoOperandsCommands[cmd]) {
|
||||
if (cmd == 0x28) {
|
||||
result += ", " + attachProgramPrefix(getSamplerVariable(byteCode), programType);
|
||||
}
|
||||
else {
|
||||
result += ", " + attachProgramPrefix(getSourceVariable(byteCode, destSwizzle), programType);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
byteCode.readDouble();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static function attachProgramPrefix(variable:String, programType:String):String {
|
||||
var char:uint = variable.charCodeAt(0);
|
||||
if (char == "o".charCodeAt(0))
|
||||
return variable + (programType == "f" ? "c" : "p");
|
||||
else if (char != "v".charCodeAt(0))
|
||||
return programType + variable;
|
||||
return variable;
|
||||
}
|
||||
|
||||
private static function getSamplerVariable(byteCode:ByteArray):String {
|
||||
var number:uint = byteCode.readUnsignedInt();
|
||||
byteCode.readByte();
|
||||
var dim:uint = byteCode.readByte() >> 4;
|
||||
var wraping:uint = byteCode.readByte() >> 4;
|
||||
var n:uint = byteCode.readByte();
|
||||
return "s" + number.toString() + " <" + samplerDimension[dim] + ", " + samplerWraping[wraping]
|
||||
+ ", " + samplerFilter[(n >> 4) & 0xf] + ", " + samplerMipmap[n & 0xf] + ">";
|
||||
}
|
||||
|
||||
private static function getSourceVariable(byteCode:ByteArray, destSwizzle:uint):String {
|
||||
var s1Number:uint = byteCode.readUnsignedShort();
|
||||
var offset:uint = byteCode.readUnsignedByte();
|
||||
var s:String = getSourceSwizzle(byteCode.readUnsignedByte(), destSwizzle);
|
||||
|
||||
var s1Type:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0);
|
||||
var indexType:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0);
|
||||
var comp:String = swizzleType[byteCode.readUnsignedByte()];
|
||||
|
||||
if (byteCode.readUnsignedByte() > 0) {
|
||||
return s1Type + "[" + indexType + s1Number.toString() + "." + comp + ((offset > 0) ? ("+" + offset.toString()) : "") + "]" + s;
|
||||
}
|
||||
return s1Type + s1Number.toString() + s;
|
||||
}
|
||||
|
||||
private static function getSourceSwizzle(swizzle:uint, destSwizzle:uint):String {
|
||||
var s:String = "";
|
||||
if (swizzle != 0xe4) {
|
||||
s += ".";
|
||||
s += swizzleType[(swizzle & 0x3)];
|
||||
s += swizzleType[(swizzle >> 2) & 0x3];
|
||||
s += swizzleType[(swizzle >> 4) & 0x3];
|
||||
s += swizzleType[(swizzle >> 6) & 0x3];
|
||||
s = s.substring(0, destSwizzle > 0 ? destSwizzle : s.length);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
alternativa3d static function checkParent(child:Class, parent:Class):Boolean {
|
||||
var current:Class = child;
|
||||
if (parent == null) return true;
|
||||
while (true) {
|
||||
if (current == parent) return true;
|
||||
var className:String = getQualifiedSuperclassName(current);
|
||||
if (className != null) {
|
||||
current = getDefinitionByName(className) as Class;
|
||||
} else return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
922
src/alternativa/engine3d/materials/EnvironmentMaterial.as
Normal file
922
src/alternativa/engine3d/materials/EnvironmentMaterial.as
Normal file
@@ -0,0 +1,922 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.materials {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.DrawUnit;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Renderer;
|
||||
import alternativa.engine3d.core.Transform3D;
|
||||
import alternativa.engine3d.core.VertexAttributes;
|
||||
import alternativa.engine3d.materials.compiler.Linker;
|
||||
import alternativa.engine3d.materials.compiler.Procedure;
|
||||
import alternativa.engine3d.materials.compiler.VariableType;
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
import alternativa.engine3d.resources.BitmapTextureResource;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
import alternativa.engine3d.resources.TextureResource;
|
||||
|
||||
import avmplus.getQualifiedClassName;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DBlendFactor;
|
||||
import flash.display3D.Context3DProgramType;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.display3D.textures.CubeTexture;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getDefinitionByName;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* The material which reflects the environment given with cube texture.
|
||||
*
|
||||
* @see alternativa.engine3d.resources.BitmapCubeTextureResource
|
||||
* @see alternativa.engine3d.resources.ExternalTextureResource
|
||||
*/
|
||||
public class EnvironmentMaterial extends TextureMaterial {
|
||||
|
||||
private static var caches:Dictionary = new Dictionary(true);
|
||||
private var cachedContext3D:Context3D;
|
||||
private var programsCache:Array;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogMode:int = FogMode.DISABLED;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogNear:Number = 1000;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogFar:Number = 5000;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogMaxDensity:Number = 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogColorR:Number = 0xC8/255;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogColorG:Number = 0xA2/255;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogColorB:Number = 0xC8/255;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d static var fogTexture:TextureResource;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const _passReflectionProcedure:Procedure = new Procedure([
|
||||
// i0 = position, i1 = normal
|
||||
"#v1=vNormal",
|
||||
"#v0=vPosition",
|
||||
"mov v0, i0",
|
||||
"mov v1, i1"
|
||||
], "passReflectionProcedure");
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const _applyReflectionProcedure:Procedure = getApplyReflectionProcedure();
|
||||
|
||||
private static function getApplyReflectionProcedure():Procedure {
|
||||
var result:Procedure = new Procedure([
|
||||
"#v1=vNormal",
|
||||
"#v0=vPosition",
|
||||
"#s0=sCubeMap",
|
||||
"#c0=cCamera",
|
||||
"sub t0, v0, c0",
|
||||
"dp3 t1.x, v1, t0",
|
||||
"add t1.x, t1.x, t1.x",
|
||||
"mul t1, v1, t1.x",
|
||||
"sub t1, t0, t1",
|
||||
"nrm t1.xyz, t1.xyz",
|
||||
"m33 t1.xyz, t1.xyz, c1",
|
||||
"nrm t1.xyz, t1.xyz",
|
||||
"tex o0, t1, s0 <cube,clamp,linear,nomip>"
|
||||
], "applyReflectionProcedure");
|
||||
result.assignVariableName(VariableType.CONSTANT, 1, "cLocalToGlobal", 3);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const _applyReflectionNormalMapProcedure:Procedure = getApplyReflectionNormalMapProcedure();
|
||||
|
||||
private static function getApplyReflectionNormalMapProcedure():Procedure {
|
||||
var result:Procedure = new Procedure([
|
||||
"#s0=sCubeMap",
|
||||
"#c0=cCamera",
|
||||
"#v0=vPosition",
|
||||
"sub t0, v0, c0",
|
||||
"dp3 t1.x, i0.xyz, t0",
|
||||
"add t1.x, t1.x, t1.x",
|
||||
"mul t1, i0.xyz, t1.x",
|
||||
"sub t1, t0, t1",
|
||||
"nrm t1.xyz, t1.xyz",
|
||||
"m33 t1.xyz, t1.xyz, c1",
|
||||
"nrm t1.xyz, t1.xyz",
|
||||
"tex o0, t1, s0 <cube,clamp,linear,nomip>"
|
||||
], "applyReflectionNormalMapProcedure");
|
||||
result.assignVariableName(VariableType.CONSTANT, 1, "cLocalToGlobal", 3);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const _blendReflection:Procedure = new Procedure([
|
||||
"#c0=cAlpha",
|
||||
"mul t1.xyz, i0.xyz, c0.y",
|
||||
"mul t0.xyz, i1, c0.z",
|
||||
"add t0.xyz, t1.xyz, t0",
|
||||
"mov t0.w, i0.w",
|
||||
"mov o0, t0"
|
||||
], "blendReflection");
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const _blendReflectionMap:Procedure = new Procedure([
|
||||
"#c0=cCamera",
|
||||
"#c1=cAlpha",
|
||||
"#s0=sReflection",
|
||||
"#v0=vUV",
|
||||
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
|
||||
"mul t0, t0, c1.z",
|
||||
"mul t1.xyz, i1, t0",
|
||||
"sub t0, c0.www, t0",
|
||||
"mul t2, i0, t0",
|
||||
"add t0.xyz, t1, t2",
|
||||
"mov t0.w, i0.w",
|
||||
"mov o0, t0"
|
||||
], "blendReflectionMap");
|
||||
|
||||
// inputs : tangent, normal
|
||||
private static const _passTBNRightProcedure:Procedure = getPassTBNProcedure(true);
|
||||
private static const _passTBNLeftProcedure:Procedure = getPassTBNProcedure(false);
|
||||
|
||||
private static function getPassTBNProcedure(right:Boolean):Procedure {
|
||||
var crsInSpace:String = (right) ? "crs t1.xyz, i0, i1" : "crs t1.xyz, i1, i0";
|
||||
return new Procedure([
|
||||
"#v0=vTangent",
|
||||
"#v1=vBinormal",
|
||||
"#v2=vNormal",
|
||||
// Calculates binormal
|
||||
crsInSpace,
|
||||
"mul t1.xyz, t1.xyz, i0.w",
|
||||
// Transpose normal matrix
|
||||
"mov v0.x, i0.x",
|
||||
"mov v0.y, t1.x",
|
||||
"mov v0.z, i1.x",
|
||||
"mov v0.w, i1.w",
|
||||
"mov v1.x, i0.y",
|
||||
"mov v1.y, t1.y",
|
||||
"mov v1.z, i1.y",
|
||||
"mov v1.w, i1.w",
|
||||
"mov v2.x, i0.z",
|
||||
"mov v2.y, t1.z",
|
||||
"mov v2.z, i1.z",
|
||||
"mov v2.w, i1.w"
|
||||
], "passTBNProcedure");
|
||||
}
|
||||
|
||||
// outputs : normal, viewVector
|
||||
private static const _getNormalTangentProcedure:Procedure = new Procedure([
|
||||
"#v0=vTangent",
|
||||
"#v1=vBinormal",
|
||||
"#v2=vNormal",
|
||||
"#v3=vUV",
|
||||
"#c0=cCamera",
|
||||
"#s0=sBump",
|
||||
// Extract normal from the texture
|
||||
"tex t0, v3, s0 <2d,repeat,linear,miplinear>",
|
||||
"add t0, t0, t0",
|
||||
"sub t0.xyz, t0.xyz, c0.www",
|
||||
// Transform the normal with TBN
|
||||
"nrm t1.xyz, v0.xyz",
|
||||
"dp3 o0.x, t0.xyz, t1.xyz",
|
||||
"nrm t1.xyz, v1.xyz",
|
||||
"dp3 o0.y, t0.xyz, t1.xyz",
|
||||
"nrm t1.xyz, v2.xyz",
|
||||
"dp3 o0.z, t0.xyz, t1.xyz",
|
||||
// Normalization after transform
|
||||
"nrm o0.xyz, o0.xyz"
|
||||
], "getNormalTangentProcedure");
|
||||
// outputs : normal, viewVector
|
||||
private static const _getNormalObjectProcedure:Procedure = new Procedure([
|
||||
"#v3=vUV",
|
||||
"#c0=cCamera",
|
||||
"#s0=sBump",
|
||||
// Extract normal from the texture
|
||||
"tex t0, v3, s0 <2d,repeat,linear,miplinear>",
|
||||
"add t0, t0, t0",
|
||||
"sub t0.xyz, t0.xyz, c0.www",
|
||||
// Normalization
|
||||
"nrm o0.xyz, t0.xyz"
|
||||
], "getNormalObjectProcedure");
|
||||
|
||||
// inputs : position
|
||||
private static const passSimpleFogConstProcedure:Procedure = new Procedure([
|
||||
"#v0=vZDistance",
|
||||
"#c0=cFogSpace",
|
||||
"dp4 t0.z, i0, c0",
|
||||
"mov v0, t0.zzzz",
|
||||
"sub v0.y, i0.w, t0.z"
|
||||
], "passSimpleFogConst");
|
||||
|
||||
// inputs : color
|
||||
private static const outputWithSimpleFogProcedure:Procedure = new Procedure([
|
||||
"#v0=vZDistance",
|
||||
"#c0=cFogColor",
|
||||
"#c1=cFogRange",
|
||||
// Restrict fog factor with the range
|
||||
"min t0.xy, v0.xy, c1.xy",
|
||||
"max t0.xy, t0.xy, c1.zw",
|
||||
"mul i0.xyz, i0.xyz, t0.y",
|
||||
"mul t0.xyz, c0.xyz, t0.x",
|
||||
"add i0.xyz, i0.xyz, t0.xyz",
|
||||
"mov o0, i0"
|
||||
], "outputWithSimpleFog");
|
||||
|
||||
// inputs : position, projected
|
||||
private static const postPassAdvancedFogConstProcedure:Procedure = new Procedure([
|
||||
"#v0=vZDistance",
|
||||
"#c0=cFogSpace",
|
||||
"dp4 t0.z, i0, c0",
|
||||
"mov v0, t0.zzzz",
|
||||
"sub v0.y, i0.w, t0.z",
|
||||
// Screen x coordinate
|
||||
"mov v0.zw, i1.xwxw",
|
||||
"mov o0, i1"
|
||||
], "postPassAdvancedFogConst");
|
||||
|
||||
// inputs : color
|
||||
private static const outputWithAdvancedFogProcedure:Procedure = new Procedure([
|
||||
"#v0=vZDistance",
|
||||
"#c0=cFogConsts",
|
||||
"#c1=cFogRange",
|
||||
"#s0=sFogTexture",
|
||||
// Restrict fog factor with the range
|
||||
"min t0.xy, v0.xy, c1.xy",
|
||||
"max t0.xy, t0.xy, c1.zw",
|
||||
"mul i0.xyz, i0.xyz, t0.y",
|
||||
// Calculate fog color
|
||||
"mov t1.xyzw, c0.yyzw",
|
||||
"div t0.z, v0.z, v0.w",
|
||||
"mul t0.z, t0.z, c0.x",
|
||||
"add t1.x, t1.x, t0.z",
|
||||
"tex t1, t1, s0 <2d,repeat,linear,miplinear>",
|
||||
"mul t0.xyz, t1.xyz, t0.x",
|
||||
"add i0.xyz, i0.xyz, t0.xyz",
|
||||
"mov o0, i0"
|
||||
], "outputWithAdvancedFog");
|
||||
|
||||
private static const _applyLightMapProcedure:Procedure = new Procedure([
|
||||
"#v0=vUV1",
|
||||
"#s0=sLightMap",
|
||||
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
|
||||
"add t0, t0, t0",
|
||||
"mul o0.xyz, i0.xyz, t0.xyz"
|
||||
], "applyLightMapProcedure");
|
||||
|
||||
private static const _passLightMapUVProcedure:Procedure = new Procedure([
|
||||
"#a0=aUV1",
|
||||
"#v0=vUV1",
|
||||
"mov v0, a0"
|
||||
], "passLightMapUVProcedure");
|
||||
|
||||
private var _normalMapSpace:int = NormalMapSpace.TANGENT_RIGHT_HANDED;
|
||||
|
||||
/**
|
||||
* Type of the normal map. Should be defined by constants of <code>NormalMapSpace</code> class.
|
||||
* @default NormalMapSpace.TANGENT
|
||||
*
|
||||
* @see NormalMapSpace
|
||||
*/
|
||||
public function get normalMapSpace():int {
|
||||
return _normalMapSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set normalMapSpace(value:int):void {
|
||||
if (value != NormalMapSpace.TANGENT_RIGHT_HANDED && value != NormalMapSpace.TANGENT_LEFT_HANDED && value != NormalMapSpace.OBJECT) {
|
||||
throw new ArgumentError("Value must be a constant from the NormalMapSpace class");
|
||||
}
|
||||
|
||||
_normalMapSpace = value;
|
||||
dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal map.
|
||||
*/
|
||||
public function get normalMap():TextureResource {
|
||||
return _normalMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set normalMap(value:TextureResource):void {
|
||||
_normalMap = value;
|
||||
dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflection texture. Should be <code>BitmapCubeTextureResource</code> or <code>ExternalTextureResource</code> with CubeTexture data.
|
||||
*/
|
||||
public function get environmentMap():TextureResource {
|
||||
return _environmentMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set environmentMap(value:TextureResource):void {
|
||||
_environmentMap = value;
|
||||
dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflectivity map.
|
||||
*/
|
||||
public function get reflectionMap():TextureResource {
|
||||
return _reflectionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set reflectionMap(value:TextureResource):void {
|
||||
_reflectionMap = value;
|
||||
dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Light map.
|
||||
*/
|
||||
public function get lightMap():TextureResource {
|
||||
return _lightMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set lightMap(value:TextureResource):void {
|
||||
_lightMap = value;
|
||||
dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflectivity.
|
||||
*/
|
||||
public var reflection:Number = 1;
|
||||
|
||||
/**
|
||||
* Number of the UV-channel for light map.
|
||||
*/
|
||||
public var lightMapChannel:uint = 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _normalMap:TextureResource;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _environmentMap:TextureResource;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _reflectionMap:TextureResource;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _lightMap:TextureResource;
|
||||
|
||||
private var localToGlobalTransform:Transform3D = new Transform3D();
|
||||
|
||||
/*alternativa3d var lightMapOptions:SamplerOptions = new SamplerOptions(this);
|
||||
|
||||
alternativa3d var normalMapOptions:SamplerOptions = new SamplerOptions(this);
|
||||
|
||||
alternativa3d var environmentMapOptions:SamplerOptions = new SamplerOptions(this);
|
||||
|
||||
alternativa3d var reflectionMapOptions:SamplerOptions = new SamplerOptions(this);
|
||||
|
||||
alternativa3d var diffuseMapOptions:SamplerOptions = new SamplerOptions(this);*/
|
||||
|
||||
/**
|
||||
* Creates a new EnvironmentMaterial instance.
|
||||
* @param diffuseMap
|
||||
* @param environmentMap
|
||||
* @param normalMap
|
||||
* @param reflectionMap
|
||||
* @param lightMap
|
||||
* @param opacityMap
|
||||
* @param alpha
|
||||
*/
|
||||
public function EnvironmentMaterial(diffuseMap:TextureResource = null, environmentMap:TextureResource = null, normalMap:TextureResource = null, reflectionMap:TextureResource = null, lightMap:TextureResource = null, opacityMap:TextureResource = null, alpha:Number = 1) {
|
||||
super(diffuseMap, opacityMap, alpha);
|
||||
this._environmentMap = environmentMap;
|
||||
this._normalMap = normalMap;
|
||||
this._reflectionMap = reflectionMap;
|
||||
this._lightMap = lightMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Material {
|
||||
var res:EnvironmentMaterial = new EnvironmentMaterial(diffuseMap, _environmentMap, _normalMap, _reflectionMap, _lightMap, opacityMap, alpha);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function clonePropertiesFrom(source:Material):void {
|
||||
super.clonePropertiesFrom(source);
|
||||
var eMaterial:EnvironmentMaterial = EnvironmentMaterial(source);
|
||||
reflection = eMaterial.reflection;
|
||||
lightMapChannel = eMaterial.lightMapChannel;
|
||||
_normalMapSpace = eMaterial._normalMapSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d override function fillResources(resources:Dictionary, resourceType:Class):void {
|
||||
super.alternativa3d::fillResources(resources, resourceType);
|
||||
if (_environmentMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_environmentMap)) as Class, resourceType)) {
|
||||
resources[_environmentMap] = true;
|
||||
}
|
||||
if (_normalMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_normalMap)) as Class, resourceType)) {
|
||||
resources[_normalMap] = true;
|
||||
}
|
||||
if (_reflectionMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_reflectionMap)) as Class, resourceType)) {
|
||||
resources[_reflectionMap] = true;
|
||||
}
|
||||
if (_lightMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_lightMap)) as Class, resourceType)) {
|
||||
resources[_lightMap] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function setupProgram(targetObject:Object3D, opacityMap:TextureResource, alphaTest:int):EnvironmentMaterialShaderProgram {
|
||||
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
|
||||
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
|
||||
var positionVar:String = "aPosition";
|
||||
var normalVar:String = "aNormal";
|
||||
var tangentVar:String = "aTangent";
|
||||
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
|
||||
vertexLinker.declareVariable(normalVar, VariableType.ATTRIBUTE);
|
||||
if (targetObject.transformProcedure != null) {
|
||||
positionVar = appendPositionTransformProcedure(targetObject.transformProcedure, vertexLinker);
|
||||
}
|
||||
var procedure:Procedure;
|
||||
if (targetObject.deltaTransformProcedure != null) {
|
||||
vertexLinker.declareVariable("tTransformedNormal");
|
||||
procedure = targetObject.deltaTransformProcedure.newInstance();
|
||||
vertexLinker.addProcedure(procedure);
|
||||
vertexLinker.setInputParams(procedure, normalVar);
|
||||
vertexLinker.setOutputParams(procedure, "tTransformedNormal");
|
||||
normalVar = "tTransformedNormal";
|
||||
|
||||
if ((_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) && _normalMap != null) {
|
||||
vertexLinker.declareVariable(tangentVar, VariableType.ATTRIBUTE);
|
||||
vertexLinker.declareVariable("tTransformedTangent");
|
||||
procedure = targetObject.deltaTransformProcedure.newInstance();
|
||||
vertexLinker.addProcedure(procedure);
|
||||
vertexLinker.setInputParams(procedure, tangentVar);
|
||||
vertexLinker.setOutputParams(procedure, "tTransformedTangent");
|
||||
tangentVar = "tTransformedTangent";
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) && _normalMap != null) {
|
||||
vertexLinker.declareVariable(tangentVar, VariableType.ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
if (_lightMap != null) {
|
||||
vertexLinker.addProcedure(_passLightMapUVProcedure);
|
||||
}
|
||||
|
||||
vertexLinker.addProcedure(_passReflectionProcedure);
|
||||
vertexLinker.setInputParams(_passReflectionProcedure, positionVar, normalVar);
|
||||
vertexLinker.addProcedure(_projectProcedure);
|
||||
vertexLinker.setInputParams(_projectProcedure, positionVar);
|
||||
vertexLinker.addProcedure(_passUVProcedure);
|
||||
if (_normalMap != null) {
|
||||
fragmentLinker.declareVariable("tNormal");
|
||||
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
|
||||
var nrmProcedure:Procedure = (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED) ? _passTBNRightProcedure : _passTBNLeftProcedure;
|
||||
vertexLinker.addProcedure(nrmProcedure);
|
||||
vertexLinker.setInputParams(nrmProcedure, tangentVar, normalVar);
|
||||
fragmentLinker.addProcedure(_getNormalTangentProcedure);
|
||||
fragmentLinker.setOutputParams(_getNormalTangentProcedure, "tNormal");
|
||||
} else {
|
||||
fragmentLinker.addProcedure(_getNormalObjectProcedure);
|
||||
fragmentLinker.setOutputParams(_getNormalObjectProcedure, "tNormal");
|
||||
}
|
||||
}
|
||||
|
||||
fragmentLinker.declareVariable("tColor");
|
||||
outputProcedure = opacityMap != null ? getDiffuseOpacityProcedure : getDiffuseProcedure;
|
||||
fragmentLinker.addProcedure(outputProcedure);
|
||||
fragmentLinker.setOutputParams(outputProcedure, "tColor");
|
||||
|
||||
if (alphaTest > 0) {
|
||||
outputProcedure = alphaTest == 1 ? thresholdOpaqueAlphaProcedure : thresholdTransparentAlphaProcedure;
|
||||
fragmentLinker.addProcedure(outputProcedure, "tColor");
|
||||
fragmentLinker.setOutputParams(outputProcedure, "tColor");
|
||||
}
|
||||
|
||||
fragmentLinker.declareVariable("tReflection");
|
||||
if (_normalMap != null) {
|
||||
fragmentLinker.addProcedure(_applyReflectionNormalMapProcedure);
|
||||
fragmentLinker.setInputParams(_applyReflectionNormalMapProcedure, "tNormal");
|
||||
fragmentLinker.setOutputParams(_applyReflectionNormalMapProcedure, "tReflection");
|
||||
} else {
|
||||
fragmentLinker.addProcedure(_applyReflectionProcedure);
|
||||
fragmentLinker.setOutputParams(_applyReflectionProcedure, "tReflection");
|
||||
}
|
||||
if (_lightMap != null) {
|
||||
fragmentLinker.addProcedure(_applyLightMapProcedure);
|
||||
fragmentLinker.setInputParams(_applyLightMapProcedure, "tColor");
|
||||
fragmentLinker.setOutputParams(_applyLightMapProcedure, "tColor");
|
||||
}
|
||||
|
||||
var outputProcedure:Procedure;
|
||||
if (_reflectionMap != null) {
|
||||
fragmentLinker.addProcedure(_blendReflectionMap);
|
||||
fragmentLinker.setInputParams(_blendReflectionMap, "tColor", "tReflection");
|
||||
outputProcedure = _blendReflectionMap;
|
||||
} else {
|
||||
fragmentLinker.addProcedure(_blendReflection);
|
||||
fragmentLinker.setInputParams(_blendReflection, "tColor", "tReflection");
|
||||
outputProcedure = _blendReflection;
|
||||
}
|
||||
|
||||
if (fogMode == FogMode.SIMPLE || fogMode == FogMode.ADVANCED) {
|
||||
fragmentLinker.setOutputParams(outputProcedure, "tColor");
|
||||
}
|
||||
if (fogMode == FogMode.SIMPLE) {
|
||||
vertexLinker.addProcedure(passSimpleFogConstProcedure);
|
||||
vertexLinker.setInputParams(passSimpleFogConstProcedure, positionVar);
|
||||
fragmentLinker.addProcedure(outputWithSimpleFogProcedure);
|
||||
fragmentLinker.setInputParams(outputWithSimpleFogProcedure, "tColor");
|
||||
} else if (fogMode == FogMode.ADVANCED) {
|
||||
vertexLinker.declareVariable("tProjected");
|
||||
vertexLinker.setOutputParams(_projectProcedure, "tProjected");
|
||||
vertexLinker.addProcedure(postPassAdvancedFogConstProcedure);
|
||||
vertexLinker.setInputParams(postPassAdvancedFogConstProcedure, positionVar, "tProjected");
|
||||
fragmentLinker.addProcedure(outputWithAdvancedFogProcedure);
|
||||
fragmentLinker.setInputParams(outputWithAdvancedFogProcedure, "tColor");
|
||||
}
|
||||
|
||||
fragmentLinker.varyings = vertexLinker.varyings;
|
||||
return new EnvironmentMaterialShaderProgram(vertexLinker, fragmentLinker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function getProceduresCRC32(targetObject:Object3D, opacityMap:TextureResource, alphaTest:int):uint {
|
||||
var crc:uint = 0xFFFFFFFF;
|
||||
var procedureCRC:uint;
|
||||
var crc32Table:Vector.<uint> = Procedure.crc32Table;
|
||||
if (targetObject.transformProcedure != null) {
|
||||
procedureCRC = targetObject.transformProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
if (targetObject.deltaTransformProcedure != null) {
|
||||
procedureCRC = targetObject.deltaTransformProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
if ((_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) && _normalMap != null) {
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
}
|
||||
if (_lightMap != null) {
|
||||
procedureCRC = _passLightMapUVProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
if (_normalMap != null) {
|
||||
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
|
||||
procedureCRC = (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED) ? _passTBNRightProcedure.crc32 : _passTBNLeftProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
|
||||
procedureCRC = _getNormalTangentProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
} else {
|
||||
procedureCRC = _getNormalObjectProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
}
|
||||
procedureCRC = opacityMap != null ? getDiffuseOpacityProcedure.crc32 : getDiffuseProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
if (alphaTest > 0) {
|
||||
procedureCRC = alphaTest == 1 ? thresholdOpaqueAlphaProcedure.crc32 : thresholdTransparentAlphaProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
if (_normalMap != null) {
|
||||
procedureCRC = _applyReflectionNormalMapProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
} else {
|
||||
procedureCRC = _applyReflectionProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
if (_lightMap != null) {
|
||||
procedureCRC = _applyLightMapProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
if (_reflectionMap != null) {
|
||||
procedureCRC = _blendReflectionMap.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
} else {
|
||||
procedureCRC = _blendReflection.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
if (fogMode == FogMode.SIMPLE) {
|
||||
procedureCRC = passSimpleFogConstProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
procedureCRC = outputWithSimpleFogProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
} else if (fogMode == FogMode.ADVANCED) {
|
||||
procedureCRC = postPassAdvancedFogConstProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
procedureCRC = outputWithAdvancedFogProcedure.crc32;
|
||||
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
return crc ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
private function getDrawUnit(program:EnvironmentMaterialShaderProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource):DrawUnit {
|
||||
// Buffers
|
||||
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
|
||||
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
|
||||
var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
|
||||
var tangentsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TANGENT4);
|
||||
if (positionBuffer == null || uvBuffer == null || normalsBuffer == null) return null;
|
||||
var i:int;
|
||||
var object:Object3D = surface.object;
|
||||
|
||||
if (program.sBump >= 0 && (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED)) {
|
||||
if (tangentsBuffer == null) return null;
|
||||
}
|
||||
|
||||
// Draw call
|
||||
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
|
||||
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, alpha);
|
||||
// Set the textures
|
||||
if (program.sLightMap >= 0) {
|
||||
drawUnit.setTextureAt(program.sLightMap, _lightMap._texture);
|
||||
drawUnit.setVertexBufferAt(program.aUV1, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[lightMapChannel]]);
|
||||
}
|
||||
|
||||
if (program.sBump >= 0) {
|
||||
drawUnit.setTextureAt(program.sBump, _normalMap._texture);
|
||||
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
|
||||
drawUnit.setVertexBufferAt(program.aTangent, tangentsBuffer, geometry._attributesOffsets[VertexAttributes.TANGENT4], VertexAttributes.FORMATS[VertexAttributes.TANGENT4]);
|
||||
}
|
||||
}
|
||||
|
||||
if (program.sReflection >= 0) {
|
||||
drawUnit.setTextureAt(program.sReflection, _reflectionMap._texture);
|
||||
}
|
||||
|
||||
if (program.sOpacity >= 0) {
|
||||
drawUnit.setTextureAt(program.sOpacity, opacityMap._texture);
|
||||
}
|
||||
|
||||
// Set the streams
|
||||
drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
|
||||
drawUnit.setVertexBufferAt(program.aUV, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
|
||||
drawUnit.setVertexBufferAt(program.aNormal, normalsBuffer, geometry._attributesOffsets[VertexAttributes.NORMAL], VertexAttributes.FORMATS[VertexAttributes.NORMAL]);
|
||||
|
||||
// Set the constants
|
||||
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
|
||||
drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform);
|
||||
|
||||
drawUnit.setTextureAt(program.sTexture, diffuseMap._texture);
|
||||
drawUnit.setTextureAt(program.sCubeMap, _environmentMap._texture);
|
||||
var cameraToLocalTransform:Transform3D = object.cameraToLocalTransform;
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.cCamera, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l);
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.cAlpha, 0, 1 - reflection, reflection, alpha);
|
||||
|
||||
// Calculate local to global matrix
|
||||
localToGlobalTransform.combine(camera.localToGlobalTransform, object.localToCameraTransform);
|
||||
drawUnit.setFragmentConstantsFromTransform(program.cLocalToGlobal, localToGlobalTransform);
|
||||
if (fogMode == FogMode.SIMPLE || fogMode == FogMode.ADVANCED) {
|
||||
var lm:Transform3D = object.localToCameraTransform;
|
||||
var dist:Number = fogFar - fogNear;
|
||||
drawUnit.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cFogSpace"), lm.i/dist, lm.j/dist, lm.k/dist, (lm.l - fogNear)/dist);
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogRange"), fogMaxDensity, 1, 0, 1 - fogMaxDensity);
|
||||
}
|
||||
if (fogMode == FogMode.SIMPLE) {
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogColor"), fogColorR, fogColorG, fogColorB);
|
||||
}
|
||||
if (fogMode == FogMode.ADVANCED) {
|
||||
if (fogTexture == null) {
|
||||
var bmd:BitmapData = new BitmapData(32, 1, false, 0xFF0000);
|
||||
for (i = 0; i < 32; i++) {
|
||||
bmd.setPixel(i, 0, ((i/32)*255) << 16);
|
||||
}
|
||||
fogTexture = new BitmapTextureResource(bmd);
|
||||
fogTexture.upload(camera.context3D);
|
||||
}
|
||||
var cLocal:Transform3D = camera.localToGlobalTransform;
|
||||
var halfW:Number = camera.view.width/2;
|
||||
var leftX:Number = -halfW*cLocal.a + camera.focalLength*cLocal.c;
|
||||
var leftY:Number = -halfW*cLocal.e + camera.focalLength*cLocal.g;
|
||||
var rightX:Number = halfW*cLocal.a + camera.focalLength*cLocal.c;
|
||||
var rightY:Number = halfW*cLocal.e + camera.focalLength*cLocal.g;
|
||||
// UV
|
||||
var angle:Number = (Math.atan2(leftY, leftX) - Math.PI/2);
|
||||
if (angle < 0) angle += Math.PI*2;
|
||||
var dx:Number = rightX - leftX;
|
||||
var dy:Number = rightY - leftY;
|
||||
var lens:Number = Math.sqrt(dx*dx + dy*dy);
|
||||
leftX /= lens;
|
||||
leftY /= lens;
|
||||
rightX /= lens;
|
||||
rightY /= lens;
|
||||
var uScale:Number = Math.acos(leftX*rightX + leftY*rightY)/Math.PI/2;
|
||||
var uRight:Number = angle/Math.PI/2;
|
||||
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogConsts"), 0.5*uScale, 0.5 - uRight, 0);
|
||||
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sFogTexture"), fogTexture._texture);
|
||||
}
|
||||
return drawUnit;
|
||||
}
|
||||
|
||||
private function getProgram(targetObject:Object3D, camera:Camera3D, opacityMap:TextureResource, alphaTest:int):EnvironmentMaterialShaderProgram {
|
||||
// Renew program cache for this context
|
||||
if (camera.context3D != cachedContext3D) {
|
||||
cachedContext3D = camera.context3D;
|
||||
programsCache = caches[cachedContext3D];
|
||||
if (programsCache == null) {
|
||||
programsCache = new Array();
|
||||
caches[cachedContext3D] = programsCache;
|
||||
}
|
||||
}
|
||||
|
||||
var key:uint;
|
||||
var program:EnvironmentMaterialShaderProgram;
|
||||
key = getProceduresCRC32(targetObject, opacityMap, alphaTest);
|
||||
program = programsCache[key];
|
||||
if (program == null) {
|
||||
program = programsCache[key] = setupProgram(targetObject, opacityMap, alphaTest);
|
||||
|
||||
program.upload(camera.context3D);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
|
||||
if (diffuseMap == null || diffuseMap._texture == null) return;
|
||||
if (_environmentMap == null || _environmentMap._texture == null || !(_environmentMap._texture is CubeTexture)) return;
|
||||
if (opacityMap != null && opacityMap._texture == null) return;
|
||||
if (_normalMap != null && _normalMap._texture == null) return;
|
||||
if (_reflectionMap != null && _reflectionMap._texture == null) return;
|
||||
if (_lightMap != null && _lightMap._texture == null) return;
|
||||
var object:Object3D = surface.object;
|
||||
|
||||
// Program
|
||||
var program:EnvironmentMaterialShaderProgram;
|
||||
var drawUnit:DrawUnit;
|
||||
// Opaque pass
|
||||
if (opaquePass && alphaThreshold <= alpha) {
|
||||
if (alphaThreshold > 0) {
|
||||
// Alpha test
|
||||
// use opacityMap if it is presented
|
||||
program = getProgram(object, camera, opacityMap, 1);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
|
||||
} else {
|
||||
// do not use opacityMap at all
|
||||
program = getProgram(object, camera, null, 0);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, null);
|
||||
}
|
||||
if (drawUnit == null) return;
|
||||
// Use z-buffer within DrawCall, draws without blending
|
||||
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
|
||||
}
|
||||
// Transparent pass
|
||||
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
|
||||
// use opacityMap if it is presented
|
||||
if (alphaThreshold <= alpha && !opaquePass) {
|
||||
// Alpha threshold
|
||||
program = getProgram(object, camera, opacityMap, 2);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
|
||||
} else {
|
||||
// There is no Alpha threshold or check z-buffer by previous pass
|
||||
program = getProgram(object, camera, opacityMap, 0);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
|
||||
}
|
||||
if (drawUnit == null) return;
|
||||
// Do not use z-buffer, draws with blending
|
||||
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
|
||||
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
|
||||
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function dirty():void {
|
||||
for each (var program:EnvironmentMaterialShaderProgram in programsCache) {
|
||||
program.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.materials.ShaderProgram;
|
||||
import alternativa.engine3d.materials.compiler.Linker;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
class EnvironmentMaterialShaderProgram extends ShaderProgram {
|
||||
|
||||
public var aTangent:int = -1;
|
||||
public var aNormal:int = -1;
|
||||
public var aPosition:int = -1;
|
||||
public var aUV:int = -1;
|
||||
public var aUV1:int = -1;
|
||||
|
||||
public var cCamera:int = -1;
|
||||
public var cLocalToGlobal:int = -1;
|
||||
public var cAlpha:int = -1;
|
||||
public var cProjMatrix:int = -1;
|
||||
|
||||
public var sBump:int = -1;
|
||||
public var sTexture:int = -1;
|
||||
public var sOpacity:int = -1;
|
||||
public var sCubeMap:int = -1;
|
||||
public var sReflection:int = -1;
|
||||
public var sLightMap:int = -1;
|
||||
public var dirty:Boolean = false;
|
||||
|
||||
public function EnvironmentMaterialShaderProgram(vertexShader:Linker, fragmentShader:Linker) {
|
||||
super(vertexShader, fragmentShader);
|
||||
fragmentShader.varyings = vertexShader.varyings;
|
||||
vertexShader.link();
|
||||
fragmentShader.link();
|
||||
aPosition = vertexShader.findVariable("aPosition");
|
||||
aNormal = vertexShader.findVariable("aNormal");
|
||||
aUV = vertexShader.findVariable("aUV");
|
||||
sBump = fragmentShader.findVariable("sBump");
|
||||
aTangent = vertexShader.findVariable("aTangent");
|
||||
sReflection = fragmentShader.findVariable("sReflection");
|
||||
sLightMap = fragmentShader.findVariable("sLightMap");
|
||||
aUV1 = vertexShader.findVariable("aUV1");
|
||||
cProjMatrix = vertexShader.findVariable("cProjMatrix");
|
||||
sTexture = fragmentShader.findVariable("sDiffuse");
|
||||
sCubeMap = fragmentShader.findVariable("sCubeMap");
|
||||
cCamera = fragmentShader.findVariable("cCamera");
|
||||
cLocalToGlobal = fragmentShader.findVariable("cLocalToGlobal");
|
||||
cAlpha = fragmentShader.findVariable("cAlpha");
|
||||
sOpacity = fragmentShader.findVariable("sOpacity");
|
||||
}
|
||||
|
||||
}
|
||||
153
src/alternativa/engine3d/materials/FillMaterial.as
Normal file
153
src/alternativa/engine3d/materials/FillMaterial.as
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.materials {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.DrawUnit;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Renderer;
|
||||
import alternativa.engine3d.core.VertexAttributes;
|
||||
import alternativa.engine3d.materials.compiler.Linker;
|
||||
import alternativa.engine3d.materials.compiler.Procedure;
|
||||
import alternativa.engine3d.materials.compiler.VariableType;
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DBlendFactor;
|
||||
import flash.display3D.Context3DProgramType;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* The materiall fills surface with solid color in light-independent manner. Can draw a Skin with no more than 41 Joints per surface. See Skin.divide() for more details.
|
||||
*
|
||||
* @see alternativa.engine3d.objects.Skin#divide()
|
||||
*/
|
||||
public class FillMaterial extends Material {
|
||||
|
||||
private static var caches:Dictionary = new Dictionary(true);
|
||||
private var cachedContext3D:Context3D;
|
||||
private var programsCache:Dictionary;
|
||||
|
||||
private static var outColorProcedure:Procedure = new Procedure(["#c0=cColor", "mov o0, c0"], "outColorProcedure");
|
||||
|
||||
/**
|
||||
* Transparency
|
||||
*/
|
||||
public var alpha:Number = 1;
|
||||
|
||||
private var red:Number;
|
||||
private var green:Number;
|
||||
private var blue:Number;
|
||||
|
||||
/**
|
||||
* Color.
|
||||
*/
|
||||
public function get color():uint {
|
||||
return (red*0xFF << 16) + (green*0xFF << 8) + blue*0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set color(value:uint):void {
|
||||
red = ((value >> 16) & 0xFF)/0xFF;
|
||||
green = ((value >> 8) & 0xFF)/0xFF;
|
||||
blue = (value & 0xff)/0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FillMaterial instance.
|
||||
* @param color Color .
|
||||
* @param alpha Transparency.
|
||||
*/
|
||||
public function FillMaterial(color:uint = 0x7F7F7F, alpha:Number = 1) {
|
||||
this.color = color;
|
||||
this.alpha = alpha;
|
||||
}
|
||||
|
||||
private function setupProgram(object:Object3D):ShaderProgram {
|
||||
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
|
||||
var positionVar:String = "aPosition";
|
||||
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
|
||||
if (object.transformProcedure != null) {
|
||||
positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
|
||||
}
|
||||
vertexLinker.addProcedure(_projectProcedure);
|
||||
vertexLinker.setInputParams(_projectProcedure, positionVar);
|
||||
|
||||
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
|
||||
fragmentLinker.addProcedure(outColorProcedure);
|
||||
fragmentLinker.varyings = vertexLinker.varyings;
|
||||
return new ShaderProgram(vertexLinker, fragmentLinker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
|
||||
var object:Object3D = surface.object;
|
||||
// Strams
|
||||
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
|
||||
// Check validity
|
||||
if (positionBuffer == null) return;
|
||||
// Program
|
||||
|
||||
// Renew program cache for this context
|
||||
if (camera.context3D != cachedContext3D) {
|
||||
cachedContext3D = camera.context3D;
|
||||
programsCache = caches[cachedContext3D];
|
||||
if (programsCache == null) {
|
||||
programsCache = new Dictionary();
|
||||
caches[cachedContext3D] = programsCache;
|
||||
}
|
||||
}
|
||||
|
||||
var program:ShaderProgram = programsCache[object.transformProcedure];
|
||||
if (program == null) {
|
||||
program = setupProgram(object);
|
||||
program.upload(camera.context3D);
|
||||
programsCache[object.transformProcedure] = program;
|
||||
}
|
||||
// Drawcall
|
||||
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
|
||||
// Streams
|
||||
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
|
||||
// Constants
|
||||
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
|
||||
drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cColor"), red, green, blue, alpha);
|
||||
// Send to render
|
||||
if (alpha < 1) {
|
||||
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
|
||||
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
|
||||
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
|
||||
} else {
|
||||
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Material {
|
||||
var res:FillMaterial = new FillMaterial(color, alpha);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
24
src/alternativa/engine3d/materials/FogMode.as
Normal file
24
src/alternativa/engine3d/materials/FogMode.as
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.materials {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class FogMode {
|
||||
|
||||
public static const DISABLED:int = 0;
|
||||
public static const SIMPLE:int = 1;
|
||||
public static const ADVANCED:int = 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
256
src/alternativa/engine3d/materials/LightMapMaterial.as
Normal file
256
src/alternativa/engine3d/materials/LightMapMaterial.as
Normal file
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Exhibit A - Source Code Form License Notice
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
|
||||
* You may add additional accurate notices of copyright ownership.
|
||||
*
|
||||
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
|
||||
* */
|
||||
|
||||
package alternativa.engine3d.materials {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.DrawUnit;
|
||||
import alternativa.engine3d.core.Light3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Renderer;
|
||||
import alternativa.engine3d.core.VertexAttributes;
|
||||
import alternativa.engine3d.materials.compiler.Linker;
|
||||
import alternativa.engine3d.materials.compiler.Procedure;
|
||||
import alternativa.engine3d.materials.compiler.VariableType;
|
||||
import alternativa.engine3d.objects.Surface;
|
||||
import alternativa.engine3d.resources.Geometry;
|
||||
import alternativa.engine3d.resources.TextureResource;
|
||||
|
||||
import avmplus.getQualifiedClassName;
|
||||
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DBlendFactor;
|
||||
import flash.display3D.Context3DProgramType;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getDefinitionByName;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Texture material which supports light map. Can draw a Skin with no more than 41 Joints per surface. See Skin.divide() for more details.
|
||||
* To be drawn with this material, geometry should have UV coordinates. Different UV-channels can be used for diffuse texture and light map.
|
||||
*
|
||||
* @see alternativa.engine3d.objects.Skin#divide()
|
||||
* @see alternativa.engine3d.core.VertexAttributes#TEXCOORDS
|
||||
*/
|
||||
public class LightMapMaterial extends TextureMaterial {
|
||||
|
||||
private static var caches:Dictionary = new Dictionary(true);
|
||||
private var cachedContext3D:Context3D;
|
||||
private var programsCache:Dictionary;
|
||||
|
||||
// inputs: color
|
||||
private static const _applyLightMapProcedure:Procedure = new Procedure([
|
||||
"#v0=vUV1",
|
||||
"#s0=sLightMap",
|
||||
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
|
||||
"add t0, t0, t0",
|
||||
"mul i0.xyz, i0.xyz, t0.xyz",
|
||||
"mov o0, i0"
|
||||
], "applyLightMapProcedure");
|
||||
|
||||
private static const _passLightMapUVProcedure:Procedure = new Procedure([
|
||||
"#a0=aUV1",
|
||||
"#v0=vUV1",
|
||||
"mov v0, a0"
|
||||
], "passLightMapUVProcedure");
|
||||
|
||||
/**
|
||||
* Light map.
|
||||
*/
|
||||
public var lightMap:TextureResource;
|
||||
/**
|
||||
* Number of the UV-channel for light map.
|
||||
*/
|
||||
public var lightMapChannel:uint = 0;
|
||||
|
||||
/**
|
||||
* Creates a new LightMapMaterial instance.
|
||||
* @param diffuseMap Diffuse texture.
|
||||
* @param lightMap Light map.
|
||||
* @param lightMapChannel Number of the UV-channel for light map.
|
||||
*/
|
||||
public function LightMapMaterial(diffuseMap:TextureResource = null, lightMap:TextureResource = null, lightMapChannel:uint = 0, opacityMap:TextureResource = null) {
|
||||
super(diffuseMap, opacityMap);
|
||||
this.lightMap = lightMap;
|
||||
this.lightMapChannel = lightMapChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():Material {
|
||||
var res:LightMapMaterial = new LightMapMaterial(diffuseMap, lightMap, lightMapChannel, opacityMap);
|
||||
res.clonePropertiesFrom(this);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
|
||||
super.fillResources(resources, resourceType);
|
||||
|
||||
if (lightMap != null &&
|
||||
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(lightMap)) as Class, resourceType)) {
|
||||
resources[lightMap] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* @param programs
|
||||
* @param camera
|
||||
* @param opacityMap
|
||||
* @param alphaTest 0 - disabled, 1 - opaque, 2 - contours
|
||||
* @return
|
||||
*/
|
||||
private function getProgram(object:Object3D, programs:Vector.<ShaderProgram>, camera:Camera3D, opacityMap:TextureResource, alphaTest:int):ShaderProgram {
|
||||
var key:int = (opacityMap != null ? 3 : 0) + alphaTest;
|
||||
var program:ShaderProgram = programs[key];
|
||||
if (program == null) {
|
||||
// Make program
|
||||
// Vertex shader
|
||||
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
|
||||
|
||||
var positionVar:String = "aPosition";
|
||||
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
|
||||
if (object.transformProcedure != null) {
|
||||
positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
|
||||
}
|
||||
vertexLinker.addProcedure(_projectProcedure);
|
||||
vertexLinker.setInputParams(_projectProcedure, positionVar);
|
||||
vertexLinker.addProcedure(_passUVProcedure);
|
||||
vertexLinker.addProcedure(_passLightMapUVProcedure);
|
||||
|
||||
// Pixel shader
|
||||
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
|
||||
fragmentLinker.declareVariable("tColor");
|
||||
var outProcedure:Procedure = (opacityMap != null ? getDiffuseOpacityProcedure : getDiffuseProcedure);
|
||||
fragmentLinker.addProcedure(outProcedure);
|
||||
fragmentLinker.setOutputParams(outProcedure, "tColor");
|
||||
|
||||
if (alphaTest > 0) {
|
||||
outProcedure = alphaTest == 1 ? thresholdOpaqueAlphaProcedure : thresholdTransparentAlphaProcedure;
|
||||
fragmentLinker.addProcedure(outProcedure, "tColor");
|
||||
fragmentLinker.setOutputParams(outProcedure, "tColor");
|
||||
}
|
||||
|
||||
fragmentLinker.addProcedure(_applyLightMapProcedure, "tColor");
|
||||
|
||||
fragmentLinker.varyings = vertexLinker.varyings;
|
||||
|
||||
program = new ShaderProgram(vertexLinker, fragmentLinker);
|
||||
|
||||
program.upload(camera.context3D);
|
||||
programs[key] = program;
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
private function getDrawUnit(program:ShaderProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource):DrawUnit {
|
||||
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
|
||||
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
|
||||
var lightMapUVBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]);
|
||||
|
||||
var object:Object3D = surface.object;
|
||||
|
||||
// Drawcall
|
||||
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
|
||||
|
||||
// Streams
|
||||
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
|
||||
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV"), uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
|
||||
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV1"), lightMapUVBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[lightMapChannel]]);
|
||||
// Constants
|
||||
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
|
||||
drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
|
||||
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, alpha);
|
||||
// Textures
|
||||
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sDiffuse"), diffuseMap._texture);
|
||||
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sLightMap"), lightMap._texture);
|
||||
if (opacityMap != null) {
|
||||
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sOpacity"), opacityMap._texture);
|
||||
}
|
||||
|
||||
return drawUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
|
||||
if (diffuseMap == null || lightMap == null || diffuseMap._texture == null || lightMap._texture == null) return;
|
||||
if (opacityMap != null && opacityMap._texture == null) return;
|
||||
|
||||
var object:Object3D = surface.object;
|
||||
|
||||
// Buffers
|
||||
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
|
||||
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
|
||||
var lightMapUVBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]);
|
||||
|
||||
if (positionBuffer == null || uvBuffer == null || lightMapUVBuffer == null) return;
|
||||
|
||||
if (camera.context3D != cachedContext3D) {
|
||||
cachedContext3D = camera.context3D;
|
||||
programsCache = caches[cachedContext3D];
|
||||
if (programsCache == null) {
|
||||
programsCache = new Dictionary();
|
||||
caches[cachedContext3D] = programsCache;
|
||||
}
|
||||
}
|
||||
|
||||
var optionsPrograms:Vector.<ShaderProgram> = programsCache[object.transformProcedure];
|
||||
if(optionsPrograms == null) {
|
||||
optionsPrograms = new Vector.<ShaderProgram>(6, true);
|
||||
programsCache[object.transformProcedure] = optionsPrograms;
|
||||
}
|
||||
|
||||
var program:ShaderProgram;
|
||||
var drawUnit:DrawUnit;
|
||||
// Opaque pass
|
||||
if (opaquePass && alphaThreshold <= alpha) {
|
||||
if (alphaThreshold > 0) {
|
||||
// Alpha test
|
||||
// use opacityMap if it is presented
|
||||
program = getProgram(object, optionsPrograms, camera, opacityMap, 1);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
|
||||
} else {
|
||||
// do not use opacityMap at all
|
||||
program = getProgram(object, optionsPrograms, camera, null, 0);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, null);
|
||||
}
|
||||
// Use z-buffer within DrawCall, draws without blending
|
||||
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
|
||||
}
|
||||
// Transparent pass
|
||||
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
|
||||
// use opacityMap if it is presented
|
||||
if (alphaThreshold <= alpha && !opaquePass) {
|
||||
// Alpha threshold
|
||||
program = getProgram(object, optionsPrograms, camera, opacityMap, 2);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
|
||||
} else {
|
||||
// There is no Alpha threshold or check z-buffer by previous pass
|
||||
program = getProgram(object, optionsPrograms, camera, opacityMap, 0);
|
||||
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
|
||||
}
|
||||
// Do not use z-buffer, draws with blending
|
||||
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
|
||||
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
|
||||
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user