Pinning Html to fixed location r3f - react-three-fiber

I'm trying to pin a drei Html element to the top left corner. I'm struggeling with getting it to stay in the same location when rotating or moving the camera. This is my current attempt:
const { mouse2D, mouse3D, normal, plane } = useMemo(() => {
return {
mouse2D: new THREE.Vector2(),
mouse3D: new THREE.Vector3(),
normal: new THREE.Vector3(),
plane: new THREE.Plane(),
};
}, []);
const { width, height } = useWindowDimensions();
useFrame(({camera, raycaster, size}) => {
mouse2D.set(((0.02 * width) / size.width) * 2 - 1, ((-0.02 * height) / size.height) * 2 + 1);
raycaster.setFromCamera(mouse2D, camera);
camera.getWorldDirection(normal).negate();
plane.setFromNormalAndCoplanarPoint(normal, mouse3D);
raycaster.ray.intersectPlane(plane, mouse3D);
});
return <Html position={[mouse3D.x, mouse3D.y, mouse3D.z]}>stuff</Html>;
The Html position updates correctly on window resize but it doesn't update on camera rotation or camera moving. Anyone got any idea how to solve this?

If anyone is interested this is my current solution:
const { viewport } = useThree();
const groupRef = useRef<THREE.Group>(null!);
useEffect(() => {
const groupRefCopy = groupRef.current;
camera.add(groupRefCopy);
return () => {
camera.remove(groupRefCopy);
};
}, [camera, groupRef.current]);
return (
<group ref={groupRef} position={[-viewport.width / 3.5, viewport.height / 3.8, -5]} rotation={[0, 0, 0]}>
<Html style={{ opacity: show ? 1 : 0, width: menuWidth, userSelect: "none", pointerEvents: show? "auto" : "none" }}>
{...}
</Html>
</group>
);
3.5 and 3.8 works very well for my scene. If anyone knows how to get these constants dynamically from 3JS, please comment!

Related

Threejs 'view crop' using viewport and scissors

