Model loading freeze scene - javascript

My scene temporary freeze during the loading time of my model. Are there any options in preventing this?
var loader = new THREE.JSONLoader();
loader.load("model.js", function(smooth) {
smooth.mergeVertices();
smooth.computeFaceNormals();
smooth.computeVertexNormals();
var modifier = new THREE.SubdivisionModifier(1);
modifier.modify(smooth);
scene.add(smooth);
var mesh = new THREE.Mesh(smooth, new THREE.MeshPhongMaterial({
color : 0x222222
}));
$('input').change(function() {
if ($("#radio1, #radio2, #radio3, #radio4").is(":checked")) {
scene.add(vierring);
} else if ($("#radio1, #radio2, #radio3, #radio4").is(":not(:checked)")) {
scene.remove(vierring);
}
});
});

Related

How to give texture to object in dae file as using 3D Viewer component in SAP UI5?

I am opening dae file in SAP UI5 as using 3D Viewer component and thridparty library(three.js). I am giving texture to these boxes at runtime stage. But I cannot see the changes of texture in box. When I move object by the mouse I can see the changes. How can I see the changes texture at runtime stage without moving the mouse?
I'm following this link: https://sapui5.hana.ondemand.com/sdk/#/entity/sap.ui.vk.tutorial.VIT/sample/sap.ui.vk.tutorial.VIT.08
<mvc:View controllerName="colladaLoader.controller.App" xmlns="sap.m" xmlns:c="sap.ui.core" xmlns:vk="sap.ui.vk"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true">
<Shell>
<App id="colladaLoader">
<Page title="{i18n>pageTitle}">
<vk:Viewer id="viewer" selectionChanged="selectionChanged" sceneLoadingSucceeded="sceneLoadingSucceeded">
<vk:contentResources>
<vk:ContentResource id="viewerResource" source="models/boxes.dae" sourceType="dae"
name="Collada Model"/>
</vk:contentResources>
</vk:Viewer>
</Page>
</App>
</Shell>
</mvc:View>
//Controller;
//library : "sap/ui/vk/threejs/thirdparty/three"
onInit: function () {
var that = this
var colladaLoader = function (parentNode, contentResource) {
return new Promise(function (resolve, reject) {
var url = "https://threejs.org/examples/js/loaders/ColladaLoader.js";
jQuery.ajax({
url: url,
dataType: "script",
cache: true
}).done(function () {
var loader = new THREE.ColladaLoader();
loader.load(contentResource.getSource(),
function (collada) { // onload
parentNode.add(collada.scene);
resolve({
node: parentNode,
contentResource: contentResource
});
dae = collada.scene;
dae.traverse(function (child) {
objects.push(child);
});
that.onChangeTextture();
},
null,// onprogress
reject // onfail
);
}).fail(function () {
MessageToast.show("Cannot load Collada loader from " + url);
reject();
});
});
};
ContentConnector.addContentManagerResolver({
pattern: "dae",
dimension: 3,
contentManagerClassName: "sap.ui.vk.threejs.ContentManager",
settings: {
loader: colladaLoader
}
});
},
//ChangeTexture code blog
onChangeTextture : function(){
var textureLoader = new THREE.TextureLoader();
var textureGreen = textureLoader.load('./materials/Green.jpg');
var textureRed = textureLoader.load('./materials/red.jpg');
objects[2].material.map = null;
objects[2].material = new THREE.MeshBasicMaterial();
setInterval(function(){
var number = Math.floor(Math.random() * 10);
if(number % 2 === 0) {
objects[2].material = new THREE.MeshBasicMaterial({
map: textureGreen
});
objects[2].material.needsUpdate = true;
objects[2].material.map.needsUpdate = true;
}
else {
objects[2].material = new THREE.MeshBasicMaterial({
map: textureRed
});
objects[2].material.needsUpdate = true;
objects[2].material.map.needsUpdate = true;
}}, 3000);
}![enter image description here](https://i.stack.imgur.com/WTKPC.gif)

Retaining the Phongness even after material change in three.js

I tried loading my .obj and .mtl files and now I'm trying to change the material of the object on keypress, I'm able to do changes as expected using texture images. However when I want to change materials using a phong effect with or without texture image it's not happening.
//THIS METHOD IS WORKING FINE WHEN I USE BASIC TEXTURE IMAGES ON KEYPRESS
var texture1 = THREE.ImageUtils.loadTexture("neavik/9.jpg");
var texture2 = THREE.ImageUtils.loadTexture("neavik/10.jpg");
if (child.material.name == "meta") {
child.material.map = texture1;
child.material.needsUpdate = true;
child.receiveShadow = true;
var index = 0;
var onKeyDown = function(event) {
if (event.keyCode == 67) { // when 'c' is pressed
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
var colors = [texture2, texture1];
if (child.material.name == "meta") {
if (index == colors.length) index = 0;
child.material.map = colors[index++];
child.material.needsUpdate = true;
child.geometry.buffersNeedUpdate = true;
child.geometry.uvsNeedUpdate = true;
}
}
}
};
document.addEventListener('keyup', onKeyDown, true);
});
// Now if i try to change materials with same effect like PHONG it doesn't reflect any changes on Keypress.[ No errors as well ]
var texture1 = THREE.ImageUtils.loadTexture("neavik/9.jpg");
var texture2 = THREE.ImageUtils.loadTexture("neavik/10.jpg");
var cleana = new THREE.MeshPhongMaterial({
map: texture1,
color: 0xffffef,
specular: 0x000000,
combine: THREE.AddOperation,
reflectivity: 0
})
var cleanb = new THREE.MeshPhongMaterial({
map: texture2,
color: 0xffffef,
specular: 0x000000,
combine: THREE.AddOperation,
reflectivity: 0
})
if (child.material.name == "meta") {
child.material = cleana;
child.material.needsUpdate = true;
child.receiveShadow = true;
var index = 0;
var onKeyDown = function(event) {
if (event.keyCode == 67) { // when 'c' is pressed
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
var colors = [cleana, cleanb];
if (child.material.name == "meta") {
if (index == colors.length) index = 0;
child.material = colors[index++];
child.material.needsUpdate = true;
child.geometry.buffersNeedUpdate = true;
child.geometry.uvsNeedUpdate = true;
}
}
}
};
document.addEventListener('keyup', onKeyDown, true);
});
Please let me know if there is another way to do this.
If you want to exchange the whole material you need to do:
child.material = cleana;
or
child.material = cleanb;
and then you most likely have to do:
child.material.needsUpdate = true;
Read more on how to update material in this How to update things reference page in the chapter Materials
UPDATE
So if you want to set using an array:
var materials = [cleana, cleanb];
child.material = materials[0]; // cleana
or
child.material = materials[1]; // cleanb
and then also most likely:
child.material.needsUpdate = true;
It works totally fine with an array of materials. You don't even need to set material.needsUpdate = true. A demonstration here in this fiddle.

