3d Primitives not Rendering correctly when createGraphics is used in p5js and WEBGL - rendering

I am trying to write some code that draws a rotating cube on an offscreen graphics window in p5js using WebGL. when I draw the cube directly on the canvas it works fine, but when I try and draw it on the graphics object, it renders incorrectly.
let canvas2;
function setup() {
createCanvas(400, 400);
canvas2 = createGraphics(400,400,WEBGL)
}
function draw() {
background(220);
canvas2.background(255);
canvas2.push();
canvas2.noFill();
canvas2.rotateX(0.01*frameCount);
canvas2.box(50,50,50);
image(canvas2,0,0,400,400);
canvas2.pop();
}
any help would be appreciated

Unlike the main canvas, p5.Graphics objects do not have their depth buffer automatically cleared for each frame. You can do this manually by calling WebGLRenderingContext.clearDepth on the drawingContext for your p5.Graphics or you can just use the built in p5.js clear() function which will clear the depth buffer and the colors.
let canvas2;
function setup() {
createCanvas(400, 400);
canvas2 = createGraphics(400, 400, WEBGL);
}
function draw() {
background(220);
// This line fixes the issue:
canvas2.clear();
canvas2.background(255);
canvas2.push();
canvas2.noFill();
canvas2.rotateX(0.01 * frameCount);
canvas2.box(50, 50, 50);
image(canvas2, 0, 0, 400, 400);
canvas2.pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Related

call a component method from main vue app

I'm super new to Vue and I'm trying to get into components.
Basically, I have the main script file with my new vue app (with a countdown function based on setInterval and in the same file a vue component( it's a stamina bar made with canvas) with width increasing and decreasing methods. Basically, I would like to decrease the bar width every second but I don't get how to call the methods declared in the component. I guess that when they are declared inside the component I could access them from everywhere, but I didn't find a proper solution ( I tried with $refs but it's not working)
Is there any way where I can call the addWidth() and subWidth() function from a method in my new Vue app?
thank you
this is the component
Vue.component('stamina', {
template: '<div><canvas ref="stamina" style="height:25px;width:300px;" /></div>',
data(){
return {
stamina_width:300,
// vueCanvas: null
}
},
methods: {
drawStamina(){
// clear canvas
this.vueCanvas.clearRect(0, 0, 300, 50);
// draw rect
this.vueCanvas.rect(20,20, this.stamina_width, 100);
//colorize with gradient
var grd = this.vueCanvas.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "orange");
grd.addColorStop(1, "green");
this.vueCanvas.fillStyle = grd;
//fill the rect with gradient
this.vueCanvas.fillRect(0, 10,this.stamina_width, 10);
},
addWidth() {
this.stamina_width += 20
this.drawStamina()
},
subWidth() {
this.stamina_width -= 20
this.drawStamina()
}
},
mounted () {
var ctx = this.$refs.stamina.getContext('2d');
this.vueCanvas = ctx;
var stam = this.stamina_width;
var grd = this.vueCanvas.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "orange");
grd.addColorStop(1, "green");
this.vueCanvas.fillStyle = grd;
//fill the rect with gradient
this.vueCanvas.fillRect(0,25,stam,25);
}
});
and this is the main vue
var match = new Vue({
el:'#app',
methods:{
//call the methods from component stamina
}
yes you can call methods up/down of parent/child components. You may need to look at creating custom events and listen for them.
Another option I believe might be less complex is to consider using Vuex.
All of your addWidth and subWidth logic will reside in a centralized place this way. All of your components--no matter how nested can "subscribe" to these things (and modify them if needed).
Check out actions. You'll dispatch an action addWidth which sends it to Vuex. In your component that is responsible for rendering the stamina bar I would use a computed property to watch for updates. Something like this:
computed: {
barWidth() {
return this.$store.getters["bar/width"]; // 20
}
}
That is just a really rough example and will not likely be what you're looking for.
VueSchool has some nice (free) courses to help get you rolling with Vuex as well.

How to avoid camera jumps during/after Tween/animation?

I'm having some trouble while creating a camera Tween in THREE.js, specifically at the end and beginning of the animation, there always seems to be a camera 'jump', meaning that the camera flickers once the animation starts and once the animation ends. For reference, what I'm doing is :
Camera is overlooking a scenario from above.
When user clicks on an element of the scenario, the camera zooms on it (by a TWEEN) and when it's close enough, the OrbitControls target change to the centroid of the selected element, and autorotate starts, so the user sees the element rotating in the center of the screen.
When user clicks again, the camera zooms out to its initial position (by a TWEEN) and goes back to the original controls.
I'm experiencing 'jumps/flickers' at the beginning and end of each TWEEN.
This is my tween function :
var origpos = new THREE.Vector3().copy(camera.position); // original position
var origrot = new THREE.Euler().copy(camera.rotation); // original rotation
camera.position.set(x, y+500, z+variation);
camera.lookAt(new THREE.Vector3(x,y,z));
var dstrot = new THREE.Euler().copy(camera.rotation)
// reset original position and rotation
camera.position.set(origpos.x, origpos.y, origpos.z);
camera.rotation.set(origrot.x, origrot.y, origrot.z);
options = {duration: 3000};
//
// Tweening
//
// position
new TWEEN.Tween(camera.position).to({
x: x,
y: y+500,
z: z
}, options.duration).easing(TWEEN.Easing.Cubic.Out).onUpdate(function () {
camera.lookAt(new THREE.Vector3(x,y,z));
}).onComplete(function () {
controls.autoRotate = true;
controls.autoRotateSpeed = 5.0;
controls.target = new THREE.Vector3(x, y, z);
}).start();
// rotation (using slerp)
(function () {
var qa = camera.quaternion; // src quaternion
var qb = new THREE.Quaternion().setFromEuler(dstrot); // dst quaternion
var qm = new THREE.Quaternion();
var o = {t: 0};
new TWEEN.Tween(o).to({t: 1}, options.duration).onUpdate(function () {
THREE.Quaternion.slerp(qa, qb, qm, o.t);
camera.quaternion.set(qm.x, qm.y, qm.z, qm.w);
}).start();
}).call(this);
OK, it seems that the issue was in itself with the method I was using to rotate the camera, using quaternions and SLERP.
I found that the best way (easier and without flicker/jump) would be to interpolate the parameters of camera.rotation instead of interpolating the quaternions.
So, a Tween of camera.rotation.the_axis_where_you_want_to_rotate works perfectly, and done concurrently with a Tween on the position, achieves the effect I was looking for.

threejs: smoothly rotate camera towards an object

i have just started to study three.js and i am having some trouble to write a function that takes as arguments an object position (Vector3) and a time in milliseconds, and gradually rotate the camera to face it in that time. Substantially a lerp version of the builtin lookAt method.
First i've tried using tweenjs to get smooth rotate transition. For the start and end parameters i've created a dummy object and set its position, rotation and quaternion the same as the camera, then i have use the lookAt function on it to face towards the object and i've stored its quaternion in a new variable "targetQuaternion". Then i have used this variable as the target parameter in the TWEEN.Tween method to update camera.quaternion. I've tried before with quaternions to avoid gymbal lock and then with rotation, but none works fine.
function rotateCameraToObject(object3D, time) {
var cameraPosition = camera.position.clone(); // camera original position
var cameraRotation = camera.rotation.clone(); // camera original rotation
var cameraQuaternion = camera.quaternion.clone(); // camera original quaternion
var dummyObject = new THREE.Object3D(); // dummy object
// set dummyObject's position, rotation and quaternion the same as the camera
dummyObject.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
dummyObject.rotation.set(cameraRotation.x, cameraRotation.y, cameraRotation.z);
dummyObject.quaternion.set(cameraQuaternion.x, cameraQuaternion.y, cameraQuaternion.z);
// lookAt object3D
dummyObject.lookAt(object3D);
// store its quaternion in a variable
var targetQuaternion = dummyObject.quaternion.clone();
// tween start object
var tweenStart = {
x: cameraQuaternion.x,
y: cameraQuaternion.y,
z: cameraQuaternion.z,
w: cameraQuaternion.w
};
//tween target object
var tweenTarget = {
x: targetQuaternion.x,
y: targetQuaternion.y,
z: targetQuaternion.z,
w: targetQuaternion.w
};
// tween stuff
var tween = new TWEEN.Tween(tweenStart).to(tweenTarget, time);
tween.onUpdate(function() {
camera.quaternion.x = tweenStart.x;
camera.quaternion.y = tweenStart.y;
camera.quaternion.z = tweenStart.z;
camera.quaternion.w = tweenStart.w;
});
tween.start();
}
So this does not work.
I've also tried another approach, computing the angle between camera vector and object vector and use that angle as target rotation:
function rotateCameraToObject(object3D, time) {
// camera original position
var cameraPosition = camera.position.clone();
// object3D position
var objectPosition = object3D.position.clone();
// direction vector from camera towards object3D
var direction = objectPosition.sub(cameraPosition);
// compute Euler angle
var angle = new THREE.Euler();
angle.setFromVector3(direction);
/*
* tween stuff
*/
var start = {
x: camera.rotation.clone().x,
y: camera.rotation.clone().y,
z: camera.rotation.clone().z,
}
var end = {
x: angle._x,
y: angle._y,
z: angle._z,
}
var tween = new TWEEN.Tween(start).to(end, time);
tween.onUpdate(function() {
camera.rotation.y = start.x;
camera.rotation.y = start.y;
camera.rotation.y = start.z;
});
tween.start();
}
This doesn't work neither, eventually camera rotate towards the object but the rotation is not right.
Any help? What is the correct way to have a lerp rotate function for the camera?
Thanks in advance!
Have you updated the tween in your animation loop?
function animate() {
requestAnimationFrame( animate );
TWEEN.update();
render();
}
However it's probably better to use the quaternion and not the rotation to turn objects - prevents gimbal lock. THREE.js provides a handy function to use spherical linear interpolation (slerp) as opposed to using tween (lerp) which can give you undesired results, most notably at a 180 deg turn. Put this in your animation loop.
camera.quaternion.slerp(targetQuaternion,t); //t = normalized value 0 to 1
You could then tween t to give you your desired easing.

Rendering a gradient in a UIBezierPath

How does one render a gradient inside a UIBezierPath and is it possible to store this state together with the bezierPath for faster re-rendering in drawRect?
I currently render bezierPaths inside drawRect but I cannot add a gradient.
When I call this method CGContextDrawLinearGradient (ctx, gradient, gradientStartPoint, gradientEndPoint, 0); I dont have an endPoint.
I tried using the BezierPath.currentPoint but I dont get the expected results.
This wrapper function renders a gradient inside a UIBezierPath (Swift 4):
func drawLinearGradient(inside path:UIBezierPath, start:CGPoint, end:CGPoint, colors:[UIColor])
{
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
path.addClip() // use the path as the clipping region
let cgColors = colors.map({ $0.cgColor })
guard let gradient = CGGradient(colorsSpace: nil, colors: cgColors as CFArray, locations: nil)
else { return }
ctx.drawLinearGradient(gradient, start: start, end: end, options: [])
ctx.restoreGState() // remove the clipping region for future draw operations
}
You could change this to render any type of gradient or other drawing code after you've set the clipping region using path.addClip().
And you could cache the CGGradient object to improve your chances of the graphics system speeding up future rendering passes.

How to re-size the Strobe media player dynamically?

I'm using the Strobe media player (with OSMF). I need to re-size the player when someone clicks a re-size button (built into the player). How can i do this? I've searched high and low but found no results.
Set scaleMode to "letterbox", then the player automatically adjusts its size depending on its parent div. You can then change its size, for example using jquery:
function getHeight() { return $(window).height(); };
function getWidth() { return $(window).width(); };
$(window).resize(function () {
$("#strobemediaplayback").css({ height: getHeight(), width: getWidth() });
});