How to get Pixi Particles to behave properly when set as a child of a slot inside of a spine animation - pixi.js

protected createParticleAnimation ( data: IAnticipationParticle ): particles.Emitter {
let particleResource = PIXI.loader.resources[ data.name ].data;
let particleImages: Array<Texture> = new Array<Texture>();
let container = new particles.ParticleContainer();
container.scale.set( 1, -1 );
let spineAnim = this.anticipationAnimations[ 0 ] as spine.Spine;
spineAnim.slotContainers.forEach( ( slotContainer: Container ) => {
if ( slotContainer.name == 'CoinParticles' ) {
container.position.set( slotContainer.children[ 0 ].x * 2, slotContainer.children[ 0 ].y * 2 );
slotContainer.addChild( container );
return;
}
} );
data.images.forEach( ( image: string ) => {
particleImages.push( PIXI.Texture.fromImage( image ) );
} );
let animation = new PIXI.particles.Emitter(
container,
particleImages,
particleResource
);
animation.emit = false;
animation.autoUpdate = true;
return animation;
}
So I create my spine animation in another function then create my particle effect as shown above and attach it to a slotContainer inside of my spine animation. But when my spine animation plays the particles always follow the parents position and do not keep their world coordinates.
I believe this is because of spine but does anyone have any ideas on how to get around this?
So for example when the emitter moves the particles that have already been generate follow the x position of the emitter

So I ended up needing to override the updateTransform method of the pixi container.
I passed the emitter to the container so it could update its transform.
I needed to calculate the original point of the sprite when I did this.
This needs to be called after the emitter is created because the container needs to passed into the creation of the emitter.
Then you need to move the containers transform back to where it was originally so the children ( the particles ) can behave properly. You then move the emitters spawn position along with the parent.
import { particles, Point } from 'pixi.js';
/**
* A container to be used when attaching a particle emitter to a spine animation
*/
export class SpineParticleContainer extends particles.ParticleContainer {
/**The emitter that is drawning to the container */
protected emitter: particles.Emitter;
/**The original position of the sprite when the emitter is set */
protected origin: Point;
constructor () {
super();
}
/**
* Sets the containers emittter so it can update the emitters position
* #param emiter The particle emitter to pass in, this should be the particle emitter assigned to this container already
*/
public setEmitter ( emiter: particles.Emitter ): void {
this.emitter = emiter;
this.origin = new Point( this.parent.worldTransform.tx, this.parent.worldTransform.ty );
}
/**Override update transform to reposition the container at its origin position and move the emitter along with the animation */
public updateTransform () {
this._boundsID++;
this.worldAlpha = this.alpha * this.parent.worldAlpha;
let position = new Point( this.parent.worldTransform.tx, this.parent.worldTransform.ty );
let newPosition = new Point( this.origin.x - position.x, this.origin.y - position.y );
this.position.set( newPosition.x, newPosition.y );
this.transform.updateTransform( this.parent.transform );
for ( let i = 0, j = this.children.length; i < j; i++ ) {
const child = this.children[ i ];
if ( child.visible ) {
child.updateTransform();
}
}
this.emitter.spawnPos.set( this.parent.worldTransform.tx, this.parent.worldTransform.ty );
}
}

Related

Layout two controls in one row so that the right one is always fully visible in SWT

A UI with a label on the left and a button-like Link widget on the right, both in one row, should be layouted so that the Link is never cropped.
If there is enough space, the controls should be left-aligned:
If not enough space, the label should be decreased in width (thereby cropping its content) so that the link is still fully visible:
The snippet below does what it should. However, it uses a RowLayout and a resize listener to adjust the RowData to assign abolute width to the particular control. This contradicts the whole idea of using a layout manger.
public static void main( String[] args ) {
Display display = new Display();
Shell shell = new Shell( display );
shell.setLayout( new FillLayout() );
Composite container = new Composite( shell, SWT.NONE );
Label label = new Label( container, SWT.NONE );
label.setText( "kdsajf alkdsjf sadkf dsakfjalksd fadskjf sadlk sfdk dsflkjg" );
Link link = new Link( container, SWT.NONE );
link.setText( "<a>Do Something...</a>" );
RowLayout layout = new RowLayout();
layout.marginWidth = 0;
layout.wrap = false;
container.setLayout( layout );
label.setLayoutData( new RowData() );
link.setLayoutData( new RowData( 90, SWT.DEFAULT ) );
container.addListener( SWT.Resize, new Listener() {
#Override
public void handleEvent( Event event ) {
int linkWidth = link.computeSize( SWT.DEFAULT, SWT.DEFAULT ).x;
RowData rowData = ( RowData )link.getLayoutData();
rowData.width = linkWidth;
rowData = ( RowData )label.getLayoutData();
rowData.width = container.getClientArea().width - linkWidth - ( layout.spacing * 2 );
if( label.computeSize( SWT.DEFAULT, SWT.DEFAULT ).x < rowData.width ) {
rowData.width = SWT.DEFAULT;
}
}
} );
shell.pack();
shell.open();
while( !shell.isDisposed() ) {
if( !display.readAndDispatch() )
display.sleep();
}
display.dispose();
}
My hope is that there is a simpler solution, possibly using a regular SWT layout.