Overview:
I have a scene with a positioned perspective camera and multiple assets that produces the output that I want
I don't want to move / change the fov of the camera, as this will require me dynamically adjusting all elements in the scene (see debug camera view image)
I want a 'view crop' of the scene from whatever the full size is to a smaller size (not always in the same proportion. See resultant canvas image
I have tried combinations of:
renderer.setScissor(0, 0, 320, 240)
renderer.setScissorTest(true)
renderer.setViewport(0, 0, 320, 240)
renderer.setSize(320, 240)
renderer.getContext().canvas.width = 320
renderer.getContext().canvas.height = 240
renderer.getContext().canvas.style.width = '320px'
renderer.getContext().canvas.style.height = '240px'
Applying the scissor gives me example what I want to see, because only those items are rendered, but the whole view is still the same size
Applying the viewport scales the WHOLE image down,
Adjusting the canvas crops relative to the whole image rather than the points I want to crop from.
I CAN do blitting (copy the exact pixels I want onto a separate canvas, but I was hoping there was another simpler way.
Any ideas?
I've added a codepen example here:
https://codepen.io/faysvas/pen/KKzPQpa
const makeCube = (scene, color, x) => {
const material = new THREE.MeshPhongMaterial({ color })
const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material)
scene.add(cube)
cube.position.x = x
return cube;
}
const addSceneContents = (scene) => {
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(-1, 2, 4)
scene.add(light)
return [
makeCube(scene, 0x44aa88, 0),
makeCube(scene, 0x8844aa, -2),
makeCube(scene, 0xaa8844, 2)
]
}
const main = () => {
const canvas = document.querySelector("#c")
const renderer = new THREE.WebGLRenderer({ canvas })
renderer.setSize(512, 512, false)
const fov = 75
const aspect = 1
const near = 0.1
const far = 5
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.z = 2
const scene = new THREE.Scene()
scene.background = new THREE.Color( 0xff0000 )
let cubes = addSceneContents(scene)
resizeTo320x240(renderer, canvas)
const render = (time) => {
time *= 0.001
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * 0.1
const rot = time * speed
cube.rotation.x = rot
cube.rotation.y = rot
})
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
}
/*
This function should 'crop' from the whole scene without
distorting the perspective of the camera and ensuring the
canvas is 320x240
e.g. I want the canvas to be the same size and output of
red cropped view below. Eg, no black and the canvas (and
it's red contents) should be in the top left of the corner
of the screen
*/
const resizeTo320x240 = (renderer, canvas) => {
console.log('code goes here')
const desiredWidth = 320
const desiredHeight = 240
const currentSize = renderer.getSize(new THREE.Vector2())
const x = (currentSize.x / 2) - (desiredWidth/2)
const y = (currentSize.y / 2) - (desiredHeight/2)
renderer.setScissor(x, y, desiredWidth, desiredHeight)
renderer.setScissorTest(true)
//renderer.setViewport(x, y, desiredWidth, desiredHeight)
}
main()
html, body {
margin: 0;
height: 100%;
background-color: grey;
}
#c {
display: block;
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r119/three.min.js"></script>
<canvas id="c"></canvas>
I figured it out. The renderer or the viewport is not the place to solve it, instead, the camera itself has the ability to offset or clip it's own output.
const makeCube = (scene, color, x) => {
const material = new THREE.MeshPhongMaterial({ color })
const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material)
scene.add(cube)
cube.position.x = x
return cube;
}
const addSceneContents = (scene) => {
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(-1, 2, 4)
scene.add(light)
return [
makeCube(scene, 0x44aa88, 0),
makeCube(scene, 0x8844aa, -2),
makeCube(scene, 0xaa8844, 2)
]
}
const main = () => {
const canvas = document.querySelector("#c")
const renderer = new THREE.WebGLRenderer({ canvas })
renderer.setSize(512, 512, false)
const fov = 75
const aspect = 1
const near = 0.1
const far = 5
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.z = 2
const scene = new THREE.Scene()
scene.background = new THREE.Color( 0xff0000 )
let cubes = addSceneContents(scene)
resizeTo320x240(renderer, canvas, camera)
const render = (time) => {
time *= 0.001
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * 0.1
const rot = time * speed
cube.rotation.x = rot
cube.rotation.y = rot
})
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
}
/*
This function should 'crop' from the whole scene without
distorting the perspective of the camera and ensuring the
canvas is 320x240
e.g. I want the canvas to be the same size and output of
red cropped view below. Eg, no black and the canvas (and
it's red contents) should be in the top left of the corner
of the screen
*/
const resizeTo320x240 = (renderer, canvas, camera) => {
console.log('code goes here')
const desiredWidth = 320
const desiredHeight = 240
const currentSize = renderer.getSize(new THREE.Vector2())
const x = (currentSize.x / 2) - (desiredWidth/2)
const y = (currentSize.y / 2) - (desiredHeight/2)
// Set the size of the renderer to the correct desired output size
renderer.setSize(desiredWidth, desiredHeight, false)
// The camera its is one that should be cropped
// This is referred to as the view offset in three.js
camera.setViewOffset(currentSize.x,currentSize.y,x,y, desiredWidth, desiredHeight)
}
main()
html, body {
margin: 0;
height: 100%;
background-color: grey;
}
#c {
display: block;
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r119/three.min.js"></script>
<canvas id="c"></canvas>

GLTF model and interaction in Three.js