Three.js + OrbitControls - Uncaught TypeError: Cannot read property 'render' of undefined

I am writing a class for rotating cube, but every time i rotate it or zoom, i get an error "Cannot read property 'render' of undefined". What am i doing wrong? I guess something is wrong with the scopes. Here is my class:
myclass = function() {
this.camera = null;
this.scene = null;
this.renderer = null;
this.product = null;
this.init = function (container) {
this.scene = new THREE.Scene();
this.camera = createCamera();
this.product = createProduct();
this.scene.add(this.product);
this.createRenderer();
this.setControls();
container.appendChild(this.renderer.domElement);
this.animate();
};
function createCamera() {
var camera = new THREE.PerspectiveCamera(20, 300 / 400, 1, 10000);
camera.position.z = 1800;
return camera;
}
function createProduct() {
var geometry = new THREE.BoxGeometry(300, 200, 200);
var materials = ...;
var product = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
return product;
}
this.createRenderer = function () {
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.renderer.setClearColor(0xffffff);
this.renderer.setSize(this.sceneWidth, this.sceneHeight);
};
this.setControls = function () {
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.addEventListener('change', this.render);
};
this.animate = function () {
requestAnimationFrame(this.animate.bind(this));
this.render();
};
this.render = function () {
this.renderer.render(this.scene, this.camera);
};
};
Tnanks.
You have a scoping problem in the callback specified here:
this.controls.addEventListener( 'change', this.render );
In your class, add
var scope = this;
Then rewrite your render() method like so:
this.render = function () {
scope.renderer.render( scope.scene, scope.camera );
};
Also, the point of adding the event listener is to remove the animation loop.
So..., remove it, and only render when the mouse causes the camera to move.
You will also have to call render() once initially, and after models load.
three.js r.69
You can use bind method as follows it will hold the scope for the render method
this.controls.addEventListener( 'change', this.render.bind(this) );

