I am trying to render textures on planes loaded from different URL's. For some reason after 2nd or 3rd image I can see in browser that loading image is stuck and it is not being rendered.
Adding the code used:
function init() {
loadPicturesFromDirectUrl();
}
function loadPicturesFromDirectUrl(currentPictureIndex) {
if (currentPictureIndex === undefined) {
currentPictureIndex = 0;
}
var picture = data.pictures[currentPictureIndex];
var loader = new THREE.TextureLoader();
loader.load(picture.url, function (texture) {
renderPicture(picture, texture);
currentPictureIndex++;
if (currentPictureIndex > data.pictures.length - 1) {
return;
}
loadPicturesFromDirectUrl(currentPictureIndex);
}, null, function (e) {
console.log(e);
});
}
function renderPicture(picture, texture) {
var planeGeometry = new THREE.PlaneGeometry(picture.size.width, picture.size.height);
var planeMaterial = new THREE.MeshBasicMaterial({ map: texture });
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.position.x = picture.location.x;
planeMesh.position.y = picture.location.y;
planeMesh.rotateY(myzeum.toRadians(180));
scene.add(planeMesh);
}
You may want to try to preload all the pictures in one function and store them in an array and render them only after all of them have been loaded. Like so:
var all_textures = [];
function init() {
loadPicturesFromDirectUrl();
for int (i = 0; i < all_picture.length; i++) {
renderPicture(data.pictures[i], all_textures[i])
}
}
function loadPicturesFromDirectUrl(currentPictureIndex) {
if (currentPictureIndex === undefined) {
currentPictureIndex = 0;
}
var picture = data.pictures[currentPictureIndex];
var loader = new THREE.TextureLoader();
loader.load(picture.url, function (texture) {
all_textures[currentPictureIndex] = texture
currentPictureIndex++;
if (currentPictureIndex > data.pictures.length - 1) {
return;
}
loadPicturesFromDirectUrl(currentPictureIndex);
}, null, function (e) {
console.log(e);
});
}
function renderPicture(picture, texture) {
var planeGeometry = new THREE.PlaneGeometry(picture.size.width, picture.size.height);
var planeMaterial = new THREE.MeshBasicMaterial({ map: texture });
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.position.x = picture.location.x;
planeMesh.position.y = picture.location.y;
planeMesh.rotateY(myzeum.toRadians(180));
scene.add(planeMesh);
}
I have a obj model that I'm loading via THREE.LoadingManager and a few textures. When I'm opening the page with clear cache - I'm always getting model without textures. I can see them after pressing F5. My code looks like this:
var manager = new THREE.LoadingManager();
var loader = new THREE.TextureLoader(manager);
var textures_loaded = 0;
var id;
for (id in materials) {
loader.load('images/' + materials[id].unique_id + '.jpg', function(t) {
t.magFilter = THREE.NearestFilter;
t.minFilter = THREE.LinearMipMapLinearFilter;
var re = /images\/(\d+)\.jpg/g;
var result = re.exec(t.image.currentSrc);
material = materials.filter(function(obj) {
return obj.unique_id == result[1];
}).shift();
material.setTexture(t);
textures_loaded += 1;
if (textures_loaded == materials.length) {
mainLoop();
}
});
}
var loader = new THREE.OBJLoader(manager);
loader.load('obj/' + model.model_name, function (object) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
var mesh = model.meshes.filter(function(mesh) {
return mesh.name == child.name;
}).shift();
child.material.map = mesh.material.texture;
child.geometry.buffersNeedUpdate;
child.geometry.uvsNeedUpdate;
}
});
scene.add(object);
}
);
I'm running mainLoop only after all textures are loaded. But sometimes model loads faster. How can I control order of loading objects using THREE.LoadingManager ?. How can I show model only after everything is loaded ?
I would do something like this. Have two functions responsible for loading each set of content inline. In this case the meshes will not be loaded until the materials are done loading.
var manager = new THREE.LoadingManager();
var loader = new THREE.TextureLoader(manager);
function loadMaterials(materials, callback){
var textures_loaded = 0;
for (var id in materials) {
loader.load('images/' + materials[id].unique_id + '.jpg', function(t) {
t.magFilter = THREE.NearestFilter;
t.minFilter = THREE.LinearMipMapLinearFilter;
var re = /images\/(\d+)\.jpg/g;
var result = re.exec(t.image.currentSrc);
material = materials.filter(function(obj) {
return obj.unique_id == result[1];
}).shift();
material.setTexture(t);
textures_loaded += 1;
if (textures_loaded == materials.length) {
callback("materialsLoaded");
//mainLoop();
}
});
}
}
var loadedMeshes = [];
function loadMeshes(meshes, callback){
var meshes_loaded = 0;
for(var i in meshes){
var loader = new THREE.OBJLoader(manager);
loader.load('obj/' + model.model_name, function (object) {
loadedMeshes.push(object);
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
//you'll need to map to the right material.
child.material.map = mesh.material.texture;
child.geometry.buffersNeedUpdate;
child.geometry.uvsNeedUpdate;
}
});
if(loadedMeshes.length == meshes.length){
callback("meshesLoaded");
}
};
}
}
var materials = ["material1", "material2"];
var meshes = ["mesh1.obj", "mesh2.obj"];
function onLoad(){
for(var i in loadedMeshes){
scene.add(loadedMeshes[i]);
}
mainLoop();
}
function init(){
loadMaterials(materials, function(t){
console.log(t);
loadMeshes(meshes, function(t){
console.log(t);
onLoad(t);
})
})
}
Maybe you can try to execute your mainLoop only when the document is ready, using some jQuery:
$('document').ready(function mainLoop(){});
"this event does not get triggered until all assets such as images have been completely received"
from jQuery .ready event
In my application i have to show the path covered by an user in particulat date working fine for first response,from second response from ajax i am getting data which is diffrent from first one but still map showing 1st response result
I included google map javascript api like below in header section of html
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
I am using following code in my jsp file
<script>
$(document).ready(function(){
window.setInterval(positionCheck, 20000);
});
</script>
<script>
$(document).ready(function(){
document.getElementById("directions_panel").innerHTML = "";
document.getElementById("map").innerHTML = "";
$("#searchSubmit").onclick(){
positionCheck();
}
});
</script>
<script type="text/javascript">
function positionCheck(){
var username=$("#xmlLabel").val();
var searchDate=$("#searchDate").val();
if(username!=""&& searchDate!=''){
This part where i used ajax call working fine for first response
from second response onwards problem arises it is showing content of first response from server
$.ajax({
type : "POST",
url : "searchLocation.mnt",
data :"xmlLabel="+username+"&searchDate="+searchDate,
dataType : "json",
mimeType : 'application/json',
success : function(data) {
if(data!=""){
mapLoaded(data);
function mapLoaded(data){
var size=0;
var counts=0;
var stops = data;
alert(stops);
size =stops.length;
if(stops.length>0){
var mapid=document.getElementById("map");
var map = new window.google.maps.Map(mapid);
// new up complex objects before passing them around
var directionsDisplay = new window.google.maps.DirectionsRenderer({suppressMarkers: true,polylineOptions: {
strokeColor: "black"
}});
var directionsService = new window.google.maps.DirectionsService();
Tour_startUp(stops);
window.tour.loadMap(map, directionsDisplay);
window.tour.fitBounds(map);
alert(stops.length);
if (stops.length > 1)
window.tour.calcRoute(directionsService, directionsDisplay);
}
alert(stops.length);
function Tour_startUp(stops) {
if (!window.tour) window.tour = {
updateStops: function (newStops) {
stops = newStops;
},
// map: google map object
// directionsDisplay: google directionsDisplay object (comes in empty)
loadMap: function (map, directionsDisplay) {
var myOptions = {
zoom:4,
center: new window.google.maps.LatLng(17.379818, 78.478542), // default to Hyderabad
mapTypeId: window.google.maps.MapTypeId.ROADMAP
};
map.setOptions(myOptions);
directionsDisplay.setMap(map);
},
fitBounds: function (map) {
var bounds = new window.google.maps.LatLngBounds();
// extend bounds for each record
jQuery.each(stops, function (key, val) {
var myLatlng = new window.google.maps.LatLng(val.latitude, val.longitude);
bounds.extend(myLatlng);
});
map.fitBounds(bounds);
},
calcRoute: function (directionsService, directionsDisplay) {
var batches = [];
var itemsPerBatch = 10; // google API max = 10 - 1 start, 1 stop, and 8 waypoints
var itemsCounter = 0;
var wayptsExist = stops.length > 0;
while (wayptsExist) {
var subBatch = [];
var subitemsCounter = 0;
for (var j = itemsCounter; j < stops.length; j++) {
subitemsCounter++;
subBatch.push({
location: new window.google.maps.LatLng(stops[j].latitude, stops[j].longitude),
stopover: true
});
if (subitemsCounter == itemsPerBatch)
break;
}
itemsCounter += subitemsCounter;
batches.push(subBatch);
wayptsExist = itemsCounter < stops.length;
// If it runs again there are still points. Minus 1 before continuing to
// start up with end of previous tour leg
itemsCounter--;
}
// now we should have a 2 dimensional array with a list of a list of waypoints
var combinedResults;
var unsortedResults = [{}]; // to hold the counter and the results themselves as they come back, to later sort
var directionsResultsReturned = 0;
for (var k = 0; k < batches.length; k++) {
var lastIndex = batches[k].length - 1;
var start = batches[k][0].location;
var end = batches[k][lastIndex].location;
// trim first and last entry from array
var waypts = [];
waypts = batches[k];
waypts.splice(0, 1);
waypts.splice(waypts.length - 1, 1);
var request = {
origin: start,
destination: end,
waypoints: waypts,
travelMode: window.google.maps.TravelMode.WALKING
};
(function (kk) {
directionsService.route(request, function (result, status) {
if (status == window.google.maps.DirectionsStatus.OK) {
var unsortedResult = { order: kk, result: result };
unsortedResults.push(unsortedResult);
directionsResultsReturned++;
if (directionsResultsReturned == batches.length) // we've received all the results. put to map
{
// sort the returned values into their correct order
unsortedResults.sort(function (a, b) { return parseFloat(a.order) - parseFloat(b.order); });
var count = 0;
for (var key in unsortedResults) {
if (unsortedResults[key].result != null) {
if (unsortedResults.hasOwnProperty(key)) {
if (count == 0) // first results. new up the combinedResults object
combinedResults = unsortedResults[key].result;
else {
// only building up legs, overview_path, and bounds in my consolidated object. This is not a complete
// directionResults object, but enough to draw a path on the map, which is all I need
combinedResults.routes[0].legs = combinedResults.routes[0].legs.concat(unsortedResults[key].result.routes[0].legs);
combinedResults.routes[0].overview_path = combinedResults.routes[0].overview_path.concat(unsortedResults[key].result.routes[0].overview_path);
combinedResults.routes[0].bounds = combinedResults.routes[0].bounds.extend(unsortedResults[key].result.routes[0].bounds.getNorthEast());
combinedResults.routes[0].bounds = combinedResults.routes[0].bounds.extend(unsortedResults[key].result.routes[0].bounds.getSouthWest());
}
count++;
}
}
}
directionsDisplay.setDirections(combinedResults);
var legs = combinedResults.routes[0].legs;
var summaryPanel = document.getElementById('directions_panel');
summaryPanel.innerHTML = '';
var totdist=0;
// alert(legs.length);
for (var i=0; i < legs.length;i++){
var markerletter = "A".charCodeAt(0);
var markerletter2= "B".charCodeAt(0)
markerletter += i;
markerletter2 += i;
markerletter = String.fromCharCode(markerletter);
markerletter2 = String.fromCharCode(markerletter2);
createMarker(directionsDisplay.getMap(),legs[i].start_location,legs[i].start_address,markerletter);//To display location address on the marker
var routeSegment = i + 1;
var point=+routeSegment+1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += '<b>Point '+ routeSegment +' :</b>'+ ' ' +legs[i].start_address + ' <br> ';
summaryPanel.innerHTML += '<b>Point '+ point +' :</b>'+ ' '+legs[i].end_address + '<br>';
summaryPanel.innerHTML += '<b>Distance Covered '+' :</b>'+legs[i].distance.text + '<br><br>';
var test=legs[i].distance.text.split(' ');
var one=parseFloat(test[0]);
if(test[1]=="m"){
var one=parseFloat(test[0]/1000);
}
totdist=parseFloat(totdist)+parseFloat(one);
}
summaryPanel.innerHTML += '<b> Total Distance :'+totdist + 'km'+ '</b><br><br>';
var i=legs.length;
var markerletter = "A".charCodeAt(0);
markerletter += i;
markerletter = String.fromCharCode(markerletter);
createMarker(directionsDisplay.getMap(),legs[legs.length-1].end_location,legs[legs.length-1].end_address,markerletter);
}
}
});
})(k);
}
}
};
}
var infowindow = new google.maps.InfoWindow(
{
size: new google.maps.Size(150,50)
});
var icons = new Array();
icons["red"] = new google.maps.MarkerImage("mapIcons/marker_red.png",
// This marker is 20 pixels wide by 34 pixels tall.
new google.maps.Size(20, 34),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is at 9,34.
new google.maps.Point(9, 34));
function getMarkerImage(iconStr) {
counts++;
if(counts==size){
var markerimageLoc = "http://www.maps.google.com/mapfiles/ms/icons/blue.png";
}else{
if (iconStr=="undefined") {
iconStr = "red";
var markerimageLoc = "http://www.maps.google.com/mapfiles/ms/icons/red.png";
}
else{
var markerimageLoc="http://www.google.com/mapfiles/marker"+ iconStr +".png";
// var markerimageLoc = "http://www.maps.google.com/mapfiles/ms/icons/red.png";
}
}
icons[iconStr] = new google.maps.MarkerImage(markerimageLoc,
// This marker is 20 pixels wide by 34 pixels tall.
new google.maps.Size(25, 34),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is at 6,20.
new google.maps.Point(9, 34));
return icons[iconStr];
}
// Marker sizes are expressed as a Size of X,Y
// where the origin of the image (0,0) is located
// in the top left of the image.
// Origins, anchor positions and coordinates of the marker
// increase in the X direction to the right and in
// the Y direction down.
var iconImage = new google.maps.MarkerImage('mapIcons/marker_red.png',
// This marker is 20 pixels wide by 34 pixels tall.
new google.maps.Size(20, 34),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is at 9,34.
new google.maps.Point(9, 34));
var iconShadow = new google.maps.MarkerImage('http://www.google.com/mapfiles/shadow50.png',
// The shadow image is larger in the horizontal dimension
// while the position and offset are the same as for the main image.
new google.maps.Size(37, 34),
new google.maps.Point(0,0),
new google.maps.Point(9, 34));
// Shapes define the clickable region of the icon.
// The type defines an HTML <area> element 'poly' which
// traces out a polygon as a series of X,Y points. The final
// coordinate closes the poly by connecting to the first
// coordinate.
var iconShape = {
coord: [9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0],
type: 'poly'
};
function createMarker(map, latlng, label, character) {
var markerletter=character;
if( /[^a-zA-Z]/.test( character ) ) {
var markerletter="undefined";
}
var contentString = '<b>'+label+'</b><br>';
var marker = new google.maps.Marker({
position: latlng,
map: map,
shadow: iconShadow,
icon: getMarkerImage(markerletter),
shape: iconShape,
title: label,
zIndex: Math.round(latlng.lat()*-100000)<<5
});
marker.myname = label;
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map,marker);
});
return marker;
}
}
}else{
}
},
error : function(e) {
alert('Error: ' + e,"Alert Box");
}
});
}
}
</script>
and i included div tag like this in body section
<div id="map" style="border: 2px solid #3872ac; height: 500px;"
class="col-sm-6"></div>
above code working fine if i am not using ajax,problem exist only for ajax
sorry to trouble all of you,i feel very silly that i declared variable of stops globally and i need to call methods like below
function initialize(data) {
size = 0;
counts = 0;
var map=0;
var stops = data;
size = stops.length;
if (stops.length > 0) {
var map = new window.google.maps.Map(document
.getElementById("map"));
// new up complex objects before passing them around
var directionsDisplay = new window.google.maps.DirectionsRenderer(
{
suppressMarkers : true,
polylineOptions : {
strokeColor : "black"
}
});
var directionsService = new window.google.maps.DirectionsService();
Tour_startUp(stops);
window.tour.loadMap(map, directionsDisplay);
i changed the below methods from window.tour.fitBounds(map); window.tour.calcRoute(stops,directionsService,
directionsDisplay); to
window.tour.fitBounds(map,stops);
if (stops.length > 1)
window.tour.calcRoute(stops,directionsService,
directionsDisplay);
}
}
Using Three.js r68+ I have been needing to very slightly modify the source to get my animations working correctly. Unmodified only one of each type of model are animated (there are multiple spawns of each type of model).
This is the modified source at line 29407 (posted code beginning at 29389):
THREE.AnimationHandler = {
LINEAR: 0,
CATMULLROM: 1,
CATMULLROM_FORWARD: 2,
//
add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },
//
animations: [],
init: function ( data ) {
// original -> if ( data.initialized === true ) return;
if ( data.initialized === true ) return data; //<-- modified
The function now returns the animation data if initialized. I'm assuming it doesn't do so because of caching. My question is what is the best practice for animating multiple models that have the same animation names? I tried naming them uniquely per model name ie: "Death_MaleWarrior" but this had no effect.
Currently my models and animations are handled like so:
var modelArray = [];
var geoCache = [];
var loader = new THREE.JSONLoader(true);
var _MODEL = function(data){
this.data = data;
this.mesh = null;
this.animations = {};
this.canAnimate = false;
this.parseAnimations = function () {
var len,i,anim;
if (this.mesh) {
if( this.mesh.geometry.animations ){
this.canAnimate = true;
len = this.mesh.geometry.animations.length;
if( len ){
for(i=0;i<len;i++){
anim = this.mesh.geometry.animations[i];
if( anim ){
this.animations[anim.name] = new THREE.Animation( this.mesh, anim );
}
}
}
}
}
};
this.playAnimation = function(label){
if (this.canAnimate) {
if( this.animations[label] ){
//if( this.animations[label].data ){
this.animations[label].play(0,1);
//}
}
}
return false;
};
this.load = function(geo){
var mat;
mat = new THREE.MeshPhongMaterial({color:somecolor, skinning:true})
this.mesh = new THREE.SkinnedMesh(geo,mat);
this.mesh.position.x = this.data.position[0];
this.mesh.position.y = this.data.position[1];
this.mesh.position.z = this.data.position[2];
this.parseAnimations();
scene.add(this.mesh);
this.playAnimation('Idle');
};
this.init = function(){
var geo;
if( geoCache[this.data.name] ){
geo = geoCache[this.data.name];
this.load(geo);
}else{
geo = loader.parse(JSON.parse(this.data.json)).geometry;
geoCache[this.data.name] = geo;
this.load(geo);
}
};
this.init();
};
var dataArray = [{name:'MaleWarrior',json:'json_data',position:[x,y,z]},{name:'FemaleWarrior',json:'json_data',position:[x,y,z]},{name:'MaleWarrior',json:'json_data',position:[x,y,z]}];
for(var i=0, len=objectArray.length; i<len; i++){
modelArray.push(new _MODEL(dataArray[i]) );
}
In this example the first MaleWarrior will animate but the second will not. If there was a second female she would not be animated either as the animation (even though it is a new THREE.Animation() ) will be considered initialized and will not return any data. If I do not check for the existence of animations.data in playAnimation I get the error ""Uncaught TypeError: Cannot read property 'name' of undefined " on line 29665".
Is what I'm doing bypassing animation caching and hurting performance? I feel like I'm missing something. How will an animation play without data?
All animation names are the same for every model "Idle", "Run", "Attack" etc.
Any help would be appreciated. If I'm not being clear enough please let me know. I have added additional detail.
This turned out to be an official three.js bug.
https://github.com/mrdoob/three.js/issues/5516
am exporting my models using ObjectExporter, my code is follows
exporter = new THREE.ObjectExporter;
var obj = exporter.parse(globalObject);
var json = JSON.stringify(obj);
console.log(json);
i can get the json exported data successfully, but after load it using ObjectLoader the Geometry only loading materials are not loading, am loading my saved model by following code
var loader = new THREE.ObjectLoader();
loader.load("savedjson.json",function ( obj ) {
scene.add( obj );
console.log(obj);
});
any clue to get materials work with the ObjectExporter?
I had the same problem. I have a first attempt at a workaround(but it needs to be improved for sure). What I do is, after loading the object, I traverse the model
loader.load("savedjson.json", function (obj){
obj.traverse(function(child){ initChild(child); });
scene.add(obj);
}
In initChild(child) I do this:
initChild(child)
{
if(child.material != null)
{
var childMaterialName = child.material.name;
child.material = new THREE.MeshPhongMaterial();
child.material.name = childMaterialName ;
AssignMap(child.material);
}
}
In AssignMap(material) I first load the textures, then assign them based on the material name:
AssignMap(material)
{
var texture_metal = new THREE.ImageUtils.loadTexture("media/texture_metal.jpg");
var texture_glass = new THREE.ImageUtils.loadTexture("media/texture_glass.jpg");
if(material.name == "metal texture")
{
material.map = texture_metal;
}
if(material.name == "glass texture")
{
material.map = texture_glass;
}
}