My js skills could be improved to say the least! But struggling with this
I can get my model to load ok into the scene but cannot seem to get the interaction working.
It's like i need to tie in the GLTF file into the raycaster, the below code is part of it. The full Codepen link is below this code.
class PickHelper {
constructor() {
this.raycaster = new THREE.Raycaster();
this.pickedObject = null;
this.pickedObjectSavedColor = 0;
}
pick(normalizedPosition, scene, camera, time) {
if (this.pickedObject) {
this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
this.pickedObject = undefined;
}
this.raycaster.setFromCamera(normalizedPosition, camera);
const intersectedObjects = this.raycaster.intersectObjects(scene.children);
if (intersectedObjects.length) {
this.pickedObject = intersectedObjects[0].object;
this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
this.pickedObject.material.emissive.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000);
this.pickedObject.rotation.y += 0.1 ;
}
}
https://codepen.io/johneemac/pen/abzqdye << FULL Code
Sorry: Cross origin issue with the gltf file on CodePen though! It won't load but you get the idea hopefully.
Super appreciate any help, thanks!
You have to perform the intersection test like so:
const intersectedObjects = this.raycaster.intersectObjects(scene.children, true);
Notice the second argument of intersectObjects(). It indicates that the raycaster should process the entire hierarchy of objects which is necessary in context of a loaded glTF asset.
three.js R112
It's not clear what you're trying to do. GLTF files are collection of materials, animations, geometries, meshes, etc.. so you can't "pick" a GLTF file. You can "pick" individual elements inside. You could write some code that if something is picked, checks of the thing that was picked is one of the meshes loaded in the GLTF scene and then pick every other thing that was loaded in the GLTF scene.
In any case,
You need to give the RayCaster a list of objects to select from. In the original example that was scene.children which is just the list of Boxes added to the root of the scene. But when loading a GLTF, unless you already know the structure of the GLTF because you created the scene yourself you'll need to go find the things you want to be able to select and add them to some list that you can pass to RayCaster.intersectObjects
This code gets all the Mesh objects from the loaded GLTF file
let pickableMeshes = [];
// this is run after loading the gLTT
// get a list of all the meshes in the scene
root.traverse((node) => {
if (node instanceof THREE.Mesh) {
pickableMeshes.push(node);
}
});
Note that you could also pass true as the second argument to RayCaster.intersectObjects as in rayCaster.intersectObjects(scene.children, true). That's probably rarely what you want though as likely you have things in the scene you don't want the user to be able to select. For example if you only wanted the user to be able to select the cars then something like
// get a list of all the meshes in the scene who's names start with "car"
root.traverse((node) => {
if (node instanceof THREE.Mesh && (/^car/i).test(node.name)) {
pickableMeshes.push(node);
}
});
Then, PickHelper class you used was changing the color of the material on each Box but that only works because each Box has its own material. If the Boxes shared materials then changing the material color would change all the boxes.
Loading a different GLTF most the objects shared the same material so to be able to highlight one requires changing the material used with that object or choosing some other method to highlight the selected thing.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 60;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 200;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 30;
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
// put the camera on a pole (parent it to an object)
// so we can spin the pole to move the camera around the scene
const cameraPole = new THREE.Object3D();
scene.add(cameraPole);
cameraPole.add(camera);
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
camera.add(light);
}
function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.Math.degToRad(camera.fov * .5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
// compute a unit vector that points in the direction the camera is now
// in the xz plane from the center of the box
const direction = (new THREE.Vector3())
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
// move the camera to a position distance units way from the center
// in whatever direction the camera was from the center already
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
// pick some near and far values for the frustum that
// will contain the box.
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
// point the camera to look at the center of the box
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
}
let pickableMeshes = [];
{
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.load('https://threejsfundamentals.org/threejs/resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {
const root = gltf.scene;
scene.add(root);
// compute the box that contains all the stuff
// from root and below
const box = new THREE.Box3().setFromObject(root);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
// set the camera to frame the box
frameArea(boxSize * 0.7, boxSize, boxCenter, camera);
// get a list of all the meshes in the scen
root.traverse((node) => {
if (node instanceof THREE.Mesh) {
pickableMeshes.push(node);
}
});
});
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
class PickHelper {
constructor() {
this.raycaster = new THREE.Raycaster();
this.pickedObject = null;
this.pickedObjectSavedMaterial = null;
this.selectMaterial = new THREE.MeshBasicMaterial();
this.infoElem = document.querySelector('#info');
}
pick(normalizedPosition, scene, camera, time) {
// restore the color if there is a picked object
if (this.pickedObject) {
this.pickedObject.material = this.pickedObjectSavedMaterial;
this.pickedObject = undefined;
this.infoElem.textContent = '';
}
// cast a ray through the frustum
this.raycaster.setFromCamera(normalizedPosition, camera);
// get the list of objects the ray intersected
const intersectedObjects = this.raycaster.intersectObjects(pickableMeshes);
if (intersectedObjects.length) {
// pick the first object. It's the closest one
this.pickedObject = intersectedObjects[0].object;
// save its color
this.pickedObjectSavedMaterial = this.pickedObject.material;
this.pickedObject.material = this.selectMaterial;
// flash select material color to flashing red/yellow
this.selectMaterial.color.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000);
this.infoElem.textContent = this.pickedObject.name;
}
}
}
const pickPosition = {x: 0, y: 0};
const pickHelper = new PickHelper();
clearPickPosition();
function render(time) {
time *= 0.001; // convert to seconds;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cameraPole.rotation.y = time * .1;
pickHelper.pick(pickPosition, scene, camera, time);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function getCanvasRelativePosition(event) {
const rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
};
}
function setPickPosition(event) {
const pos = getCanvasRelativePosition(event);
pickPosition.x = (pos.x / canvas.clientWidth ) * 2 - 1;
pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1; // note we flip Y
}
function clearPickPosition() {
// unlike the mouse which always has a position
// if the user stops touching the screen we want
// to stop picking. For now we just pick a value
// unlikely to pick something
pickPosition.x = -100000;
pickPosition.y = -100000;
}
window.addEventListener('mousemove', setPickPosition);
window.addEventListener('mouseout', clearPickPosition);
window.addEventListener('mouseleave', clearPickPosition);
window.addEventListener('touchstart', (event) => {
// prevent the window from scrolling
event.preventDefault();
setPickPosition(event.touches[0]);
}, {passive: false});
window.addEventListener('touchmove', (event) => {
setPickPosition(event.touches[0]);
});
window.addEventListener('touchend', clearPickPosition);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
#info { position: absolute; left: 0; top: 0; background: black; color: white; padding: 0.5em; font-family: monospace; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/examples/js/loaders/GLTFLoader.js"></script>
<canvas id="c"></canvas>
<div id="info"></div>

