I am making a flame using the THREE.js and spark.js, but when I render the world I can't see the flame and the world is empty. I saw the console for the error but there is no error regarding this. I tried a lot but can't find out the actual error. Here is the code.
threexSparks = new THREEx.Sparks({
maxParticles : 400,
counter : new SPARKS.SteadyCounter(300)
});
//threexSparks.position.x = 1000;
// setup the emitter
//var emitter = threexSparks.emitter();
var counter = new SPARKS.SteadyCounter(500);
var emitter = new SPARKS.Emitter(counter);
var initColorSize = function() {
this.initialize = function(emitter, particle) {
particle.target.color().setHSV(0.3, 0.9, 0.4);
particle.target.size(150);
};
};
emitter.addInitializer(new initColorSize());
emitter.addInitializer(new SPARKS.Position(new SPARKS.PointZone(new THREE.Vector3(1000, 0, 0))));
emitter.addInitializer(new SPARKS.Lifetime(0, 0.8));
emitter.addInitializer(new SPARKS.Velocity(new SPARKS.PointZone(new THREE.Vector3(0, 250, 00))));
emitter.addAction(new SPARKS.Age());
emitter.addAction(new SPARKS.Move());
emitter.addAction(new SPARKS.RandomDrift(1000, 0, 1000));
emitter.addAction(new SPARKS.Accelerate(0, -200, 0));
Thanks
Tere is strange problems with particles and WebGL render. It will be good if you're using CanvasRender. But for WebGL not.
Also in your code you forgot about creating threejs objects for particles. Sparks.js allows only interface for particles. But you need to create particles itself.
You can look at my jsfiddle example. there I use modified version of sparks.js library. Changes just to be able to override VectorPool behaviour.
http://jsfiddle.net/YeJ4X/35/
Main part there is:
var particleCount = 1800,
particles = new THREE.Geometry(), //store particle vertices
pMaterial = new THREE.ParticleBasicMaterial({
size: 10,
map: txture, //in jsfiddle i create texture from canvas
transparent: true
});
var particleSystem = new THREE.ParticleSystem(particles, pMaterial); //threejs particle system
//initialize our particles (and set that are dirty). sparkjs initialize it later for us
for(var p = 0; p < particleCount; p++) {
v = new THREE.Vector3(numMax,numMax,numMax);
v.isDirty=true;
particles.vertices.push(v);
}
SPARKS.VectorPool.__pools = particles.vertices; //initialize vectors pool
And there is my new vector pool for sparksjs
SPARKS.VectorPool = {
__pools: [],
get: function() {
var ret = _.find(this.__pools, function(v){return v.isDirty});
ret.isDirty=false;
return ret;
},
release: function(v) {
v.isDirty=true;
v.set(numMax,numMax,numMax);
}
};
Of course you must care about count of partices that are used in sparks.js and precreated by hands.
My sparkjs fork is here: https://github.com/elephanter/sparks.js there I fix problem with lastest tweenjs and add other little changes I described before.
Related
I'm attempting to display a list of renderer/scene/camera "groups" on the same html page. I ran into problems when trying to implement a render loop on all of them, using requestAnimationFrame. I am, however, unable to successfully render even a single frame by looping over the list.
calling renderer.render on each of the "groups" in the same function that I instantiate them in works fine. I refactored the code to allow calling render on each of the renderers outside of said function, and my blue spheres disappear;
I have some elements...
<div class="1"></div>
<div class="2"></div>
<div class="3"></div>
<div class="4"></div>
that I add some threeJs things to
$(document).ready(function(){
addResizeandCenterListener(window, $("#container"), 4/3);
addResizeListener($("#container"), $(".playerhalf"), 2/1);
for (let item of [".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8"]) {
var threeDetails = addThreeRendererToElem($(item), item);
var scene = threeDetails["scene"];
addClickListenerToElem($(item), scene);
}
});
threeList = [];
//this adds a renderer/scene/camera to the element "elem"
function addThreeRendererToElem(elem, name){
var width = elem.innerWidth();
var height = elem.innerHeight();
var scene = newScene();
scene.name = name;
addDefaultLight(scene);
addBlueBall(scene);
var camera = addDefaultCamera(scene, width, height);
var renderer = newRenderer(width, height);
elem[0].appendChild(renderer.domElement);
//this works fine regardless of how many renderer/scene/camera groups I have placed in the DOM
//renderer.render(scene, camera);
var threeDict = {"renderer":renderer, "scene":scene, "camera":camera}
threeList.push(threeDict)
//this does not work, even if I only place a single renderer/scene camera group in the DOM
renderList();
}
function renderList() {
var tlist = threeList;
for (var i = 0; i< tlist;i++) {
var threeDict = tlist[i];
threeDict["renderer"].render(threeDict["scene"],
threeDict["camera"]);
}
}
function newScene(){
scene = new THREE.Scene();
return scene;
}
function newCamera(w, h){
camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 100);
return camera;
}
function newRenderer(w, h){
renderer = new THREE.WebGLRenderer({alpha:true, antialias:true});
renderer.setSize(w, h);
return renderer;
}
function addObjectToScene(obj, scene){
scene.add(obj);
}
function ballObj(color){
var ballGeometry = new THREE.SphereGeometry(3, 16, 16);
var ballMaterial = new THREE.MeshPhongMaterial({color: 0x33aaff});
var ball = new THREE.Mesh(ballGeometry, ballMaterial);
return ball;
}
function spotLightObj(){
var spot = new THREE.SpotLight(0xffffff);
return spot;
}
function addDefaultCamera(scene, width, height){
var camera = newCamera(width, height);
camera.position.set(1,1,20);
camera.lookAt(scene.position);
return camera;
}
function addDefaultLight(scene){
var spot = spotLightObj();
spot.position.set(1, 10, 4);
addObjectToScene(spot, scene);
}
function addBlueBall(scene){
var hexcolor = "0x33aaff";
bball = ballObj(hexcolor);
addObjectToScene(bball, scene);
}
I expect to see some blue spheres, like when I am calling render from inside addThreeRendererToElem, but I don't. Instead, the canvases are all blank. The canvases are in the correct places on the web page, inspecting threeList in the console shows that all the objects are indeed there. There are no error messages.
Incorrect ending condition on your for-loop inside the renderList function.
You are missing .length. Should be:
function renderList() {
var tlist = threeList;
for ( var i = 0; i < tlist.length; i++ ) {
var threeDict = tlist[i];
threeDict["renderer"].render( threeDict["scene"], threeDict["camera"] );
}
}
I have created a custom component in a-frame that creates a bespoke geometry. I am using the tick function to animate the update of the geometry's vertices. That is all working fine, however the shadow on the geometry does not update.
In my below example I toggle a flat shape between 2 states, folded and unfolded. When, for example it animates from folded to unfolded, the faces retain the shadow as though it were still folded.
Here is the HTML, please see <a-fold> in this example starting with the folded state (this can be changed to 'unfolded' by changing that attribute).
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
</head>
<body>
<a-scene test>
<a-fold
state="folded"
color="blue"
position="-7.5 1 -20">
</a-fold>
<a-plane
position="0 0 -4"
rotation="-90 0 0"
width="20"
height="20"
color="#7BC8A4">
</a-plane>
<a-sky
color="#ECECEC">
</a-sky>
</a-scene>
</body>
</html>
and the javascript
//a trigger to test state change
AFRAME.registerComponent('test', {
init: function () {
setTimeout(function(){
var target = document.querySelector('a-fold');
target.setAttribute('changestate', true);
}, 2000);
}
});
//component
AFRAME.registerComponent('folder', {
schema: {
state: {type: 'string', default: 'unfolded'},
color: {type: 'color', default: '#000'},
changestate: {type: 'boolean', default: false},
changedur: {type: 'number', default: 400},
},
store: {
unfolded: [{"x":15,"y":0,"z":0},{"x":15,"y":15,"z":0},{"x":0,"y":15,"z":0},{"x":0,"y":0,"z":0},{"x":0,"y":15,"z":0},{"x":15,"y":0,"z":0},],
folded: [{"x":15,"y":0,"z":0},{"x":12,"y":12,"z":5},{"x":0,"y":15,"z":0},{"x":3,"y":3,"z":5},{"x":0,"y":15,"z":0},{"x":15,"y":0,"z":0},],
},
update: function () {
this.geometry = new THREE.Geometry();
var geometry = this.geometry
var verts = this.store[this.data.state]
$.each( verts, function( i, v3 ) {
geometry.vertices.push ( new THREE.Vector3(v3.x, v3.y, v3.z) );
});
geometry.faces.push(new THREE.Face3(0, 1, 2))
geometry.faces.push(new THREE.Face3(3, 4, 5))
geometry.computeBoundingBox();
geometry.computeFaceNormals();
geometry.computeVertexNormals();
this.material = new THREE.MeshStandardMaterial({color: this.data.color});
this.material.side = THREE.DoubleSide;
this.material.needsUpdate = true
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.mesh.castShadow = true;
this.mesh.receiveShadow = true;
this.el.setObject3D('mesh', this.mesh);
},
tick: function (t, td) {
if (this.data.changestate === true){
var dur = this.data.changedur
var geom = this.el.getObject3D('mesh').geometry
var currVerts = geom.vertices
var toVerts = this.store[this.gotoState()]
var somethingChanged = false;
var allAxis = ["x", "y", "z"];
var thisParent = this
$.each( currVerts, function( i, vert) {
var curr = currVerts[i]
var to = toVerts[i]
$.each( allAxis, function( i, axis ) {
if (thisParent.approxEqual(curr[axis], to[axis])) {
somethingChanged = somethingChanged || false;
} else if (curr[axis] < to[axis]) {
var step = thisParent.stepCalc(curr[axis], to[axis], dur, td)
curr[axis] += step;
somethingChanged = true;
} else {
var step = thisParent.stepCalc(curr[axis], to[axis], dur, td)
curr[axis] -= step;
somethingChanged = true;
}
});
});
geom.verticesNeedUpdate = somethingChanged;
}
},
gotoState: function (){
var to = ""
var current = this.data.state
var states = Object.keys(this.store)
$.each( states, function( i, state) {
if ( state != current ){
to = state
}
});
return to;
},
approxEqual: function (x, y) {
return Math.abs(x - y) < 0.00001;
},
stepCalc: function (curr, to, dur, delta){
var distance = Math.abs(curr - to)
var speed = distance/dur
var step = speed*delta
return step;
},
remove: function () {
this.el.removeObject3D('mesh');
}
});
//primitive
AFRAME.registerPrimitive('a-fold', {
defaultComponents: {
folder: {}
},
mappings: {
state: 'folder.state',
color: 'folder.color',
changestate: 'folder.changestate',
changedur: 'folder.changedur'
}
});
Sorry, I know its a lot but I don't want to simplify it in case I lose the problem. As you can see in the component I have tried to add this.material.needsUpdate = true as well as adding this.mesh.castShadow = true and this.mesh.receiveShadow = true but it does not make a difference.
On a side note, if I do an animation of the whole entity (ie a rotation) I can see the material reflecting the light so the material does respond to lighting dynamically, just not when I change the shape by updating the vertices.
Here are 2 images that demonstrate what I mean.
You can see that in 2nd image, although the plane is flat, its shadows suggest it has a fold. And it does the same the other way around (ie if it starts unfolded, the shadow is correct and that persists when it folds).
Here is a js fiddle
Any more info needed, please ask. Any help is much appreciated as ever.
Lighting is calculated using normal vectors, in your case you're moving the mesh vertices but leaving the normal vectors, which is why the lighting doesn't change. You need to add geometry.computeVertexNormals(); in your tick function after you've altered the vertices.
In 3D shadows are different to shading or lighting, the problem you're having is related to lighting but not shadows. Which is why adjusting the mesh.castShadow properties didn't behave like you expected.
I found this great tutorial on creating draggable maps with inertia: http://www.emanueleferonato.com/2016/01/18/how-to-create-a-html-draggable-and-scrollable-map-with-inertia-using-phaser-framework/
The tutorial has the dragging effect applied on just an image. I'm trying to have the dragging effect applied on a group of sprites instead, so they would all drag at the same time (map image + group of sprites).
The issue I have is that I'm a little confused about what this.scrollingMap represents syntax-wise. So when it comes to replacing this line with a group, I'm a little lost.
this.scrollingMap = game.add.image(0, 0, "map");
Anyone have any ideas?
I copied the simplified code below as well if that helps.
var game = new Phaser.Game(window.innerWidth, window.innerHeight, Phaser.AUTO, '', { preload: preload, create: create, update: update });
function preload() {
game.load.image('map', 'assets/images/baseMap.png');
game.load.image('star', 'assets/images/star.png');
}
function create() {
// Creating the group
world = game.add.group();
world.create(0, 0, 'map');
// Adding random sprites to it
for (var i = 0; i < 10; i++)
{ world.create(game.world.randomX, game.world.randomY, 'star');}
//This group works on its own.
//I would like to link it to the dragging animation below "scrollingMap".
//The Draggable Map from the tutorial
// Adding the big map to scroll
this.scrollingMap = game.add.image(0, 0, "map"); //<-- This is where I am having trouble changing from an image to a group.
// map will accept inputs
this.scrollingMap.inputEnabled = true;
// map can be dragged
this.scrollingMap.input.enableDrag(false);
// custom property: we save map position
this.scrollingMap.savedPosition = new Phaser.Point(this.scrollingMap.x, this.scrollingMap.y);
// custom property: the map is not being dragged at the moment
this.scrollingMap.isBeingDragged = false;
// custom property: map is not moving (or is moving at no speed)
this.scrollingMap.movingSpeed = 0;
// map can be dragged only if it entirely remains into this rectangle
this.scrollingMap.input.boundsRect = new Phaser.Rectangle(game.width - this.scrollingMap.width, game.height - this.scrollingMap.height, this.scrollingMap.width * 2 - game.width, this.scrollingMap.height * 2 - game.height);
// when the player starts dragging...
this.scrollingMap.events.onDragStart.add(function(){
this.scrollingMap.isBeingDragged = true;
// set movingSpeed property to zero. This will stop moving the map
// if the player wants to drag when it's already moving
this.scrollingMap.movingSpeed = 0;
}, this);
// when the player stops dragging...
this.scrollingMap.events.onDragStop.add(function(){
this.scrollingMap.isBeingDragged = false;
}, this);
} //End create function
function update() {
// if the map is being dragged...
if(this.scrollingMap.isBeingDragged){
this.scrollingMap.savedPosition = new Phaser.Point(this.scrollingMap.x, this.scrollingMap.y);
}
// if the map is NOT being dragged...
else{
// if the moving speed is greater than 1...
if(this.scrollingMap.movingSpeed > 1){
this.scrollingMap.x += this.scrollingMap.movingSpeed * Math.cos(this.scrollingMap.movingangle);
this.scrollingMap.y += this.scrollingMap.movingSpeed * Math.sin(this.scrollingMap.movingangle);
if(this.scrollingMap.x < game.width - this.scrollingMap.width){
this.scrollingMap.x = game.width - this.scrollingMap.width;
}
if(this.scrollingMap.x > 0){
this.scrollingMap.x = 0;
}
if(this.scrollingMap.y < game.height - this.scrollingMap.height){
this.scrollingMap.y = game.height - this.scrollingMap.height;
}
if(this.scrollingMap.y > 0){
this.scrollingMap.y = 0;
}
this.scrollingMap.movingSpeed *= friction;
// save current map position
this.scrollingMap.savedPosition = new Phaser.Point(this.scrollingMap.x, this.scrollingMap.y);
}
// if the moving speed is less than 1...
else{
var distance = this.scrollingMap.savedPosition.distance(this.scrollingMap.position);
var angle = this.scrollingMap.savedPosition.angle(this.scrollingMap.position);
if(distance > 4){
this.scrollingMap.movingSpeed = distance * speedMult;
this.scrollingMap.movingangle = angle;
}
}
}
}
So, I ended up finding the solution...
First off, I removed all this.scrollingMap and changed them to scrollingMap to remove any confusion. Ended up stil working perfectly.
scrollingMap = game.add.image(0, 0, "map");
scrollingMap.anchor.set(0.05,0.5);
scrollingMap.inputEnabled = true;
[etc...]
Next, Groups in Phaser don't seem to be able to have input working on elements together, only one at a time. So changing to something like wouldn't work:
scrollingMap = game.add.group();
map = game.add.image(0, 0, "map");
scrollingMap.add(map);
// The following line won't work
scrollingMap.inputEnabled = true;
I tried using the Align functions that Phaser offers... Until I ended up realising that you can actually nest sprites within other sprites like so:
scrollingMap = game.add.image(0, 0, "map");
someSprite = game.add.image(100, 100, "sprite");
scrollingMap.addChild(someSprite);
And voila! There's the solution, as simple as that.
Note that you can also add groups as children:
someGroup = game.add.group();
scrollingMap.addChild(someGroup);
Found the solution here if anyone's curious:
http://www.html5gamedevs.com/topic/7745-move-a-group-of-sprites-together-as-one-body/
I am creating this 3d graph that represents a network of people. The nodes in the network are the names of people and between them lines are drawn whi9ch represents a connection. The purpose of this network is that people can turn it around and look through it to see what their network looks like, however, when I turn the camera around the network, the 3d text that I creat3ed with textgeometry is stuck to the same position and unformtunately therefore unreadable for the user when they want to look at the network from a different side. I know that this question has been asked several times before and I have read most of the answers on here, but I still cannot get it to work.
The problem that I encounter mostly is that when I try to do something like this:
nodeRenderer.lookAt(camera.position);
I get a problem that the camera is not defined. The camera is added to the scene, however this happens in a different .js document. This is where the scene gets created and the camera gets added (/ngraph.three/index.js):
var THREE = require('./lib/three');
module.exports = function (graph, settings) {
var merge = require('ngraph.merge');
settings = merge(settings, {
interactive: true
});
var beforeFrameRender;
var isStable = false;
var disposed = false;
var layout = createLayout(settings);
var renderer = createRenderer(settings);
var camera = createCamera(settings);
var scene = settings.scene || new THREE.Scene();
(...)
function renderNode(nodeId) {
nodeRenderer(nodeUI[nodeId]);
}
(...)
function initNode(node) {
var ui = nodeUIBuilder(node);
if (!ui) return;
// augment it with position data:
ui.pos = layout.getNodePosition(node.id);
// and store for subsequent use:
nodeUI[node.id] = ui;
scene.add(ui);
}
(...)
function createCamera(settings) {
if (settings.camera) {
return settings.camera;
}
var container = renderer.domElement;
var camera = new THREE.PerspectiveCamera(75, container.clientWidth/container.clientHeight, 0.1, 3000);
camera.position.z = 400;
return camera;
}
And this is where the nodes get created (ngraph.three/lib/defaults.js):
var THREE = require('./three');
module.exports.createNodeUI = createNodeUI;
module.exports.createLinkUI = createLinkUI;
module.exports.nodeRenderer = nodeRenderer;
module.exports.linkRenderer = linkRenderer;
function createNodeUI(node) {
var nodeMaterial = new THREE.MeshFaceMaterial( [
new THREE.MeshPhongMaterial( { color: 0x00cccc, shading: THREE.FlatShading } ), // front
new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) // side
] );
var nodeGeometry = new THREE.TextGeometry( node.data, { size: 5, height: 2, curveSegments: 6, font: "helvetiker", weight: "normal", style: "normal" });
var nodeDirection = new THREE.Quaternion (THREE.Camera.Quaternion);
return new THREE.Mesh(nodeGeometry, nodeMaterial);
}
function createLinkUI(link) {
var linkGeometry = new THREE.Geometry();
linkGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
linkGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
var linkMaterial = new THREE.LineBasicMaterial({ color: 0x00cccc });
return new THREE.Line(linkGeometry, linkMaterial);
}
(...)
function nodeRenderer(node) {
node.position.x = node.pos.x;
node.position.y = node.pos.y;
node.position.z = node.pos.z;
}
(...)
I tried ui.quaternion = camera.quaternion; in /ngraph.three/index.js, but this didn't do anything, and I've tried nodeUI.lookAt(camera.position); but this gave an error: TypeError: nodeUI.lookAt is not a function.
PS: I'm using Three.js Rev. 68, Ngraph and node.js.
I can only guess since a lot of code is missing but your createNodeUI returns a THREE.Mesh which is a subclass of Object3D which has the function .lookAt(vector) to align its local z-axis to the given vector.
And you said you receive this Error : TypeError: nodeUI.lookAt is not a function that looks like you re trying to call .lookAt of the array and not of the node itself.
so try nodeUI[node.id].lookAt(camera.position)
I'm trying to create an online web tool for eeg signal analysis. The tool suppose to display a graph of an eeg signal synchronize with a movie that was display to a subject.
I've already implemented it successfully on csharp but I can't find a way to do it easily with any of the know javascript chart that I saw.
A link of a good tool that do something similar can be found here:
http://www.mesta-automation.com/real-time-line-charts-with-wpf-and-dynamic-data-display/
I've tried using dygraph, and google chart. I know that it should be relatively easy to create an background thread on the server that examine the movie state every ~50ms. What I was not able to do is to create a marker of the movie position on the chart itself dynamically. I was able to draw on the dygraph but was not able to change the marker location.
just for clarification, I need to draw a vertical line as a marker.
I'm in great suffering. Please help :)
Thanks to Danvk I figure out how to do it.
Below is a jsfiddler links that demonstrate such a solution.
http://jsfiddle.net/ng9vy8mb/10/embedded/result/
below is the javascript code that do the task. It changes the location of the marker in synchronizing with the video.
There are still several improvement that can be done.
Currently, if the user had zoomed in the graph and then click on it, the zoom will be reset.
there is no support for you tube movies
I hope that soon I can post a more complete solution that will also enable user to upload the graph data and video from their computer
;
var dc;
var g;
var v;
var my_graph;
var my_area;
var current_time = 0;
//when the document is done loading, intialie the video events listeners
$(document).ready(function () {
v = document.getElementsByTagName('video')[0];
v.onseeking = function () {
current_time = v.currentTime * 1000;
draw_marker();
};
v.oncanplay = function () {
CreateGraph();
};
v.addEventListener('timeupdate', function (event) {
var t = document.getElementById('time');
t.innerHTML = v.currentTime;
g.updateOptions({
isZoomedIgnoreProgrammaticZoom: true
});
current_time = v.currentTime * 1000;
}, false);
});
function change_movie_position(e, x, points) {
v.currentTime = x / 1000;
}
function draw_marker() {
dc.fillStyle = "rgba(255, 0, 0, 0.5)";
var left = my_graph.toDomCoords(current_time, 0)[0] - 2;
var right = my_graph.toDomCoords(current_time + 2, 0)[0] + 2;
dc.fillRect(left, my_area.y, right - left, my_area.h);
};
//data creation
function CreateGraph() {
number_of_samples = v.duration * 1000;
// A basic sinusoidal data series.
var data = [];
for (var i = 0; i < number_of_samples; i++) {
var base = 10 * Math.sin(i / 90.0);
data.push([i, base, base + Math.sin(i / 2.0)]);
}
// Shift one portion out of line.
var highlight_start = 450;
var highlight_end = 500;
for (var i = highlight_start; i <= highlight_end; i++) {
data[i][2] += 5.0;
}
g = new Dygraph(
document.getElementById("div_g"),
data, {
labels: ['X', 'Est.', 'Actual'],
animatedZooms: true,
underlayCallback: function (canvas, area, g) {
dc = canvas;
my_area = area;
my_graph = g;
bottom_left = g.toDomCoords(0, 0);
top_right = g.toDomCoords(highlight_end, +20);
draw_marker();
}
});
g.updateOptions({
clickCallback: change_movie_position
}, true);
}