How to paint a line of decals on object with Three.js? - three.js

I have This Example , as you can see - the event that used for adding decals to the object is 'pointerup', like in the following function :
window.addEventListener( 'pointerup', function ( event ) {
if ( moved === false ) {
checkIntersection( event.clientX, event.clientY );
if ( intersection.intersects ) shoot();
}
} );
I wonder how can i add decals while the mouse/ pointer are pressed - so if i could do it - it will be like the action of drawing - which is what i want to achieve...
The problem is that i cant figure out which event and function should i use for repeatedly track each move and append it...

The problem is that i cant figure out which event and function should i use for repeatedly track each move and append it...
You can do this by combining pointerdown, pointerup and pointermove event listeners. Use the first and second one to manage a boolean variable e.g. drawing. On pointerdown, you set it to true. On pointerup, you set it to false. You then know when the interaction is in the drawing state.
In the pointermove event listener, you check for drawing. If set to true, you execute the actual drawing logic. The official three.js example webgl_materials_texture_canvas demonstrates this workflow. The idea of the example is to draw on a canvas which is used as a texture for a cube.

Related

two model in one scene

I think two canvas on a page, load the two models, right hand slide, can compare, but now found a problem, fingers sliding at the same time, the program will mistake is magnified model, rather than rotating model?
So, I think of a way , a canvas, placed two camera, two models, fingers sliding, control both the location of the camera, make the model with rotating frequency, but the specific implementation, found that the model is relatively rotating, not with the effect of the rotation frequency?
I changed the location of the number of touches in the OrbitControls file to solve the problem of both fingers sliding and amplifying the model
function onTouchStart( event ) {
// debugger;
if ( scope.enabled === false ) return;
// switch ( event.touches.length ) {
switch ( event.targetTouches.length ) {
}
}

WebGLRenderer.render called with undefined camera