Rotate at specific pivot point

I'm trying to pivot a piece of a headphone set. It's capable of doing such in my model (in Maya).. but I can't seem to figure it out in threejs.
I know I can rotate my objects X Y and Z by doing something like this:
object.rotateX(THREE.Math.degToRad(degreeX));
object.rotateY(THREE.Math.degToRad(degreeY));
object.rotateZ(THREE.Math.degToRad(degreeZ));
But how do I keep the pivot point stationary while the rests rotates/moves? So in my example, I'd want the ear piece to be able to move left and right based off of the black-ish screw you see in my picture.
You could nest your headphones Mesh inside another THREE.Group, reposition the headphones inside this group so the pivot is in the desired position, then rotate the parent.
// You take your headphones and nest them inside a Group
var headphones = new THREE.Mesh(geom, material);
var parent = new THREE.Group();
parent.add(headphones);
// Then you move your headphones to the desired pivot position
headphones.position.set(-5, 0.1, 0);
// Parent is going to rotate around it origin
parent.rotateX(THREE.Math.degToRad(degreeX));
Note that if you want the pivot to be at (5, -0.1, 0), you should move headphones in the opposite direction: (-5, 0.1, 0).
Parent your model to another THREE.Object3D but to make it easy use the SceneUtils.attach function.
Example:
Click then drag, each time you click the pivot object will be moved to that location and then the model (the cube) will be attached to the pivot by calling THREE.SceneUtils.attach(model, scene, pivot). When you let off the mouse the model is detached using THREE.SceneUtils.detach(model, pivot, scene).
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// make the camera look down
camera.position.set(0, 10, 0);
camera.up.set(0, 0, -1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
scene.add(new THREE.GridHelper(40, 40));
let model;
{
const cubeSize = 3;
const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
const cubeMat = new THREE.MeshBasicMaterial({color: 'red'});
model = new THREE.Mesh(cubeGeo, cubeMat);
model.position.set(.5, .5, .5);
scene.add(model);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
}
render();
let rotate = false;
const startPos = {x:0, y:0};
const raycaster = new THREE.Raycaster();
const pivot = new THREE.Object3D();
scene.add(pivot);
pivot.add(new THREE.AxesHelper(.5));
function setPivotPoint(e) {
startPos.x = e.clientX;
startPos.y = e.clientY;
const normalizedPosition = {
x: e.clientX / canvas.clientWidth * 2 - 1,
y: e.clientY / canvas.clientHeight * -2 + 1,
};
// this part is NOT important to the answer. The question
// is how to rotate from some point. This code is picking
// a point. Which point to pick was not part of the question
// but to demo the solution it's important to pick a point
// put the pivot where the mouse was clicked
raycaster.setFromCamera(normalizedPosition, camera);
const intersection = raycaster.intersectObjects(scene.children)[0];
if (intersection) {
if (rotate) {
removeFromPivot();
}
pivot.position.copy(intersection.point);
pivot.rotation.set(0,0,0);
pivot.updateMatrixWorld();
rotate = true;
// this the important part. We're making the cube
// a child of 'pivot' without it moving in world space
THREE.SceneUtils.attach(model, scene, pivot);
render();
}
}
function rotatePivot(e) {
e.preventDefault();
if (rotate) {
const dx = e.clientX - startPos.x;
const dy = e.clientY - startPos.y;
const maxDelta = Math.abs(dx) > Math.abs(dy) ? dx : dy;
pivot.rotation.y = maxDelta * 0.01;
render();
}
}
function removeFromPivot() {
if (rotate) {
rotate = false;
THREE.SceneUtils.detach(model, pivot, scene);
window.removeEventListener('mousemove', rotatePivot);
window.removeEventListener('mouseup', removeFromPivot);
}
}
canvas.addEventListener('mousedown', (e) => {
e.preventDefault();
setPivotPoint(e);
if (rotate) {
window.addEventListener('mousemove', rotatePivot);
window.addEventListener('mouseup', removeFromPivot);
}
});
}
main();
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/js/utils/SceneUtils.js"></script>

