@awayjs/scene
Version:
AwayJS scene classes
526 lines (525 loc) • 24.1 kB
JavaScript
var _a;
import { ColorTransform } from '@awayjs/core';
import { BlendMode } from '@awayjs/stage';
import { HierarchicalProperty } from '@awayjs/view';
import { Sprite } from '../display/Sprite';
import { MorphSprite } from '../display/MorphSprite';
import { DisplayObjectContainer } from '../display/DisplayObjectContainer';
var BLEND_MODES = [
'', BlendMode.NORMAL, BlendMode.LAYER,
BlendMode.MULTIPLY, BlendMode.SCREEN,
BlendMode.LIGHTEN, BlendMode.DARKEN,
BlendMode.DIFFERENCE, BlendMode.ADD,
BlendMode.SUBTRACT, BlendMode.INVERT,
BlendMode.ALPHA, BlendMode.ERASE, BlendMode.OVERLAY,
BlendMode.HARDLIGHT
];
function mapBlendIdToString(id) {
if (id === void 0) { id = 1; }
return BLEND_MODES[id] || BLEND_MODES[1];
}
var Timeline = /** @class */ (function () {
function Timeline(factory) {
this.factory = factory;
this._update_indices = [];
this._update_frames = [];
this.isButton = false;
this._initalMcID = -1;
this.numKeyFrames = 0;
this.keyframe_indices = [];
this.avm1Exports = {};
this.avm1InitActions = {};
this.avm1ButtonActions = [];
this.graphicsPool = {};
this.audioPool = {};
this.placeObjectTagsForSessionIDs = {};
this._labels = {};
this._framescripts = {};
this._framescripts_translated = {};
this.keyframe_to_frameidx = {};
}
Timeline.prototype.resetScripts = function () {
this._framescripts = {};
this._framescripts_translated = {};
};
/* should be called after timeline-streams have been set.
prepares
*/
Timeline.prototype.init = function () {
if ((this.frame_command_indices == null) || (this.frame_recipe == null) || (this.keyframe_durations == null)) {
return;
}
var frame_cnt = 0;
var ic = 0;
var ic2 = 0;
var duration = 0;
var keyframe_cnt = 0;
var last_construct_frame = 0;
this.keyframe_firstframes = [];
this.keyframe_constructframes = [];
this.keyframe_indices = [];
this.keyframe_to_frameidx = {};
this.keyframe_to_frameidx[0] = 0;
var duration_all = 0;
for (ic = 0; ic < this.numKeyFrames; ic++) {
this.keyframe_to_frameidx[ic] = duration_all;
duration = this.keyframe_durations[(ic)];
duration_all += duration;
if (this.frame_recipe[ic] & 1)
last_construct_frame = keyframe_cnt;
this.keyframe_firstframes[keyframe_cnt] = frame_cnt;
this.keyframe_constructframes[keyframe_cnt++] = last_construct_frame;
for (ic2 = 0; ic2 < duration; ic2++)
this.keyframe_indices[frame_cnt++] = ic;
}
};
/**
* for AS3:
* - called from constructor of MC
* for AS2:
* - called when decoding swf-tags to timeline-streams
* @param script -
* @param frame_idx - the index of the frame (not the keyframe-index)
* @param target_mc - the mc-instance that is calling this function
*/
Timeline.prototype.add_framescript = function (script, frame_idx, target_mc, isAVM1) {
if (isAVM1 === void 0) { isAVM1 = false; }
if (this._initalMcID >= 0 && target_mc.id != this._initalMcID)
return;
this._initalMcID = target_mc.id;
if (!this._framescripts[frame_idx]) {
this._framescripts[frame_idx] = [];
}
if (Array.isArray(script)) {
for (var i = 0; i < script.length; i++) {
this._framescripts[frame_idx][this._framescripts[frame_idx].length] = script[i];
}
}
else {
this._framescripts[frame_idx].push(script);
}
this._framescripts_translated[frame_idx] = !isAVM1;
};
/**
* get a array of framescripts for a specific frame
* for AVM1 "checkForTranslation" should be true, so we get translated framescripts
* @param target_mc
* @param frame_idx
* @param checkForTranslation
*/
Timeline.prototype.get_script_for_frame = function (target_mc, frame_idx, checkForTranslation) {
if (checkForTranslation === void 0) { checkForTranslation = false; }
if (frame_idx >= 0 && this._framescripts[frame_idx]) {
if (checkForTranslation && !this._framescripts_translated[frame_idx]) {
// todo: cleanup so we can retrieve className of target_mc without hacks
var name_1 = target_mc.className ? target_mc.className : target_mc.name;
this._framescripts[frame_idx] = this.factory.createFrameScripts(this._framescripts[frame_idx], frame_idx, name_1, target_mc.id);
this._framescripts_translated[frame_idx] = true;
}
return this._framescripts[frame_idx];
}
return null;
};
Object.defineProperty(Timeline.prototype, "numFrames", {
get: function () {
return this.keyframe_indices.length;
},
enumerable: false,
configurable: true
});
Timeline.prototype.getKeyframeIndexForFrameIndex = function (frame_index) {
return this.keyframe_indices[frame_index];
};
Timeline.prototype.getChildInstance = function (symbolID, sessionID) {
return this.factory.createChildInstanceForTimeline(this, symbolID, sessionID);
};
Timeline.prototype.extractHitArea = function (target_mc) {
target_mc.reset(false);
this.gotoFrame(target_mc, this.numFrames - 1, false);
var i = target_mc.numChildren;
var hitArea = new DisplayObjectContainer();
var child;
var originalChild;
while (i > 0) {
i--;
originalChild = target_mc.getChildAt(i);
child = originalChild.clone();
//child.reset();
child.x = originalChild.x;
child.scaleX = originalChild.scaleX;
child.y = originalChild.y;
child.scaleY = originalChild.scaleY;
child.rotationZ = originalChild.rotationZ;
hitArea.addChild(child);
}
hitArea.pickObjectFromTimeline = true;
target_mc.pickObject = hitArea;
target_mc.reset(false);
return hitArea;
};
/**
* Get the label at the current frame of the target_mc MovieClip instance.
* If the current frame has no label, it returns null
* @param target_mc
*/
Timeline.prototype.getCurrentFrameLabel = function (target_mc) {
var firstFrameIdxOfCurKeyFrame = this.keyframe_to_frameidx[target_mc.constructedKeyFrameIndex];
for (var key in this._labels) {
if (this._labels[key].keyFrameIndex == target_mc.constructedKeyFrameIndex &&
firstFrameIdxOfCurKeyFrame == target_mc.currentFrameIndex) {
return this._labels[key].name;
}
}
return null;
};
/**
* Get the label at the current frame of the target_mc MovieClip instance.
* If the current frame has no label it returns the name of the previous frame that includes a label.
* If the current frame and previous frames do not include a label, it returns null
* @param target_mc
*/
Timeline.prototype.getCurrentLabel = function (target_mc) {
var label = null;
var lastLabelframeIdx = -1;
for (var key in this._labels) {
var keyIndex = this._labels[key].keyFrameIndex;
if (keyIndex > lastLabelframeIdx && keyIndex <= target_mc.constructedKeyFrameIndex) {
lastLabelframeIdx = keyIndex;
label = this._labels[key].name;
}
}
return label;
};
Timeline.prototype.jumpToLabel = function (target_mc, label, offset) {
if (offset === void 0) { offset = 0; }
if (!this._labels[label]) {
console.warn('[TIMELINE] - jumpToLabel with invalid label', target_mc, label, offset);
return;
}
var key_frame_index = this._labels[label].keyFrameIndex;
if (key_frame_index >= 0)
target_mc.currentFrameIndex = this.keyframe_firstframes[key_frame_index] + offset;
};
/**
* Get scripts for a specific frame
* atm this is only called from AVM1MovieClip._callFrame
* @param target_mc
* @param label
* @param isAVM1
*/
Timeline.prototype.getScriptForLabel = function (target_mc, label, isAVM1) {
if (isAVM1 === void 0) { isAVM1 = false; }
var key_frame_index = this._labels[label.toLowerCase()].keyFrameIndex;
if (key_frame_index < 0)
return null;
var frameIdx = this.keyframe_firstframes[key_frame_index];
if (frameIdx >= 0 && this._framescripts[frameIdx]) {
return this.get_script_for_frame(target_mc, frameIdx, isAVM1);
}
return null;
};
/**
* move the playhead of the timeline to a specific frame
* @param target_mc
* @param frame_idx
* @param queue_script
* @param queue_pass2
* @param forceReconstruct
*/
Timeline.prototype.gotoFrame = function (target_mc, frame_idx, queue_script, queue_pass2, forceReconstruct) {
if (queue_script === void 0) { queue_script = true; }
if (queue_pass2 === void 0) { queue_pass2 = false; }
if (forceReconstruct === void 0) { forceReconstruct = false; }
var current_keyframe_idx = target_mc.constructedKeyFrameIndex;
var target_keyframe_idx = this.keyframe_indices[frame_idx];
if (current_keyframe_idx == target_keyframe_idx && !forceReconstruct) {
target_mc.adapter.queueFrameScripts(this, frame_idx, queue_script);
return;
}
else if (current_keyframe_idx + 1 == target_keyframe_idx) {
// target_keyframe_idx is the next keyframe. we can just use constructnext for this
this.constructNextFrame(target_mc, queue_script, true);
return;
}
// when constructing a frame we must start constructing
// either at the beginning of the timeline, or at a frame where all object was removed
// construct_keyframe_idx is the index of the first keyframe we must process
var construct_keyframe_idx = this.keyframe_constructframes[target_keyframe_idx];
// 3 keyframes:
// - current_keyframe_idx
// - target_keyframe_idx
// - construct_keyframe_idx
// normally construction must start at construct_keyframe_idx
// if we jump forward, and target_keyframe_idx >
var start_construct_idx = construct_keyframe_idx;
var jump_forward = (target_keyframe_idx > current_keyframe_idx);
if (jump_forward && current_keyframe_idx > construct_keyframe_idx)
start_construct_idx = current_keyframe_idx + 1;
target_mc.adapter.constructFrame(this, start_construct_idx, target_keyframe_idx, jump_forward, frame_idx, queue_pass2, queue_script);
// apply update commands for objects still on stage (only if they are not blocked by script)
this.applyCollectedUpdateCommands(target_mc);
target_mc.constructedKeyFrameIndex = target_keyframe_idx;
};
Timeline.prototype.applyCollectedUpdateCommands = function (target_mc) {
var k;
var len = this._update_indices.length;
for (k = 0; k < len; k++)
this.update_childs(target_mc, this._update_indices[k], this._update_frames[k]);
};
/**
* constructs the next frame of the timeline
* expects the target_mc.currentFrameIndex to already be set to the next frame
* @param target_mc
* @param queueScript
* @param scriptPass1
*/
Timeline.prototype.constructNextFrame = function (target_mc, queueScript, scriptPass1) {
if (queueScript === void 0) { queueScript = true; }
if (scriptPass1 === void 0) { scriptPass1 = false; }
var frameIndex = target_mc.currentFrameIndex;
var new_keyFrameIndex = this.keyframe_indices[frameIndex];
if (queueScript)
target_mc.adapter.queueFrameScripts(this, frameIndex, scriptPass1);
if (target_mc.constructedKeyFrameIndex != new_keyFrameIndex) {
target_mc.constructedKeyFrameIndex = new_keyFrameIndex;
var frame_command_idx = this.frame_command_indices[new_keyFrameIndex];
var frame_recipe = this.frame_recipe[new_keyFrameIndex];
if (frame_recipe & 1) {
target_mc.adapter.removeAllTimelineChilds();
}
else if (frame_recipe & 2) {
this.remove_childs_continous(target_mc, frame_command_idx++);
}
if (frame_recipe & 4)
this.add_childs_continous(target_mc, frame_command_idx++);
if (frame_recipe & 8)
this.update_childs(target_mc, frame_command_idx++);
if (frame_recipe & 16)
this.start_sounds(target_mc, frame_command_idx++);
}
};
Timeline.prototype.remove_childs_continous = function (sourceMovieClip, frame_command_idx) {
var start_index = this.command_index_stream[frame_command_idx];
var end_index = start_index + this.command_length_stream[frame_command_idx];
for (var i = start_index; i < end_index; i++) {
// in avm1 we remove by depth, in avm2 we remove by sessionID
sourceMovieClip.adapter.removeTimelineChildAt(this.remove_child_stream[i]);
}
};
Timeline.prototype.add_childs_continous = function (target_mc, frame_command_idx) {
var idx;
var start_index = this.command_index_stream[frame_command_idx];
var end_index = start_index + this.command_length_stream[frame_command_idx];
for (var i = start_index; i < end_index; i++) {
idx = i * 3;
if (typeof this.add_child_stream[idx] === 'undefined') {
console.warn('[Timeline] - add_childs_continous - could not find child-id in child_stream for idx', idx, this.add_child_stream);
continue;
}
var childAsset = this.factory.createChildInstanceForTimeline(this, this.add_child_stream[idx + 2], this.add_child_stream[idx]);
target_mc.adapter.addTimelineChildAtDepth(childAsset, this.add_child_stream[idx + 1]);
}
};
Timeline.prototype.start_sounds = function (target_mc, frame_command_idx) {
var start_index = this.command_index_stream[frame_command_idx];
var end_index = start_index + this.command_length_stream[frame_command_idx];
for (var i = start_index; i < end_index; i++) {
var audioProps = this.audioPool[this.add_sounds_stream[i]];
if (audioProps) {
var sound = audioProps.sound;
if (!sound) {
console.warn('[Timeline] Missing sound in audioProps!', audioProps);
return;
}
if (audioProps.cmd == 15) { // start sound
target_mc.startSound(sound, audioProps.props.loopCount || 0);
}
else if (audioProps.cmd == 16) { // stop sound
target_mc.stopSound(sound);
}
//console.log("start sound:", child);
}
}
};
Timeline.prototype.update_childs = function (target_mc, frame_command_idx, frameIdx) {
if (frameIdx === void 0) { frameIdx = -1; }
var start = this.command_index_stream[frame_command_idx];
var end = start + this.command_length_stream[frame_command_idx];
for (var i = start; i < end; i++) {
var child = target_mc.getTimelineChildAtSessionID(this.update_child_stream[i]);
if (!child) {
continue;
}
// check if the child is blocked by script for transform
this._blocked = !!(child._adapter && child.adapter.isBlockedByScript());
var propsStart = this.update_child_props_indices_stream[i];
var propsEnd = propsStart + this.update_child_props_length_stream[i];
for (var p = propsStart; p < propsEnd; p++) {
Timeline.applyFunctionMap[this.property_type_stream[p]](this, child, target_mc, this.property_index_stream[p]);
}
}
};
Timeline.update_mtx_all = function (timeline, child, _target_mc, i) {
if (timeline._blocked || child.noTimelineUpdate)
return;
i *= 6;
var new_matrix = child.transform.matrix3D;
var props_stream = timeline.properties_stream_f32_mtx_all;
new_matrix._rawData[0] = props_stream[i++];
new_matrix._rawData[1] = props_stream[i++];
new_matrix._rawData[4] = props_stream[i++];
new_matrix._rawData[5] = props_stream[i++];
new_matrix._rawData[12] = props_stream[i++];
new_matrix._rawData[13] = props_stream[i];
child.transform.invalidateComponents();
};
Timeline.update_colortransform = function (timeline, child, _target_mc, i) {
if (child._adapter && child.adapter.isColorTransformByScript())
return;
i *= 8;
var props_stream = timeline.properties_stream_f32_ct;
var new_ct = child.transform.colorTransform || (child.transform.colorTransform = new ColorTransform());
new_ct._rawData[0] = props_stream[i++];
new_ct._rawData[1] = props_stream[i++];
new_ct._rawData[2] = props_stream[i++];
new_ct._rawData[3] = props_stream[i++];
new_ct._rawData[4] = props_stream[i++];
new_ct._rawData[5] = props_stream[i++];
new_ct._rawData[6] = props_stream[i++];
new_ct._rawData[7] = props_stream[i];
child.transform.invalidateColorTransform();
};
Timeline.update_masks = function (timeline, child, target_mc, i) {
// an object could have a multiple groups of masks, in case a graphic clip was merged into the timeline
// this is not implemented in the runtime yet
// for now, a second mask-group would overwrite the first one
var masks = [];
var numMasks = timeline.properties_stream_int[i++];
if (numMasks === 0) {
child.updateTimelineMask(null);
return;
}
//mask may not exist if a goto command moves the playhead to a point in the timeline after
//one of the masks in a mask array has already been removed. Therefore a check is needed.
for (var m = 0; m < numMasks; m++) {
masks.push(target_mc.getTimelineChildAtSessionID(timeline.properties_stream_int[i + m]));
}
child.updateTimelineMask(masks);
};
Timeline.update_name = function (timeline, child, target_mc, i) {
if (timeline.properties_stream_strings[i].indexOf('instance') == 0) {
return;
}
target_mc.adapter.unregisterScriptObject(child);
child.name = timeline.properties_stream_strings[i];
target_mc.adapter.registerScriptObject(child);
};
Timeline.update_button_name = function (timeline, target, sourceMovieClip, i) {
target.name = timeline.properties_stream_strings[i];
target.addButtonListeners();
sourceMovieClip.adapter.registerScriptObject(target);
};
Timeline.update_visibility = function (_timeline, child, target_mc, i) {
if (!child._adapter || !child.adapter.isVisibilityByScript()) {
child.visible = !!i;
}
};
Timeline.update_mtx_scale_rot = function (timeline, child, _target_mc, i) {
if (timeline._blocked || child.noTimelineUpdate) {
return;
}
i *= 4;
var new_matrix = child.transform.matrix3D;
var props_stream = timeline.properties_stream_f32_mtx_scale_rot;
new_matrix._rawData[0] = props_stream[i++];
new_matrix._rawData[1] = props_stream[i++];
new_matrix._rawData[4] = props_stream[i++];
new_matrix._rawData[5] = props_stream[i];
child.transform.invalidateComponents();
child._invalidateHierarchicalProperty(HierarchicalProperty.SCENE_TRANSFORM);
};
Timeline.update_mtx_pos = function (timeline, child, target_mc, i) {
if (timeline._blocked || child.noTimelineUpdate)
return;
i *= 2;
var new_matrix = child.transform.matrix3D;
new_matrix._rawData[12] = timeline.properties_stream_f32_mtx_pos[i++];
new_matrix._rawData[13] = timeline.properties_stream_f32_mtx_pos[i];
child.transform.invalidatePosition();
};
Timeline.enable_maskmode = function (_timeline, child, _target_mc, _i) {
child.maskMode = true;
};
Timeline.remove_masks = function (_timeline, child, _target_mc, _i) {
child.updateTimelineMask(null);
};
Timeline.update_filters = function (timeline, child, _target_mc, i) {
var startFilter = timeline.properties_stream_int[i++];
var numFilter = timeline.properties_stream_int[i++];
var adapter = child.adapter;
if (numFilter === 0) {
adapter.updateFilters(null);
return;
}
adapter.updateFilters(timeline.properties_stream_filters.slice(startFilter, startFilter + numFilter));
};
Timeline.swap_graphics = function (timeline, child, _target_mc, i) {
if (!child.isAsset(Sprite)) {
console.warn('[Timeline] - swap_graphics - child is not a Sprite');
return;
}
var sprite = child;
var target = timeline.graphicsPool[timeline.properties_stream_int[i]];
if (target.id === sprite.graphics.id) {
// already the same graphics
return;
}
sprite.graphics = target;
};
Timeline.start_audio = function (_timeline, _child, _target_mc, _i) { };
Timeline.set_ratio = function (_timeline, child, _target_mc, i) {
if (!child.isAsset(MorphSprite)) {
console.warn('[Timeline] - set_ratio - child is not a MorphSprite');
return;
}
child.setRatio(_timeline.properties_stream_int[i] / 0xffff);
};
Timeline.update_blendmode = function (_timeline, child, _target_mc, i) {
child.blendMode = mapBlendIdToString(i);
};
Timeline.update_rendermode = function (_timeline, _child, _target_mc, i) {
console.log('update rendermode ' + i);
};
Timeline.prototype.dispose = function () {
this.keyframe_indices = [];
this.avm1Exports = {};
this.avm1InitActions = {};
this.avm1ButtonActions = [];
this.graphicsPool = {};
this.audioPool = {};
this.placeObjectTagsForSessionIDs = {};
this._labels = {};
this._framescripts = {};
this._framescripts_translated = {};
};
Timeline.applyFunctionMap = (_a = {},
_a[1 /* AType.UPDATE_MTX */] = Timeline.update_mtx_all,
_a[2 /* AType.UPDATE_CMTX */] = Timeline.update_colortransform,
_a[3 /* AType.UPDATE_MASKS */] = Timeline.update_masks,
_a[4 /* AType.UPDATE_NAME */] = Timeline.update_name,
_a[5 /* AType.UPDATE_BUTTON_NAME */] = Timeline.update_button_name,
_a[6 /* AType.UPDATE_VISIBLE */] = Timeline.update_visibility,
_a[7 /* AType.UPDATE_BLENDMODE */] = Timeline.update_blendmode,
_a[8 /* AType.UPDATE_RENDERMODE */] = Timeline.update_rendermode,
_a[9 /* AType.UPDATE_FILTERS */] = Timeline.update_filters,
_a[11 /* AType.UPDATE_SCALE_ROT */] = Timeline.update_mtx_scale_rot,
_a[12 /* AType.UPDATE_POS */] = Timeline.update_mtx_pos,
_a[200 /* AType.ENABLE_MASKMODE */] = Timeline.enable_maskmode,
_a[201 /* AType.REMOVE_MASK */] = Timeline.remove_masks,
_a[202 /* AType.SWAP_GRAPHICS */] = Timeline.swap_graphics,
_a[203 /* AType.SET_RATIO */] = Timeline.set_ratio,
_a[204 /* AType.START_AUDIO */] = Timeline.start_audio,
_a);
return Timeline;
}());
export { Timeline };