I'm using three.js and OrbitControls.js in combination, in a 3D app. Sometimes WebGLRenderer.render gets called with an undefined camera. This happens when I use the mouse to navigate the 3D model, using the controls delivered by OrbitConrols.
The undefined camera argument is weird, as my animate function (see code below) always calls WebGLRenderer.render with a well defined camera. Also, when instantiating OrbitControls, I give it a well defined camera. So the question is - how can it be that WebGLRenderer.render is at some point called with an undefined camera?
NOTE: The source code for the WebGLRenderer.render function can be seen in line 20426 in the three.js source code.
I attempted to locate all the potential call sites by searching for the text string render(. There are 14 such matches on the string render(, but none of these gives an undefined camera as argument. Thus, the trail was cold.
I tried stack tracing from the callee (the function body of the WebGLRenderer.renderfunction), but this merely lead back to some mix-it-all event hub. But it gave me a hint that the caller might be Javascript itself, calling from its DOM event system. That would explain why I couldn't find the call site in the three.js source code.
Thus perhaps the problem is associated with the point at which OrbitControls interacts with the Javascript event system. When initialising my app, I register an event on my OrbitControls instance. See code below. Could this be causing trouble? No camera is given as argument when this happens though :/
var myControls = require('../../instances/three/myControls');
var myRenderer = require('../../instances/three/myRenderer');
myControls.damping = 0.2;
myControls.domElement.addEventListener( 'change', myRenderer.render );
EDIT:
I'm using these versions:
three.js 0.81.0
three-orbit-controls 72.0.0
So, indeed the wrong-doing call to render was coming from some event system (the Javascript DOM event system). This module shows where the render function is given as callback to an event listener:
var myControls = require('../../instances/three/myControls');
var myRenderer = require('../../instances/three/myRenderer');
myControls.damping = 0.2;
myControls.addEventListener( 'change', myRenderer.render );
module.exports = {};
( where the myControls module looks like this:
var THREE = require('three');
var orbitControls = require('three-orbit-controls');
var myRenderer = require('../../instances/three/myRenderer');
var myCamera = require('./myCamera');
require('../../sideeffects/three/addCamera');
// add orbitControls to THREE:
var OrbitControls = orbitControls( THREE );
// make controls:
var controls = new OrbitControls( myCamera, myRenderer.domElement );
module.exports = controls;
)
However, it seems that OrbitControls.js has been changed in such a way that instances of the OrbitControls constructor are no longer DOM-elements. Thus, one CANNOT register events on them, as I was doing with the line:
myControls.addEventListener( 'change', myRenderer.render );
Instead, instances of the OrbitControls constructor has a new property: domElement which, as you might have guessed, is indeed a DOM element. Thus, I can fix the problem by changing the target of my event attachment to myControls.domElement. Thus the above line comes to look like this:
myControls.domElement.addEventListener( 'change', myRenderer.render );
and that solved the problem :)
EDIT:
It turns out that this line can actually be omitted completely! It is only necessary if there is no animation loop (a perpetual loop that calls requestAnimationFrame on each loop step)
Also, dampening can be enabled by doing this:
myControls.enableDamping = true;
and then adding myControls.update(); somewhere in your animation loop.

Control multiple cameras with the same controls

I have two different threejs scenes and each has its own camera. I can control each camera individually with a corresponding TrackballControls instance.
Is there a reliable way to 'lock' or 'bind' these controls together, so that manipulating one causes the same camera repositioning in the other? My current approach is to add change listeners to the controls and update both cameras to either's change, but this isn't very neat as, for one, both controls can be changing at once (due to dampening).
I believe it should work if you set the matrices of the second camera to the values of the first and disable automatic matrix-updates of both cameras:
camera2.matrix = camera1.matrix;
camera2.projectionMatrix = camera1.projectionMatrix;
camera1.matrixAutoUpdate = false;
camera2.matrixAutoUpdate = false;
But now you need to update the matrix manually in your renderloop:
camera1.updateMatrix();
That call will take the values for position, rotation and scale (that have been updated by the controls) and compose them into camera1.matrix, which per assignment before is also used as the matrix for the second camera.
However, this feels a bit hacky and can lead to all sorts of weird problems. I personally would probably prefer the more explicit approach you have already implemented.
Question is why are you even using two camera- and controls-instances? As long as the camera isn't added to the scene you can just render both scenes using the same camera.
Is it possible to use the Observer or Publisher design patterns to control these objects?
It seems that you are manipulating the cameras with a control. You might create an object that has the same control interface, but when you pass a command to the object, it repeats that same command to each of the subscribed or registered cameras.
/* psuedo code : es6 */
class MasterControl {
constructor(){
this.camera_bindings = [];
}
control_action1(){
for( var camera of this.camera_bindings ){
camera.control_action1();
}
}
control_action2( arg1, arg2 ){
for( var camera of this.camera_bindings ){
camera.control_action2( arg1, arg2 );
}
}
bindCamera( camera ){
if( this.camera_bindings.indexOf( camera ) === -1 ){
this.camera_bindings.push( camera );
}
}
}
var master = new MasterControl();
master.bindCamera( camera1 );
master.bindCamera( camera2 );
master.bindCamera( camera3 );
let STEP_X = -5;
let STEP_Y = 10;
//the following command will send the command to all three cameras
master.control_action2( STEP_X, STEP_Y );
This binding is self created rather than using native three.js features, but it is easy to implement and can get you functional quickly.
Note: I wrote my psuedocode in es6, because it is simpler and easy to communicate. You can write it in es5 or older, but you must change the class definition into a series of functional object definitions that create the master object and its functionality.

Tween on a boolean property fails

I am using TWEEN to create a series of animations on THREE.
According to the documentation, creating a tween on a non numeric property should change the property at the end of the transition time.
I am trying to animate the visible property of a material, but it won't work.
Of course I am not trying to change gradually from non-visible to visible; I just want to do it at some moment, but integrated in the tween sequence.
It is not working, and after a quick look at the code, it seems difficult to make it work.
Is there something that I missing in the TWEEN documentation. Or any workaround ?
to
to ( props , duration , ease )
Queues a tween from the current values to the target properties. Set duration to 0 to jump to these value. Numeric properties will be tweened from their current value in the tween to the target value. Non-numeric properties will be set at the end of the specified duration.
tween.js documentation
You have to use .onComplete
new TWEEN.Tween( cube.material ).to( { opacity: 0 }, 1000 )
.onUpdate(function(){
// do something if u want
})
.onComplete(function(){
// change boolean
console.log('DONE');
})
.start();
animate();
Don't forget to set the transparent flag of the material

How to draw a new line on Gtk::DrawingArea area, while peristing previous lines that have already been drawn?

I am using C++11 with GNU tool chain with gtkmm3, on Ubuntu 12.04 LTS 32 bit.
I have been playing wtih some of the examples for gtkmm3 in Programming with gtkmm 3.
Based on 17.2.1.Example there, I inherited from Gtk::DrawingArea (MyDrawingArea here) and overrode the on_draw() event handler as follows:
MyDrawingArea.hpp
...
protected:
bool on_draw ( const Cairo::RefPtr<Cairo::Context>& cr ) override;
MyDrawingArea.cpp
bool MyDrawingArea::on_draw( const Cairo::RefPtr<Cairo::Context>& cr )
{
Gtk::Allocation allocation = get_allocation( );
const int width = allocation.get_width( );
const int height = allocation.get_height( );
int coord1{ height - 3 };
cr->set_line_width( 3.0 );
this->get_window( )->freeze_updates( );
cr->set_source_rgb( 0, 0.40, 0.60 );
cr->move_to( 0, coord1 );
cr->line_to( width, coord1 );
cr->stroke( );
cr->set_source_rgb( 1, 0.05, 1 );
cr->move_to( mXStart, coord1 );
cr->line_to( mXStart, mYAxis * 1.5 );
cr->show_text( to_string( mYAxis ) );
cr->stroke( );
mXStart += 5;
this->get_window( )->thaw_updates( );
return true;
}
My goal is to draw a simple bar graph based on a calculation I do in a little test application, the idea being that each time the on_draw() event is called, the next bar would be moved 5 units to the right on mXAxis and a vertical line would be drawn based on the new mYaxis value, which is computed based on the results of the new calculation.
When I want to repaint my graph and trigger the MyDrawingArea::on_draw() event, I call MyDrawingArea.show_all() from my application after the calculation has completed, and new x and y axes have been set.
However, this does not work as I expected: MyDrawingArea.show_all() invalidates the entire drawing window and draws from scratch: the new graph line appears in its proper place, but the previous ones are erased. I also tried MyDrawingArea.queue_draw(), which had the same effect. But I want to persist the previous graph results so I can get a profile of the calculation results, as I calculate with different values.
This implementation is also causing the bottom line on my graph (my x axis on the graph)- drawn by the first stroke() call in my code example, to be rendered anew on each call to on_draw() - although this should not be necassary since this line persists for the lifetime of MyDrawingArea - it should not be necessary to invalidate and then re-draw it on each new on_draw() event, as my code is currently doing, because I am haven't yet found a way to handle this.
I am very new to Cairo, so I'm sure I'm probably doing this completely wrong, but explicit, task-oriented documentation appears to be sparse - have not found anything that explains how to do this, although I'm sure it is quite simple.
What do I need to do to draw a new line on Gtk::DrawingArea, while persisting previous graph lines that have already been drawn on previous passes, and establish graphics elements that will persist for the lifetime of the Gtk::DrawingArea widget. Obviously using show_all() or queue_draw() and doing it all in the on_draw() event is not the way to go.
In general, you must draw the entire widget and Cairo will clip the drawing to the predefined dirty region. See also GTK reference manual for the "GtkWidget::draw" signal for performance tips:
The signal handler will get a cr with a clip region already set to the
widget's dirty region, i.e. to the area that needs repainting.
Complicated widgets that want to avoid redrawing themselves completely
can get the full extents of the clip region with
gdk_cairo_get_clip_rectangle(), or they can get a finer-grained
representation of the dirty region with
cairo_copy_clip_rectangle_list().
So you may be able to redraw only the region you want with gtk_widget_queue_draw_area().

Resources