how can i modify mesh size in my scene?

First to say, I do not speak English well.
Anyway, Let's get something straight.
I want to modify mesh(cube) size when i scroll the mouse wheel to zoomin or zoomout.
I hope to increase mesh(cube) size when zoom in and Opposite case, too.
my code is below.
<script src="../lib/Three.js/build/Three.js"></script>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script>
var CANVAS_WIDTH = 400,
CANVAS_HEIGHT = 300;
var renderer = null, //웹지엘 또는 2D
scene = null, //씬 객체
camera = null; //카메라 객체
var capture = false,
start = [],
angleX = 0,
angleY = 0,
zoom = 1.0;
function initWebGL()
{
setupRenderer();
setupScene();
setupCamera();
var myColor = new THREE.Color( 0xff0000 );
myColor.setRGB(0.0, 1.0, 0.0);
var alpha = 1.0;
renderer.setClearColor(myColor, alpha);
(function animLoop(){
//camera zoom in and zomm out
renderer.render(scene, camera);
requestAnimationFrame( animLoop );
})();
/**
mouse event code for screen control about zoom, rotate
**/
$(document).ready(function() {
console.log($("#my-canvas").length);
$("#my-canvas").on("mousedown", function(e) {
capture = true;
start = [e.pageX, e.pageY];
console.log("start:" + start);
});
$("#my-canvas").on("mouseup", function(e) {
console.log(e.type);
capture = false;
console.log("end capture");
});
$("#my-canvas").mousemove(function(e) {
console.log(e.type);
if (capture)
{
var x = (e.pageX - start[0]);
var y = (e.pageY - start[1]);
//시작위치 업데이트
start[0] = e.pageX;
start[1] = e.pageY;
angleX += x;
angleY += y;
//console.log()
}
});
});
$(document).ready(function(evt) {
$("#my-canvas").on("mousewheel", function (e) {
adjustZoom(window.event.wheelData);
}).on("DOMMouseScroll", function (e) {
//파이어폭스
adjustZoom(e.originalEvent.detail * -1.0);
});
});
function adjustZoom(delta) {
if(delta > 0)
{
zoom += 0.1;
} else {
zoom -= 0.1;
if(zoom < 0.01) { zoom = 0.1;}
}
}
}
function setupRenderer()
{
renderer = new THREE.WebGLRenderer({canvas: document.createElement( 'canvas')});
renderer.setSize( CANVAS_WIDTH, CANVAS_HEIGHT );
$(renderer.domElement).attr('id','my-canvas');
//캔버스 엘리먼트를 추가하는 곳
document.body.appendChild( renderer.domElement );
}
function setupScene()
{
scene = new THREE.Scene();
addMesh();
addLight();
}
function setupCamera()
{
camera = new THREE.PerspectiveCamera(
35, //시야
CANVAS_WIDTH / CANVAS_HEIGHT, //종횡비
.1, //전방 절단면
10000 //후방 절단면
);
camera.position.set(-15, 10, 10);
camera.lookAt( scene.position );
scene.add( camera );
}
function addMesh()
{
var cube = new THREE.Mesh(
new THREE.CubeGeometry( 5, 7, 5 ),
new THREE.MeshLambertMaterial( { color: 0x0000FF} )
);
scene.add(cube);
}
function addLight()
{
var light = new THREE.PointLight( 0xFFFFFF );
light.position.set( 20, 20, 20 );
scene.add(light);
}
</script>
You wish to modify the scale value of the object. This can be done for each axis.
Each mesh object has a scale value as a vector.
So this would
mesh.scale.set( 2, 1, 1 )
Or in your case
cube.scale.set();
You can also access it this way,
cube.scale.x = 2.0;
Though the cube object is stored locally, you might want to set the globally and alter this value with the mouse action.
Hope that well.
As a note, the question provides a bit too much of the script, shorter and faster to the point is better.