Three.js how to fade out audio?

A want to make crossfade effect between two audio.
I try Tween.JS for that, but it's not do it smoothly, how I want...
var sound_b_1 = new THREE.PositionalAudio( listener );
sound_b_1.load('mysound.ogg');
sound_b_1.setRefDistance(20);
sound_b_1.setVolume(1);
sound_b_1.autoplay = true;
scene.add(sound_b_1);
var volume = {x : 1}; // tweens not work without object
// using Tween.js
new TWEEN.Tween(volume).to({
x: 0
}, 1000).onUpdate(function() {
sound_b_1.setVolume(this.x);
}).onComplete(function() {
sound_b_1.stop();
}).start();
Hot to do that using Tween or other ways?
I do not see anything wrong with the code you provided, works fine for me, only it is not complete. You need to call TWEEN.update(time) in your render/update function:
complete code:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.z = 5;
var listener = new THREE.AudioListener();
var sound_b_1 = new THREE.PositionalAudio( listener )
sound_b_1.load('mysound.ogg');
sound_b_1.setRefDistance(20);
sound_b_1.setVolume(1);
sound_b_1.autoplay = true;
scene.add(sound_b_1);
var volume = {x : 1}; // tweens not work without object
// using Tween.js
new TWEEN.Tween(volume).to({
x: 0
}, 1000).onUpdate(function() {
sound_b_1.setVolume(this.x);
}).onComplete(function() {
sound_b_1.stop();
}).start();
var time = 0; // incrementing time variable
var render = function () {
requestAnimationFrame( render );
// normally the render render function is called 60 times a second.
// convert to milliseconds
time += ((1/60) * 1000);
TWEEN.update(time);
renderer.render(scene, camera);
};
//setTimeout(()=>{sound_b_1.stop();}, 5000);
render();
This will cause mysound.ogg to start playing at full volume and then interpolated linearly to no volume at all and then stop playing.
If you want another audio clip to start playing you just do the same thing but let the volume start at 0 and interpolate to 1.
Other solution would be using THREE.audioListener internal volume value:
var listener = new THREE.AudioListener();
new TWEEN.Tween(listener.gain.gain)
.to(
{
value: 0,
},
1000
).start()

How to write depth to a texture and read it on the next pass on a shader in DirectX11?