Add model.obj to three.MESH

I am new to three.js and I am making an augmented reality app on web to show plates of food. So far I have managed to show a cube. The problem raised when I tried to show a model.obj instead of geomerty and material in mesh. Can anyone please help me on how to put a model.obj in THREE.MESH since I cannot do it. Below is my code.
function createContainer() {
var model = new THREE.Object3D();
model.matrixAutoUpdate = false;
return model;
}
function createMarkerMesh(color) {
manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
var texture = new THREE.Texture();
var loader = new THREE.ImageLoader(manager);
loader.load(texturePath, function (image) {
texture.image = image;
texture.needsUpdate = true;
texture.transparent = true;
});
var loader = new THREE.OBJLoader(manager);
return loader.load(objectPath, function (object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
child.material.transparent = true;
}
});
object.position.z = -50;
return object;
});
// var geometry = new THREE.CubeGeometry( 100,100,100 );
//var material = new THREE.MeshPhongMaterial( {color:color, side:THREE.DoubleSide } );
var mesh = new THREE.Mesh( object.geometry, object.material);
mesh.position.z = -50;
return mesh;
}
function createMarkerObject(params) {
//return createMarkerMesh(params.color);
var modelContainer = createContainer();
var modelMesh = createMarkerMesh(params.color);
console.log(modelMesh);
modelContainer.add( modelMesh);
function transform(matrix) {
modelContainer.transformFromArray( matrix );
}
return {
transform: transform,
model: modelContainer
}
return {
createMarkerObject:createMarkerObject
}
}
The code in function Create MArker MEsh is were the cube was created and worked fine now I have commented those parts and tried to show the object but nothing is happening please help me.
try this change in your code... let me know if any problem raise..
function createMarkerMesh(color) {
manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
var texture = new THREE.Texture();
var loader = new THREE.ImageLoader(manager);
loader.load(texturePath, function (image) {
texture.image = image;
texture.needsUpdate = true;
texture.transparent = true;
});
var tmpMesh;
var loader = new THREE.OBJLoader(manager);
loader.load(objectPath, function (object) {
var group = new THREE.Object3D()
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
child.material.transparent = true;
//here in child the geometry and material are available
var mesh = new THREE.Mesh( child.geometry, child.material);
//mesh.position.z = -50;
group.add(mesh);
}
});
group.position.z = -50;
scene.add(group);//return group;
});
}
Finally I have found an answer for this problem and here I am going to explain what the problem was with the above code. Hope that this will help.
The problem was that the createMarkerObject was being called before the mesh was created so the object3D container was being filled with an empty mesh. To arrange this I have declared the modelContainer as a global variable and inserted it in the createMarkerMesh function. In the createMarkerObject I have made an if condition so that it checks if the modelContainer is full before adding it to the object3D container. Below is the updated code.
//Loads the model and texture and creates a mesh returns mesh with model
function createMarkerMesh() {
manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
var texture = new THREE.Texture();
var loader = new THREE.ImageLoader(manager);
loader.load(texturePath, function (image) {
texture.image = image;
texture.needsUpdate = true;
texture.transparent = true;
});
var tmpMesh, mesh;
var loader = new THREE.OBJLoader(manager);
loader.load(objectPath, function (object) {
tmpMesh = object; //store the object in a variable
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
child.material.transparent = true;
}
});
mesh = new THREE.Mesh( tmpMesh.children[0].geometry, tmpMesh.children[0].material);
mesh.position.z = -10;
mesh.scale.set( 3, 3, 0 );
console.log(mesh);
modelContainer.add( mesh);
console.log(modelContainer);
//return mesh;
});
}
//Creates the position of the object on the marker
function createMarkerObject() {
modelContainer = createContainer();
var modelMesh = createMarkerMesh();
//console.log(modelMesh);
if(modelMesh){
modelContainer.add( modelMesh);
}
function transform(matrix) {
modelContainer.transformFromArray( matrix );
}
return {
transform: transform,
model: modelContainer
}
return {
createMarkerObject:createMarkerObject
}
}

