Zpět

Vue.js & Three.js & VueGL

Ukázka #1 - Data-binding do 3D scény

  • 
    <div class="fixed-ratio">
        <!-- VueGL scéna -->
        <vgl-renderer antialias>
    	    <vgl-scene background-color="#fff">
    		    <vgl-text-geometry 
    			    name="textGeometry"
    			    font="../../../libs/threejs/three.helvetiker.json"
    			    size="2"
    			    height="1"
    			    :text="text">
    		    </vgl-text-geometry>
    		    <vgl-mesh-standard-material
    			    name="material" 
    			    :color="diffuseColor">
    		    </vgl-mesh-standard-material>
    		    <vgl-mesh 
    			    geometry="textGeometry"
    			    material="material"
    			    position="0 0 0">
    		    </vgl-mesh>
    		    <vgl-ambient-light 
    			    :color="ambientColor"
    			    intensity="0.5">
    		    </vgl-ambient-light>
    		    <vgl-directional-light
    			    color="#fff"
    			    position="0 4 2"
    			    intensity="2">
    		    </vgl-directional-light>
    	    </vgl-scene>
    	    <vgl-perspective-camera
    		    orbit-position="16 1.4 0"
    		    orbit-target="5 0 0">
    	    </vgl-perspective-camera>
        </vgl-renderer>
    </div>
    <div>
        <!-- Formulář pro ovládání scény -->
        <form>
    	    <div>
    		    <label>Text</label>
                <!-- Vstup pro text s obousměrným data-bindingem v-model -->
    		    <input type="text" v-model="text" />
    	    </div>
    	        <div class="grid">
    		    <div>
    			    <label>Základní barva</label>
    			    <div>
    				    <input type="color" v-model="diffuseColor" />
    				    <span :style="'color: ' + diffuseColor"> {{ diffuseColor }} </span>
    			    </div>
    		    </div>
    		    <div>
    			    <label>Barva lesku</label>
    			    <div>
    				    <input type="color" v-model="ambientColor" />
    				    <span :style="'color: ' + ambientColor"> {{ ambientColor }} </span>
    			    </div>
    		    </div>
    	    </div>
        </form>
    </div>
    
  • // Inicializace VueGL komponentů
    Object.keys(VueGL).forEach((key) => {
        Vue.component(key, VueGL[key]);
    });
    
    // Orbitální kamera
    function OrbitCamera(canvasId) {
        var canvas = document.getElementById(canvasId);
        var controls = new THREE.OrbitControls(canvas.__vue__.vglNamespace.cameras.active, canvas);
        function update() {
            requestAnimationFrame(update);
            controls.update();
            canvas.__vue__.vglNamespace.update();
        }
        update();
    }
    ready(function () {
        new OrbitCamera("canvas1");
    });
    
    // Vue aplikace
    var app1 = new Vue({
        el: '#app1',
        data: {
            text: 'Hello world!',
            diffuseColor: '#dd3300',
            ambientColor: '#ee9966'
        }
    });
    
{{ diffuseColor }}
{{ ambientColor }}

