UNPKG

node-red-node-rdk-3dsim

Version:

配合RDK硬件使用的3D可视化功能包

224 lines (180 loc) 6.96 kB
<script type="text/x-red" data-template-name="rdk-3d 3drobot"> <div class="form-row node-input-name"> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rdk-3drobot.label.name"></span></label> <input type="text" id="node-input-name" data-i18n="[placeholder]rdk-3drobot.names.3drobot" style="width: 296px;"> </div> </script> <script type="importmap"> { "imports": { "three": "https://cdn.jsdelivr.net/npm/three@latest/build/three.module.min.js", "three/addons/": "https://cdn.jsdelivr.net/npm/three@latest/examples/jsm/" } } </script> <script type="module"> import * as THREE from 'https://cdn.jsdelivr.net/npm/three@latest/build/three.module.min.js'; import { GLTFLoader } from '/3drobot/js/loaders/GLTFLoader.js'; (function() { const width = 640; const height = 480; const states = [ 'Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing' ]; const emotes = [ 'Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp' ]; let flag = true; let nodeID = ''; let $boxframe = undefined; let offscreenCanvas = undefined; let scene = undefined; let camera = undefined; let renderer = undefined; let model = undefined; let animations = undefined; let clock = undefined; let mixer = undefined; let actions = {}; let currentState = 'Idle'; let previousAction = undefined; let activeAction = undefined; const createFrameImage = (id) => { $boxframe = document.getElementById('boxframe-' + id); if(!$boxframe){ const $container = document.getElementById(id); if (!$container) { return }; const bubble = document.createElementNS("http://www.w3.org/2000/svg", 'polyline'); bubble.setAttribute('id', 'bubble-' + id); bubble.setAttribute('style', 'fill:transparent'); bubble.setAttribute('stroke', '#999999'); $container.insertBefore(bubble, $container.lastChild.nextSibling); const img = document.createElementNS("http://www.w3.org/2000/svg", 'image'); img.setAttribute('id', 'boxframe-' + id); img.setAttribute('x', '1'); img.setAttribute('y', '47'); $container.insertBefore(img, $container.lastChild.nextSibling); $boxframe = img; } } const createOffscreenCanvas = () => { if(!offscreenCanvas){ offscreenCanvas = document.createElement('canvas'); offscreenCanvas.width = width; offscreenCanvas.height = height; } renderer = new THREE.WebGLRenderer({ canvas: offscreenCanvas, antialias: true }); scene = new THREE.Scene(); scene.background = new THREE.Color( 0xe0e0e0 ); scene.fog = new THREE.Fog( 0xe0e0e0, 20, 100 ); clock = new THREE.Clock(); camera = new THREE.PerspectiveCamera( 45, offscreenCanvas.width / offscreenCanvas.height, 0.25, 100 ); camera.position.set( - 5, 3, 10 ); camera.lookAt( 0, 2, 0 ); const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x8d8d8d, 3 ); hemiLight.position.set( 0, 20, 0 ); scene.add( hemiLight ); const dirLight = new THREE.DirectionalLight( 0xffffff, 3 ); dirLight.position.set( 0, 20, 10 ); scene.add( dirLight ); const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0xcbcbcb, depthWrite: false } ) ); mesh.rotation.x = - Math.PI / 2; scene.add( mesh ); const grid = new THREE.GridHelper( 200, 40, 0x000000, 0x000000 ); grid.material.opacity = 0.2; grid.material.transparent = true; scene.add( grid ); const loader = new GLTFLoader(); loader.load( '/3drobot/js/models/RobotExpressive.glb', function ( gltf ) { model = gltf.scene; animations = gltf.animations; mixer = new THREE.AnimationMixer( model ); scene.add( model ); for ( let i = 0; i < animations.length; i ++ ) { const clip = animations[ i ]; const action = mixer.clipAction( clip ); actions[ clip.name ] = action; if ( emotes.indexOf( clip.name ) >= 0 || states.indexOf( clip.name ) >= 4 ) { action.clampWhenFinished = true; action.loop = THREE.LoopOnce; } } activeAction = actions[currentState]; activeAction.play(); // setTimeout(() => { // testAnimation(); // }, 5000) }, undefined, function ( e ) { console.error( e ); } ); } function fadeToAction( name, duration ) { previousAction = activeAction; activeAction = actions[ name ]; if ( previousAction !== activeAction ) { previousAction.fadeOut( duration ); } activeAction .reset() .setEffectiveTimeScale( 1 ) .setEffectiveWeight( 1 ) .fadeIn( duration ) .play(); } function animate() { requestAnimationFrame(animate); flag = !flag; if(!flag){ return; } const dt = clock.getDelta(); if ( mixer ) mixer.update( dt ); renderer.render(scene, camera); if($boxframe){ offscreenCanvas.setAttribute('width', width); offscreenCanvas.setAttribute('height', height); $boxframe.setAttribute('href', offscreenCanvas.toDataURL('image/png')); } } function testAnimation(){ if(states.length === 0) return; setTimeout(() => { fadeToAction(states.pop(), 0.2); testAnimation(); }, 5000) } RED.comms.subscribe("moverobot", function(topic, move){ if(move !== currentState){ if(states.indexOf(move) >= 0){ currentState = move; fadeToAction(currentState, 0.2); } } }); RED.nodes.registerType("rdk-3d 3drobot",{ category: "RDK 3D Sim", color: "#ff3019", defaults: { name: {value:""} }, inputs:1, outputs:0, align: 'right', icon: "robot.svg", paletteLabel: function() { return this._("rdk-3drobot.names.3drobot"); }, oneditprepare: function() {}, label: function() { if(nodeID === '' || !$boxframe){ nodeID = this.id; createFrameImage(nodeID); if($boxframe){ createOffscreenCanvas(); animate(); } } return this.name || this._("rdk-3drobot.names.3drobot"); } }); })() </script>