dat.GUI, auto control (.listener) does not transfer to morph target

I want to control the morpt targets of a json character automatically be code. The control of the sliders works, but the face does not move. When I use the mouse, the facel morph targets moves well. Why does it not work if I do this without a mouse? Must be any connected here, or must the mouse still be activated? Many thanks for the answer.

var shape = {
mouth_open: 0.0, //Anfangsposition 0.0
};

var gui = new dat.GUI({
autoplace: false,
width: 250,
height: 9 * 32 - 1
});

var folder = gui.addFolder( ‘Morph Targets’ );
folder.add( shape, ‘mouth_open’, 0, 1 ).step( 0.01 ).name(‘mouth_open’).listen().onChange( function( a ) { mesh.morphTargetInfluences[ 40 ] = a;})
folder.open();

var update = function() {
requestAnimationFrame(update);
shape.mouth_open = 0.50
};

update();

Please have a look at the code of the following three.js example: https://threejs.org/examples/#webgl_loader_mmd_pose

I hope the implementation helps you further.

Thanks for the reply and the good link. With the manual control of the dt.Gui by mouse I have no problems, it all works. The problem is that the face no longer moves when I control the slider without mouse, but by code. Only the slider moves, but not the face.

For an example https://workshop.chromeexperiments.com/examples/gui/#9--Updating-the-Display-Automatically

In this example the text moves without problems. If I want to move such a character moves only the slider, but the face does not.

A simple example with a cube. Use the mouse to work, but not automatically.

<script src="js/three.js"></script>

<script src="js/controls/OrbitControls.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/libs/stats.min.js"></script>

<script>

if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

var container, stats;
var camera, scene, renderer;
var geometry, objects;
var mesh;

     init();
     animate();

function init() {

     container = document.getElementById( 'container' );

     camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 15000 );
     camera.position.z = 500;

     scene = new THREE.Scene();
     scene.background = new THREE.Color( 0x222222 );
     scene.fog = new THREE.Fog( 0x000000, 1, 15000 );

var light = new THREE.PointLight( 0xff2200 );
    light.position.set( 100, 100, 100 );
    scene.add( light );

var light = new THREE.AmbientLight( 0x111111 );
    scene.add( light );

var geometry = new THREE.BoxGeometry( 100, 100, 100 );
var material = new THREE.MeshLambertMaterial( { color: 0xffffff, morphTargets: true } );

// construct 8 blend shapes

     for ( var i = 0; i < 8; i ++ ) {
           var vertices = [];

	   for ( var v = 0; v < geometry.vertices.length; v ++ ) {
		 vertices.push( geometry.vertices[ v ].clone() );

					if ( v === i ) {

						vertices[ vertices.length - 1 ].x *= 2;
						vertices[ vertices.length - 1 ].y *= 2;
						vertices[ vertices.length - 1 ].z *= 2;

					}

				}

				geometry.morphTargets.push( { name: "target" + i, vertices: vertices } );

			}

			mesh = new THREE.Mesh( geometry, material );

			scene.add( mesh );

			//

			var params = {
				influence1: 0,
				
			};

var gui = new dat.GUI();

var folder = gui.addFolder( 'Morph Targets' );
    folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).listen().onChange( function( value ) {        mesh.morphTargetInfluences[ 0 ] = value; } );				
    folder.open();
			
var update = function() {
 requestAnimationFrame(update);			
var time = Date.now() * 0.003;
params.influence1 = 0.5 * Math.sin( 0.5 * time ) + 0.3; 				
};
		
update();		

//

renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );

 //

 controls = new THREE.OrbitControls( camera, renderer.domElement );

//

 window.addEventListener( 'resize', onWindowResize, false );

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
 camera.updateProjectionMatrix();

  renderer.setSize( window.innerWidth, window.innerHeight );

}

 function animate() {

requestAnimationFrame( animate );
			render();

 }

function render() {

mesh.rotation.y += 0.01;

renderer.render( scene, camera );

}

</script>
</body>
</html>

Um, i’m not sure dat.gui supports this :thinking: . It looks like that the assignment of a new value and the subsequent UI update via .listen() does not trigger a change event. Therefore, the morphtarget config of your mesh is not updated via the .onChange() callback. As a workaround, have a look at the following fiddle and check out the new update() method: https://jsfiddle.net/caL2g356/5/

If you want a cleaner solution, i recommend you post an issue right here: https://github.com/dataarts/dat.gui/issues

1 Like

This is a very good solution for me. Thank you very much for the effort !!!

Vielen Herzlichen Dank für Deine Bemühungen, mir reicht das im Moment völlig aus. Nun ist der Weg für den nächste Schritt geöffnet. Ich versuche nun das dat.GUI über die Coordinaten von OpenCV und WebRTC zu steuern. Grüsse
Tom