Ukázka #2 - Práce s polem

  • 
    <div class="fixed-ratio">
        <!-- VueGL scéna -->
        <vgl-renderer antialias>
    	    <vgl-scene background-color="#222">
                <!-- Cyklus s využitím directivy v-for pro vykreslení všech kostek -->
    		    <vgl-group v-for="(item, index) in items">
    			    <vgl-box-geometry 
    				    :name="'box' + index"
    				    :width="item.size.x"
    				    :height="item.size.y"
    				    :depth="item.size.z">
    			    </vgl-box-geometry>
    			    <vgl-mesh-standard-material 
    				    :name="'material' + index"
    				    :color="item.color">
    			    </vgl-mesh-standard-material>
    			    <vgl-mesh 
    				    :geometry="'box' + index"
    				    :material="'material' + index"
    				    :position="(((index - ((items.length - 1) / 2)) * 2) + (item.position.x * 1)) + ' ' + item.position.y + ' ' + item.position.z">
    			    </vgl-mesh>
    		    </vgl-group>
                <vgl-ambient-light
    			    color="#fff"
    			    intensity="0.5">
    		    </vgl-ambient-light>
    		    <vgl-directional-light
    			    color="#fff"
    			    position="0 4 2"
    			    intensity="2">
    		    </vgl-directional-light>
    	    </vgl-scene>
    	    <vgl-perspective-camera 
    		    orbit-position="10 1.4 0"
    		    orbit-target="0 0 0">
    	    </vgl-perspective-camera>
        </vgl-renderer>
    </div>
    <div>
        <!-- Formulář pro ovládání scény -->
        <form>
            <ul>
                <!-- Cyklus s využitím directivy v-for pro ovládací panely ke všem kostkám -->
                <li v-for="(item, index) in items">
                    <div class="grid">
                        <div>
                            <label>Objekt #{{index + 1}}</label>
                            <div>
                                <input type="color" v-model="item.color" />
                                <span :style="'color: ' + item.color"> {{ item.color }} </span>
                            </div>
                        </div>
                        <div>
                            <label>Šířka {{ Math.floor(item.size.x * 100) }}%</label>
                            <input type="range" v-model="item.size.x" min="0.1" max="2" step="0.1" />
                        </div>
                        <div>
                            <label>Výška {{ Math.floor(item.size.y * 100) }}%</label>
                            <input type="range" v-model="item.size.y" min="0.1" max="2" step="0.1" />
                        </div>
                        <div>
                            <label>Délka {{ Math.floor(item.size.z * 100) }}%</label>
                            <input type="range" v-model="item.size.z" min="0.1" max="2" step="0.1" />
                        </div>
                        <div>
                            <label>X {{ item.position.x }}</label>
                            <input type="range" v-model="item.position.x" min="-1" max="1" step="0.1" />
                        </div>
                        <div>
                            <label>Y {{ item.position.y }}</label>
                            <input type="range" v-model="item.position.y" min="-1" max="1" step="0.1" />
                        </div>
                        <div>
                            <label>Z {{ item.position.z }}</label>
                            <input type="range" v-model="item.position.z" min="-1" max="1" step="0.1" />
                        </div>
                        <div>
                            <!-- Tlačítko napojené na metodu pro smazání kostky -->
                            <a v-on:click="remove(index)">Smazat</a>
                        </div>
                    </div>
                </li>
            </ul>
            <!-- Tlačítko napojené na metodu pro přidání kostky -->
            <a v-on:click="add()" > Přidat nový objekt</a>
        </form>
    </div>
    
  • // Inicializace VueGL komponentů
    Object.keys(VueGL).forEach((key) => {
        Vue.component(key, VueGL[key]);
    });
    
    // Orbitální kamera
    function OrbitCamera(canvasId) {
        var canvas = document.getElementById(canvasId);
        var controls = new THREE.OrbitControls(canvas.__vue__.vglNamespace.cameras.active, canvas);
        function update() {
            requestAnimationFrame(update);
            controls.update();
            canvas.__vue__.vglNamespace.update();
        }
        update();
    }
    ready(function () {
        new OrbitCamera("canvas2");
    });
    
    // Vue aplikace
    var app2 = new Vue({
        el: '#app2',
        data: {
            items: [],
        },
        methods: {
            // metoda pro přidání kostky
            add: function () {
                this.items.push({
                    size: { x: 1, y: 1, z: 1 },
                    position: { x: 0, y: 0, z: 0 },
                    color: getRandomColor()
                });
            },
            // metoda pro smazání kostky
            remove: function (index) {
                this.items.splice(index, 1);
            }
        },
        // metoda volaná při inicializaci
        created: function () {
            this.add();
            this.add();
        }
    });
    
  • {{ item.color }}
Přidat nový objekt