complex clipping boundary in kinetic.js with draggable image

Check out my html5 based clipping constraint on
http://shedlimited.debrucellc.com/test3/canvaskinclip.html
(messing with jsfiddle on http://jsfiddle.net/aqaP7/4/)
So, in html5 I can easily draw a shaped boundary like the following:
context.beginPath();
context.moveTo(5, 5);
context.lineTo(34, 202);
context.lineTo(2, 405);
context.lineTo(212, 385);
context.lineTo(425, 405);
context.lineTo(400, 202);
context.lineTo(415, 10);
context.lineTo(212, 25);
context.clip();
In kinetic.js though, all I see for clipping options is: height, width, and x, y,
I came across the following : Mask/Clip an Image using a Polygon in KineticJS, but the inner/fill image can't be set to draggable
any help please!
In the new kineticJS versions, a lot of the work is done in the background for you.
Take a look at this tutorial:
This fiddle gets you pretty close, here's the code:
<body>
<div id="container"></div>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.0-beta2.js"></script>
<script>
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
function draw(images) {
var stage = new Kinetic.Stage({
container: 'container',
width: 600,
height: 700
});
var layer = new Kinetic.Layer();
var patternPentagon = new Kinetic.RegularPolygon({
x: 220,
y: stage.getHeight() / 4,
sides: 5,
radius: 70,
fillPatternImage: images.yoda,
fillPatternOffset: [-220, 70],
stroke: 'black',
strokeWidth: 4,
draggable: true
});
patternPentagon.on('dragmove', function() {
//this.setFillPatternImage(images.yoda);
//this.setFillPatternOffset(-100, 70);
var userPos = stage.getUserPosition();
this.setFillPatternOffset(-userPos.x,-userPos.y);
layer.draw();
this.setX(220);
this.setY(stage.getHeight() / 4);
});
layer.add(patternPentagon);
stage.add(layer);
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
loadImages(sources, function(images) {
draw(images);
});
</script>
</body>
There is a more complex/accurate way of doing this without making it a background pattern, like with grouping objects together

Resources