I'm programming a two-pass effect in DirectX 11 (SharpDX). It's supposed to write the depth to a texture in the first pass and then use that texture to extract data on the second one in the pixel shader.
What I get is a white screen, with nothing but the interface and I don't know why nothing is being printed. What could be the problem? I would say I should get at least something from the Depth Texture. Is there an easier way to obtain what I'm aiming for?
For information about what I'm doing:
This is how I'm setting the depth texture values:
this.depthBuffer = new Texture2D(device, new Texture2DDescription()
{
Format = Format.R32_Typeless,
ArraySize = 1,
MipLevels = 1,
Width = (int)host.ActualWidth,
Height = (int)host.ActualHeight,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.DepthStencil | BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
});
this.depthBufferShaderResourceView = new ShaderResourceView(this.device, this.depthBuffer, new ShaderResourceViewDescription()
{
Format = Format.R32_Float,
Dimension = ShaderResourceViewDimension.Texture2D,
Texture2D = new ShaderResourceViewDescription.Texture2DResource()
{
MipLevels = 1,
MostDetailedMip = 0,
}
});
var depthStencilDesc = new DepthStencilStateDescription()
{
DepthComparison = Comparison.LessEqual,
DepthWriteMask = global::SharpDX.Direct3D11.DepthWriteMask.All,
IsDepthEnabled = true,
};
And here is how I sample the depth in the .fx file:
int3 posTex = int3(input.p.xy, 0);
float depthPixel = DepthTexture.Load(posTex);
float4 color = float4(depthPixel, depthPixel , depthPixel, 1.0f );
return color;
And here the way I'm now setting the Depth Buffer stencil view as a Render Target in 2 passes. In the first I try to set the depthstencilview as a target. In the second pass I'm trying to set teh depth texture as a shader resource to read from it.
this.device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(this.vertexBuffer, LinesVertex.SizeInBytes, 0));
// PASS 0
this.device.ImmediateContext.OutputMerger.SetTargets(depthBufferStencilView);
this.device.ImmediateContext.ClearDepthStencilView(this.depthBufferStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
this.technique.GetPassByIndex(0).Apply(this.device.ImmediateContext);
this.device.ImmediateContext.DrawIndexed(this.geometry.Indices.Length, 0, 0);
// PASS 1
this.device.ImmediateContext.OutputMerger.ResetTargets(); // unbinding the depthStencilView
this.device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(this.vertexBuffer, LinesVertex.SizeInBytes, 0));
this.depthStencilShaderResourceVariable = effect.GetVariableByName("DepthTexture").AsShaderResource();
this.depthStencilShaderResourceVariable.SetResource(this.depthBufferShaderResourceView);
this.technique.GetPassByIndex(1).Apply(this.device.ImmediateContext);
this.device.ImmediateContext.DrawIndexed(this.geometry.Indices.Length, 0, 0);
Finally, this is how I set the two passes in the .fx file:
technique11 RenderMyTechnique
{
pass P0
{
SetDepthStencilState( DSSDepthLessEqual, 0 );
SetVertexShader ( CompileShader( vs_4_0, VShader() ) );
SetHullShader ( NULL );
SetDomainShader ( NULL );
SetGeometryShader ( NULL );
SetPixelShader ( NULL );
}
pass P1
{
SetDepthStencilState( DSSDepthLessEqual, 0 );
SetVertexShader ( CompileShader( vs_4_0, VShader() ) );
SetHullShader ( NULL );
SetDomainShader ( NULL );
SetGeometryShader ( CompileShader( gs_4_0, GShader() ) );
SetPixelShader ( CompileShader( ps_4_0, PShader() ) );
}
}
When you call:
this.device.ImmediateContext.OutputMerger.ResetTargets();
After that your perform your draw directly, you don't bind any render target to it, so you need to call:
this.device.ImmediateContext.OutputMerger.SetTargets(renderView);
renderview being any rendertarget view (can be your swapchain). For the moment you do a draw but to "nothing".
Second potential issue, you redraw the same model, but with no depth stencil, so results might be different (specially since in one version you use a Geometry Shader and not in the other one). So your depth data from previous pass might not be valid at all.

YUI3 scrollView and mousewheel

I'm starting to work only with YUI3. I include component scrollView, but it did not work mousewheel event, in the options I have not found how to turn on it. I would appreciate any help.
var scrollView = new Y.ScrollView({
id: "scrollview",
srcNode: '.scrollview-item',
height: 375,
flick: {
minDistance: 10,
minVelocity: 0.3,
axis: "y"
}
});
scrollView.render();
I stumbled upon this as well, after some trial and error, I managed to get that working(note, that it is just plain scrolling, without an easing).
var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
fixArgs = function(args) {
var a = Y.Array(args, 0, true), target;
if (Y.UA.gecko) {
a[0] = DOM_MOUSE_SCROLL;
// target = Y.config.win;
} else {
// target = Y.config.doc;
}
if (a.length < 3) {
// a[2] = target;
} else {
// a.splice(2, 0, target);
}
return a;
};
Y.Env.evt.plugins.mousewheel = {
on: function() {
return Y.Event._attach(fixArgs(arguments));
},
detach: function() {
return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
}
};
This is the YUI mousewheel event, but it's changed a bit. The biggest issue was, that originally, either the window or document elements, which makes no sense(for example when you mousewheel over the #myelement you want that to be the returned target..)
Bellow is the code used to initialize the ScrollView and the function that handles the mousewheel event:
// ScrollView
var scrollView = new Y.ScrollView({
id: "scrollview",
srcNode: '#mycontainer',
height: 490,
flick: {
minDistance:10,
minVelocity:0.3,
axis: "y"
}
});
scrollView.render();
var content = scrollView.get("contentBox");
var scroll_modifier = 10; // 10px per Delta
var current_scroll_y, scroll_to;
content.on("mousewheel", function(e) {
// check whether this is the scrollview container
if ( e.currentTarget.hasClass('container') ) {
current_scroll_y = scrollView.get('scrollY');
scroll_to = current_scroll_y - ( scroll_modifier * e.wheelDelta );
// trying to scroll above top of the container - scroll to start
if ( scroll_to <= scrollView._minScrollY ) {
// in my case, this made the scrollbars plugin to move, but I'm quite sure it's important for other stuff as well :)
scrollView._uiDimensionsChange();
scrollView.scrollTo(0, scrollView._minScrollY);
} else if ( scroll_to >= scrollView._maxScrollY ) { // trying to scroll beneath the end of the container - scroll to end
scrollView._uiDimensionsChange();
scrollView.scrollTo(0, scrollView._maxScrollY);
} else { // otherwise just scroll to the calculated Y
scrollView._uiDimensionsChange();
scrollView.scrollTo(0, scroll_to);
};
// if we have scrollbars plugin, flash the scrollbar
if ( scrollView.scrollbars ) {
scrollView.scrollbars.flash();
};
// prevent browser default behavior on mouse scroll
e.preventDefault();
};
});
So basically that's how I managed to that, but my next challenge is to get the scrollbar work like regular scrollbar(when you drag it, the container should move correspondingly...)
Hope this helps anyone :)