Ukázka #3 - Pokorčilejší scéna

  • 
    <vgl-renderer antialias id="canvas3">
    	<vgl-scene background-color="#222" name="leds">
    		<vgl-box-geometry name="plane" width="16" height="0.1" depth="6"></vgl-box-geometry>
    		<vgl-mesh-standard-material name="planeMaterial" color="#0D660D"></vgl-mesh-standard-material>
    		<vgl-mesh geometry="plane" material="planeMaterial" position="0 0 0"></vgl-mesh>
    
    		<vgl-mesh-standard-material name="goldMaterial" color="#FF9900"></vgl-mesh-standard-material>
    		<vgl-text-geometry name="textLed" font="../../../libs/threejs/three.helvetiker.json" size="0.75" height="0.1" text="LED diody (8 bitu)"></vgl-text-geometry>
    		<vgl-mesh geometry="textLed" material="goldMaterial" position="-4.05 0 -1.6" rotation="-1.5707963268, 0, 0"></vgl-mesh>
    
    		<vgl-mesh-standard-material name="ledMaterialOff" color="#CCCCCC"></vgl-mesh-standard-material>
    		<vgl-mesh-standard-material name="ledMaterialOn" :color="color"></vgl-mesh-standard-material>
    		<vgl-sphere-geometry name="sphere" radius="0.45" width-segments="16" height-segments="16"></vgl-sphere-geometry>
    		<vgl-cylinder-geometry name="cylinder" radius-top="0.45" radius-bottom="0.45" height="1" radial-segments="16" height-segments="1" />
    		<vgl-cylinder-geometry name="bottom" radius-top="0.5" radius-bottom="0.5" height="0.16" radial-segments="16" height-segments="1" />
    
    		<vgl-mesh-standard-material name="buttonBottomMaterial" color="#1A1A1A"></vgl-mesh-standard-material>
    		<vgl-box-geometry name="buttonBottom" width="1" height="0.4" depth="1"></vgl-box-geometry>
    		<vgl-mesh-standard-material name="buttonMaterial" color="#8080CC"></vgl-mesh-standard-material>
    
    		<vgl-group v-for="(led, index) in leds">
    			<vgl-group :position="((((leds.length - index) - (leds.length / 2)) * 2) - 1) + ' 0 0'">
    				<vgl-mesh geometry="sphere" :material="(led.state ? 'ledMaterialOn' : 'ledMaterialOff')" position="0 1 0"></vgl-mesh>
    				<vgl-mesh geometry="cylinder" :material="(led.state ? 'ledMaterialOn' : 'ledMaterialOff')" position="0 0.5 0"></vgl-mesh>
    				<vgl-mesh geometry="bottom" :material="(led.state ? 'ledMaterialOn' : 'ledMaterialOff')" position="0 0.08 0"></vgl-mesh>
    				<vgl-text-geometry :name="'textLed' + index" font="../../../libs/threejs/three.helvetiker.json" size="0.5" height="0.1" :text="(Math.pow(2, index)).toString()"></vgl-text-geometry>
    				<vgl-mesh :geometry="'textLed' + index" material="goldMaterial" position="0 0 1.2" rotation="-1.5707963268, 0, 0"></vgl-mesh>
    				<vgl-group position="0 0.1 2">
    					<vgl-mesh geometry="buttonBottom" material="buttonBottomMaterial" position="0 0.08 0"></vgl-mesh>
    					<vgl-cylinder-geometry :name="'buttonLed' + index" :height="(led.state ? 0.2 : 0.6)" radius-top="0.4" radius-bottom="0.4" radial-segments="16" height-segments="1" />
    					<vgl-clickable-mesh :onclick="(function () { lightSwitch(index); })" :geometry="'buttonCylinder' + index" material="buttonMaterial" position="0 0.35 0"></vgl-clickable-mesh>
    				</vgl-group>
    			</vgl-group>
    		</vgl-group>
    
    		<vgl-ambient-light color="#fff" intensity="0.5"></vgl-ambient-light>
    		<vgl-directional-light color="#fff" position="0 4 2" intensity="2"></vgl-directional-light>
    	</vgl-scene>
    	<vgl-perspective-camera name="active" orbit-position="12 1.4 0" orbit-target="0 0 0">
    	</vgl-perspective-camera>
    </vgl-renderer>
    
  • // VueGL komponent pro klikatelný objekt
    VueGL.VglClickableMesh = {
        mixins: VueGL.VglMesh.mixins,
        props: ['onclick', "geometry", "material"],
        computed: {
            inst: () => new THREE.Mesh()
        },
        created: function () {
            this.inst.onclick = this.$props.onclick;
        },
        name: "VglClickableMesh"
    };
    
    // VueGL komponent pro průhledný materiál
    VueGL.VglTransparentMaterial = {
        mixins: VueGL.VglMeshStandardMaterial.mixins,
        props: ['opacity', "color", "name"],
        computed: {
            inst: () => new THREE.MeshStandardMaterial()
        },
        watch: {
            inst: {
                handler: function (inst) { inst.color.setStyle(this.color); },
                immediate: true,
            },
            color: function (newColor) {
                this.inst.color.setStyle(newColor);
                this.update();
            }
        },
        created: function () {
            this.inst.depthWrite = false;
            this.inst.transparent  = true;
            this.inst.opacity = this.$props.opacity;
        },
        name: "VglTransparentMaterial"
    };
    
    // Inicializace VueGL komponent
    Object.keys(VueGL).forEach((key) => {
        Vue.component(key, VueGL[key]);
    });
    
    // Orbitální kamera
    function OrbitCamera(canvasId) {
        var canvas = document.getElementById(canvasId);
        var controls = new THREE.OrbitControls(canvas.__vue__.vglNamespace.cameras.active, canvas);
        function update() {
            requestAnimationFrame(update);
            controls.update();
            canvas.__vue__.vglNamespace.update();
        }
        update();
    }
    
    // Klikání na objekty pomocí parpsku
    function RayCasterClicker(canvasId, scene) {
        var canvas = document.getElementById(canvasId);
        var raycaster = new THREE.Raycaster();
        var mouse = new THREE.Vector2();
        var rect = canvas.getBoundingClientRect();
        canvas.addEventListener("click", function (e) {
            mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
            mouse.y = - ((event.clientY - rect.top) / rect.height) * 2 + 1;
            raycaster.setFromCamera(mouse, canvas.__vue__.vglNamespace.cameras.active);
            var intersects = raycaster.intersectObjects(canvas.__vue__.vglNamespace.scenes[scene].children, true);
            if (intersects.length > 0) {
                var i = 0, clicked = false;
                while (i < intersects.length || !clicked) {
                    if (intersects[i].object.onclick) {
                        clicked = true;
                        intersects[i].object.onclick();
                    }
                    i++;
                }
            }
        });
    }
    
    ready(function () {
        new OrbitCamera("canvas3");
        new RayCasterClicker("canvas3", "leds");
    });
    
    // Vue aplikace
    var app3 = new Vue({
        el: '#app3',
        data: {
            leds: [
                { state: false },
                { state: true },
                { state: false },
                { state: false },
                { state: true },
                { state: true },
                { state: false },
                { state: false },
            ],
            color: '#ff0000',
        },
        // dopočítáváné vlastnosti pro hexadecimální a binární zápis
        computed: {
            int: {
                get: function () {
                    var r = 0;
                    for (var i = 0; i < this.leds.length; i++) {
                        r += this.leds[i].state ? Math.pow(2, i) : 0;
                    }
                    return r;
                },
                set: function (v) {
                    var bin = toBin(v);
                    for (var i = 0; i < this.leds.length; i++) {
                        this.leds[this.leds.length - i - 1].state = bin[i] == "1";
                    }
                }
            },
            hex: {
                get: function () { return toHex(this.int); },
                set: function (v) { this.int = parseInt(v, 16); }
            },
            bin: {
                get: function () { return toBin(this.int); },
                set: function (v) { this.int = parseInt(v, 2); }
            }
        },
        methods: {
            // metoda pro přepnutí LED diody
            lightSwitch: function (index) {
                this.leds[index].state = !this.leds[index].state;
            }
        }
    });
    
  • 
    
    
{{ color }}