I have yet another question on the subject. If I try now to control the GUI with my automatically generated values, only I am not quite clear how I can make the connection.

//Yawn_detection.js

setPoint(face.vertices, 39, p1); // left eye inner corner
setPoint(face.vertices, 42, p0); // right eye outer corner

var eyeDist = calcDistance(p0, p1);

setPoint(face.vertices, 62, p0); // mouth corner left
setPoint(face.vertices, 66, p1); // mouth corner right

var mouthOpen = calcDistance(p0, p1);
var yawnFactor = mouthOpen / eyeDist;

yawnFactor -= 0.35; 

if(yawnFactor < 0) yawnFactor = 0;

yawnFactor *= 2.0; 

if(yawnFactor > 1.0) yawnFactor = 1.0;

if(yawnFactor < 0.0) { yawnFactor = 0.0; }
if(yawnFactor > 1.0) { yawnFactor = 1.0; }

// Face Tracking Resultat: 68 points.

FaceExample.dom.updateHeadline("\n" +
"Mund offen: " + (yawnFactor * 1).toFixed(2));  <---- Dies erzeugt den sich ändernden Wert 0.00 - 1.00 auf dem     Frontend.
				
				
///////////////////////////////////////////////////////////////////////////

				
//DOM_Utils.js				
				
dom.updateHeadline = function(text) {

var subline = dom.getElement("_subline");      
	if(subline) {
		while(text.indexOf("\n") >= 0) {
			text = text.replace("\n", "<br>");
		}
		subline.innerHTML = "<b>" + text + "</b>";
	}
};	

var subline = getElement("_subline");
	if(subline) subline.style.top = (height + 10) + "px";
	
///////////////////////////////////////////////////////////////////////////

//index, dat.Gui, wie bekomme ich den ausgegebenen Wert von 0.00 - 1.00 vom (yawnFactor) ins dat.Gut ?

 var params = {						  
	influence1: 0.0, //Anfangsposition 0.0			
};

var gui = new dat.GUI();															

var folder = gui.addFolder( 'Morph Targets' );			
	folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).name('mouth_open').listen().onChange( function( value ) { mesh.morphTargetInfluences[ 40 ] = value;});
	folder.open();			
		
var update = function() {
	requestAnimationFrame(update);			
              //var time = Date.now() * 0.003;
		var shape = (yawnFactor * 1).toFixed(2);
		var value = shape();    //0.5 * Math.sin( 0.5 * time ) + 0.3;
		var value = 
		mesh.morphTargetInfluences[ 40 ] = params.influence1 = value; 		
		};	
		
	update();		
});

Maybe this helps: https://jsfiddle.net/caL2g356/6/

Just keep one thing in mind: dat.gui is primarily designed for changing variables in a lightweight manner. If you need more sophisticated data binding features or read-only fields, you might want to build your own user interface with something like React or a similar UI library. The code of the fiddle feels already a little bit hacky…

Thanks for the reply, I have made a mistake with the inserted code. Actually, I meant something like this. The calculation of the automatism should be replaced by the values coming from the point detector. So I’m looking for a way to give the date.GUI live steam data.

var params = {						  
	influence1: 0.0, //Anfangsposition 0.0			
};
							
var gui = new dat.GUI({
		autoplace: false, 
		width: 250,
		height: 9 * 32 - 1
});																				

var folder = gui.addFolder( 'Morph Targets' );			
	folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).name('mouth_open').listen().onChange( function( value ) { mesh.morphTargetInfluences[ 40 ] = value;});
	folder.open();			
		
var update = function() {
	requestAnimationFrame(update);			
		//var time = Date.now() * 0.003;			
		var shape = document.getElementsById("(yawnFactor * 1).toFixed(2)");
		var value = shape();    
		mesh.morphTargetInfluences[ 40 ] = params.influence1 = value; 		
		};	
		
	update();		
});

I’m not sure if the dat.GUI understands this output of text. Maybe I have to make changes here. I can create a changing output on the page, and I want to pass this to the dat.GUI so that it moves

FaceExample.dom.updateHeadline("\n" +
"Mund offen: " + (yawnFactor * 1).toFixed(2));   //Erzeugt den Wert 0.00 - 1.00 in Textform auf der Page

I think the problem is because I can not pass the value “yawnFactor” from the other script to the dat.GUI. Please see the comments in the attached example. There should be a simple way to get the value.

<!DOCTYPE html>
<html>
<head>
	<title>My first three.js app</title>
	<style>
		body { margin: 0; }
		canvas { width: 100%; height: 100% }
	</style>