Drag/Move Multiple Selected Features - OpenLayers

I know that I can easily allow a user to select multiple Features/Geometries in OpenLayers but I then want enable the user to easily drag/move all of the selected features at the same time.
With the ModifyFeature control it only moves one feature at a time ... is there a way to easily extend this control (or whatever works) to move all of the selected features on that layer?
Okay, skip the ModifyFeature control and just hook into the SelectFeature control to keep track of the selected features and then use the DragControl to manipulate the selected points at the same time.
Example of the control instantiation:
var drag = new OpenLayers.Control.DragFeature(vectors, {
onStart: startDrag,
onDrag: doDrag,
onComplete: endDrag
});
var select = new OpenLayers.Control.SelectFeature(vectors, {
box: true,
multiple: true,
onSelect: addSelected,
onUnselect: clearSelected
});
Example of the event handling functions:
/* Keep track of the selected features */
function addSelected(feature) {
selectedFeatures.push(feature);
}
/* Clear the list of selected features */
function clearSelected(feature) {
selectedFeatures = [];
}
/* Feature starting to move */
function startDrag(feature, pixel) {
lastPixel = pixel;
}
/* Feature moving */
function doDrag(feature, pixel) {
for (f in selectedFeatures) {
if (feature != selectedFeatures[f]) {
var res = map.getResolution();
selectedFeatures[f].geometry.move(res * (pixel.x - lastPixel.x), res * (lastPixel.y - pixel.y));
vectors.drawFeature(selectedFeatures[f]);
}
}
lastPixel = pixel;
}
/* Featrue stopped moving */
function endDrag(feature, pixel) {
for (f in selectedFeatures) {
f.state = OpenLayers.State.UPDATE;
}
}
Hmm...
I tried the code above, and couldn't make it work. Two issues:
1) To move each feature, you need to use the original position of that feature, and add the "drag vector" from whatever feature the DragControl is moving around by itself (i.e. the feature-parameter to doDrag).
2) Since DragFeatures own code sets lastPixel=pixel before calling onDrag, the line calling move() will move the feature to (0,0).
My code looks something like this:
var lastPixels;
function startDrag(feature, pixel) {
// save hash with selected features start position
lastPixels = [];
for( var f=0; f<wfs.selectedFeatures.length; f++){
lastPixels.push({ fid: layer.selectedFeatures[f].fid,
lastPixel: map.getPixelFromLonLat( layer.selectedFeatures[f].geometry.getBounds().getCenterLonLat() )
});
}
}
function doDrag(feature, pixel) {
/* because DragFeatures own handler overwrites dragSelected.lastPixel with pixel before this is called, calculate drag vector from movement of "feature" */
var g = 0;
while( lastPixels[g].fid != feature.fid ){ g++; }
var lastPixel = lastPixels[g].lastPixel;
var currentCenter = map.getPixelFromLonLat( feature.geometry.getBounds().getCenterLonLat() );
var dragVector = { dx: currentCenter.x - lastPixel.x, dy: lastPixel.y - currentCenter.y };
for( var f=0; f<layer.selectedFeatures.length; f++){
if (feature != layer.selectedFeatures[f]) {
// get lastpixel of this feature
lastPixel = null;
var h = 0;
while( lastPixels[h].fid != layer.selectedFeatures[f].fid ){ h++; }
lastPixel = lastPixels[h].lastPixel;
var newPixel = new OpenLayers.Pixel( lastPixel.x + dragVector.dx, lastPixel.y - dragVector.dy );
// move() moves polygon feature so that centre is at location given as parameter
layer.selectedFeatures[f].move(newPixel);
}
}
}
I had a similar problem and solved it by overriding DragFeature's moveFeature function and putting this.lastPixel = pixel inside the for loop that applies the move to all features within my layer vector. Until I moved this.lastPixel = pixel inside the loop, all features except the one being dragged got crazily distorted.
`OpenLayers.Control.DragFeature.prototype.moveFeature = function (pixel) {
var res = this.map.getResolution();
for (var i = 0; i < vector.features.length; i++) {
var feature = vector.features[i];
feature .geometry.move(res * (pixel.x - this.lastPixel.x),
res * (this.lastPixel.y - pixel.y));
this.layer.drawFeature(feature );
this.lastPixel = pixel;
}
this.onDrag(this.feature, pixel);
};
`

Resources