Circle radius with Driving Directions in Bing Maps

I'm currently developing a web site that are using Bing Maps. I'm using the Bing Maps Version 7. I have created a fully functional driving directions function. Which works like this:
The user rightclicks on the map, where doesn't matter. Then a context menu is brought up, where the user can chose between two alternatives. Which is: Align Start and Align Finish.
As you might understand those functions are creating a way-point on the locations of where the user right-clicked. Also a radius circle is aligned on respective way-point. Both start and finish way-points are dragable / movable, which means that the user can move the way-points around. The problem is that when the user moves one of the way-points the radius circle isn't moving also, which not is weird because I have not created a function for that yet. I don't think that is hard to do, but I don't know how to get the new position of the moved way-point. I'm posting my code. So I do really need some help with making this "RadiusCircleMove".
Here is my Javascript Code:
var map = null;
var directionsManager;
var directionsErrorEventObj;
var directionsUpdatedEventObj;
var startPosition;
var checkpointPosition;
var finishPosition;
var popuplat;
var popuplon;
var waypointType;
var startcircle;
var checkpointcircle;
var finishcircle;
var startcirclelat;
var startcirclelon;
var checkpointcirclelat;
var checkpointcirclelon;
var finishcirclelat;
var finishcirclelon;
$(document).ready(function () {
//this one line will disable the right mouse click menu
$(document)[0].oncontextmenu = function () { return false; }
GetMap();
});
function GetMap() {
map = new Microsoft.Maps.Map(document.getElementById("myMap"), { credentials: "Enter Bing Key Here", zoom: 4, center: new Microsoft.Maps.Location(45, -100) });
Microsoft.Maps.registerModule("BMv7.AdvancedShapes", "BMv7.AdvancedShapes.min.js");
Microsoft.Maps.loadModule("BMv7.AdvancedShapes");
//map.AttachEvent("onclick", ShowPopupMenu);
Microsoft.Maps.Events.addHandler(map, 'click', RemovePopupMenu);
Microsoft.Maps.Events.addHandler(map, 'rightclick', ShowPopupMenu);
}
function ShowPopupMenu(e) {
var point = new Microsoft.Maps.Point(e.getX(), e.getY());
popuplat = e.target.tryPixelToLocation(point).latitude
popuplon = e.target.tryPixelToLocation(point).longitude
var menu = document.getElementById('popupmenu');
menu.style.display = 'block'; //Showing the menu
menu.style.left = e.pageX + "px"; //Positioning the menu
menu.style.top = e.pageY + "px";
}
function RemovePopupMenu() {
document.getElementById("popupmenu").style.display = 'none';
}
function createDirectionsManager() {
var displayMessage;
if (!directionsManager) {
directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
//displayMessage = 'Directions Module loaded\n';
//displayMessage += 'Directions Manager loaded';
}
//alert(displayMessage);
directionsManager.resetDirections();
directionsUpdatedEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', function () {} );
}
function createDrivingRoute() {
if (!directionsManager) { createDirectionsManager(); }
directionsManager.resetDirections();
// Set Route Mode to driving
directionsManager.setRequestOptions({ routeMode: Microsoft.Maps.Directions.RouteMode.driving });
if (waypointType == "start") {
addDefaultPushpin();
startPosition = new Microsoft.Maps.Directions.Waypoint({ location: new Microsoft.Maps.Location(popuplat, popuplon) });
startcirclelat = popuplat;
startcirclelon = popuplon;
}
if (waypointType == "checkpoint") {
addDefaultPushpin();
checkpointPosition = new Microsoft.Maps.Directions.Waypoint({ location: new Microsoft.Maps.Location(popuplat, popuplon) });
checkpointcirclelat = popuplat;
checkpointcirclelon = popuplon;
}
if (waypointType == "finish") {
finishPosition = new Microsoft.Maps.Directions.Waypoint({ location: new Microsoft.Maps.Location(popuplat, popuplon) });
finishcirclelat = popuplat;
finishcirclelon = popuplon;
directionsManager.addWaypoint(startPosition);
directionsManager.addWaypoint(checkpointPosition);
directionsManager.addWaypoint(finishPosition);
directionsManager.calculateDirections();
deletePushpin();
CreateStartCircle();
CreateCheckpointCircle();
CreateFinishCircle();
}
// Set the element in which the itinerary will be rendered
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('directionsItinerary') });
//alert('Calculating directions...');
}
function createDirections() {
if (!directionsManager) {
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', { callback: createDrivingRoute });
}
else {
createDrivingRoute();
}
}
function AddStartPosition() {
waypointType = "start";
createDirections();
RemovePopupMenu();
}
function AddCheckpointPosition() {
waypointType = "checkpoint";
createDirections();
RemovePopupMenu();
}
function AddFinishPosition() {
waypointType = "finish";
createDirections();
RemovePopupMenu();
}
function addDefaultPushpin() {
var pushpin = new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(popuplat, popuplon));
map.entities.push(pushpin);
}
function deletePushpin() {
for (var i = map.entities.getLength() - 1; i >= 0; i--) {
var pushpin = map.entities.get(i);
if (pushpin instanceof Microsoft.Maps.Pushpin) {
map.entities.removeAt(i);
};
}
}
function CreateStartCircle() {
startcircle = DecStartCircle();
map.entities.push(startcircle);
}
function CreateCheckpointCircle() {
checkpointcircle = DecCheckpointCircle();
map.entities.push(checkpointcircle);
}
function CreateFinishCircle() {
finishcircle = DecFinishCircle();
map.entities.push(finishcircle);
}
/***** Start Circle ****/
function DecStartCircle() {
var polygonOptions = {
fillColor: new Microsoft.Maps.Color(100, 0, 0, 255),
strokeColor: new Microsoft.Maps.Color(100, 255, 0, 0)
};
return new BMv7.AdvanceShapes.Circle(new Microsoft.Maps.Location(startcirclelat, startcirclelon), 80000, polygonOptions);
}
/***** Checkpoint Circle ****/
function DecCheckpointCircle() {
var polygonOptions = {
fillColor: new Microsoft.Maps.Color(100, 0, 0, 255),
strokeColor: new Microsoft.Maps.Color(100, 255, 0, 0)
};
return new BMv7.AdvanceShapes.Circle(new Microsoft.Maps.Location(checkpointcirclelat, checkpointcirclelon), 80000, polygonOptions);
}
/***** Finish Circle ****/
function DecFinishCircle() {
var polygonOptions = {
fillColor: new Microsoft.Maps.Color(100, 0, 0, 255),
strokeColor: new Microsoft.Maps.Color(100, 255, 0, 0)
};
return new BMv7.AdvanceShapes.Circle(new Microsoft.Maps.Location(finishcirclelat, finishcirclelon), 80000, polygonOptions);
}
I believe you need to actually implement your DirectionsUpdated event. Your code contains the following empty function:
directionsUpdatedEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', function () {} );
You shouldn't be doing anything with the map until the directions have completed loading. Once they have loaded, then you can do the following:
Clear all map entities map.entities.clear(); which removes all previous polylines and polygons
Then reset directions: directionsManager.resetDirections(); to clear the current directions (otherwise you'll see waypoints appended).
Then get all of the waypoints from the directions module directionsManager.getAllWaypoints();
Now plot your three circles.
Your problem is one of timing and correct sequencing.

Categories