</head>
<body>
	<script src="js/three.js"></script>
	<script src="js/libs/dat.gui.min.js"></script>			
	<script src="js/test.js"></script>    
	
	<script>
	
	/*Ausgelagertes Script test.js mit der Variable "var Wert = 0.5"
	* die ich anstelle von "var value = 0.5" im dat.Gui verwenden möchte.
	*/
	
	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 );
		
	///
		
	var params = {						  
	    influence1: 0.0, 	
	};
							
	var gui = new dat.GUI();																				

	var folder = gui.addFolder( 'Morph Targets' );			
	    folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).name('mouth_open').listen().onChange( function( value ) { mesh.morphTargetInfluences[ 0 ] = value;});
	    folder.open();			
		
	var update = function() {
	    requestAnimationFrame(update);			
	//var value = wert; //Diesen Wert (0.5) möchte ich von dem Ausgelagerten Script test.js importieren.
	var value = 0.5  
	    params.influence1 = value; 		
	};	
		
	update();		
				
	///
		
	var animate = function () {
	    requestAnimationFrame( animate );

	    renderer.render(scene, camera);
	};

	animate();
        </script>
</body>
</html>

If the variable Wert is defined in global scope, there should be no problems to access its value in your main JavaScript source. Without a live example, it will be difficult to give more detailed feedback. Any chances you provide a new fiddle?

Which with the variable wert is just a try to figure out where the problem is. There are about 10 scripts working together, so it will be difficult to put in fiddle

https://jsfiddle.net/19m1zrej/

(function exampleCode() {
"use strict";		

var face = faces[i];

if(	face.state === testState.FACE_TRACKING_START ||
	face.state === testState.FACE_TRACKING) {
	// Yawn Detection - Or: How wide open is the mouth?

	setPoint(face.vertices, 39, p1); // right eye inner corner
	setPoint(face.vertices, 42, p0); // left eye inner corner

	var eyeDist = calcDistance(p0, p1);

	setPoint(face.vertices, 62, p0); // mouth corner left
	setPoint(face.vertices, 66, p1); // mouth corner right

	var mouthOpen = calcDistance(p0, p1);
	var yawnFactor = mouthOpen / eyeDist;

	yawnFactor -= 0.35; // remove smiling

	if(yawnFactor < 0) yawnFactor = 0;

	yawnFactor *= 2.0; // scale up a bit

	if(yawnFactor > 1.0) yawnFactor = 1.0;

	if(yawnFactor < 0.0) { yawnFactor = 0.0; }
	if(yawnFactor > 1.0) { yawnFactor = 1.0; }
			
	// Face Tracking results: 68 facial feature points.

	draw.drawTriangles(	face.vertices, face.triangles, false, 1.0, color, 0.4);
	draw.drawVertices(	face.vertices, 2.0, false, color, 0.4);

	FaceExample.dom.updateHeadline("\n" +
	"Mund offen: " + (yawnFactor * 1).toFixed(2));  //Diesen Wert versuche ich ins dat.Gui zu bekommen.
			
	var wert = 0.5;					//Daher erst mal der Versuch mit diesem Wert.
		
};	

var p0				= new test.Point();
var p1				= new test.Point();

var setPoint		= testPointUtils.setPoint;
var calcDistance	= testPointUtils.calcDistance;

FaceExample.dom.updateHeadline("\n" +
"Mund offen.");	

FaceExample.dom.updateCodeSnippet(exampleCode + "");

}();

If I use the test variable “wert” above “(function exampleCode () {” does it work. But not below the function, which is probably logical, just how do i get this value now from there, actually i need (yawnFactor * 1) .toFixed (2));

Within the function is the variable or the value local and not global, so I can not see it.

This is obviously a JavaScript namespacing problem. Use the following example as an orientation for exposing internal variables of a closure. Besides, google after “IIFE” or “JavaScript namespacing pattern” for more information.

https://jsfiddle.net/ppq25vpL/

A thousand thanks for the reply and the valuable reference. I have been working with java for 3 weeks and still have a lot to learn. I will read everything about it to find out where I have to use something.

What I’m doing wrong? Can they help me once again …

//Test Script local to global

(function (exampleCode) {
    "use strict";

var wert = 0.5;
var name = 'wert';

Object.assign( exampleCode, {

    getValue: function() {

	    return name;
  
      }

    } );

} )( window.exampleCode = window.exampleCode || {} );


/////////////////////////////////////////////////////////////////////

 //main script get var wert 0.5  from Test Script

    var update = function() {
	     requestAnimationFrame(update);			
		    //var time = Date.n/ow() * 0.003;			
		    //var value = wert;     //0.5 * Math.sin( 0.5 * time ) + 0.3;
		    console.log( exampleCode.getValue() );
		    mesh.morphTargetInfluences[ 40 ] = params.influence1 = value; 		
	};	
		
	update();

You are returning the wrong value from your closure. See https://jsfiddle.net/ppq25vpL/1/

I hope I understand it correctly, the run is not yet. from> https://jsfiddle.net/khfyjahf/ to> https://jsfiddle.net/19m1zrej/1/