Three.js select GLTF object from a dropdown with DAT.gui - three.js

I started messing about with Three.js a couple of months ago.
I have basic js knowledge, most of the code was adapted from the Three.js Examples.
I want to load a model selected from a dropdown list.
There is only one model displayed at once, so I didnt want to add a new gltf loader for each model.
I have 1 gltf loader and I want to change the path based upon the option the user selects from the dropdown.
VARIABLES
let pathSelected, pathHe, pathCat, pathGears;
pathHe = ('./MODEL/he.gltf');
pathCat = ('./MODEL/cat/cat.gltf');
pathGears = ('./MODEL/gears/gears.gltf');
.....
GLTF LOADER
function init() {
...
loader = new GLTFLoader();
loader.load(pathSelected, function (gltf) {
gltf.scene.traverse(function (child) {
if (child.isMesh) {
children.push(child);
}
});
...
}
DAT.GUI PANEL
function createPanel() {
...
var objSelector = objectShow.add(settings, 'modelOptions', ["HE", "Cat", "Gear Box"]).name("Seclect").listen();
objSelector.onChange(function (value) {
var value = settings.modelOptions;
if (value == "Heat Exchanger") {
pathSelected = pathHe;
console.log('Dropdown: HE', pathSelected);
}
else if (value == "Cat") {
pathSelected = pathCat;
console.log('Dropdown: Cat', pathSelected);
}
else if (value == "Gear Box") {
pathSelected = pathGears;
console.log('Dropdown: Cat', pathSelected);
}
else { ... }
});
...
}
DEMO:
https://mellowmonks.in/demos/9/
PW: MellowMonk#123

Related

three.js intersectObjects doesn't display functions in userData object

when I do raycasting trying to apply function from clicked object. without a problem I found which object clicked via raycaster. But when I try to get the function -I already gave it to the mesh- it doesn't display.
mesh.children.forEach((child: any) => {
child.name = "portal"
child.userData.clickable = true
child.userData.dblclick = function () {
window.location.href = "example"
}
})
console.log(mesh)
I can see the clickable prop, but not the dblclick prop. But If I change the value to string, then dblclick shows as a prop.
raycasting:
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
if (intersects.length > 0) {
intersects.forEach((mesh: any) => {
if (mesh.object.userData.hasOwnProperty("clickable")) {
console.log(mesh.object.userData)
mesh.object.userData.dblclick();
}
});
}
Is it mean that intersects doesn't apply function as a value?

How to add lightmap to three js?

I baked some lightmap from on blender 2.8(Bake type - combined, Influence - "Direct", "Indirect", "Diffuse").
After that, I tried to add lightmap to part of my model.
function initColor(parent, type, mtl) {
parent.traverse(o => {
if (o.isMesh) {
if (o.name.includes(type)) {
o.material = mtl;
}
}
});
}
var lightmapTexture = new THREE.ImageUtils.loadTexture( "images/FrontLightmap.png" );
var front_material = new THREE.MeshBasicMaterial({lightMap: lightmapTexture });
const INITIAL_MAP = [
{childID: "Front", mtl: front_material}
];
for (let object of INITIAL_MAP) {
initColor(theModel, object.childID, object.mtl);
}
But the result - black material(front part).
Is there is possible to separately usage of lightmap and different material colors at the same time?

Performance issue when zooming (Leaflet 0.7.3)

I'm facing a performance problem with Leaflet (version 0.7.3). I'm working with an OSM map that I use to display a bunch of CircleMarkers linked by decorated Polylines (with arrow pattern every 25px). Loading take a little time but the main problem is that when I zoom the map I start facing severe lag (from the zoom level 16) and, beyond a certain limit (say 18 most of the time), browser just freeze and eventually crash (tested with chrome and firefox). I tried with a bunch of 1,000 linked markers, then I dropped to a set of around 100, but still the same concern... Of course, with 10 markers or less I don't have any problem.
Did you already face a similar trouble? How can I optimize Leaflet performances so that I can use an accurate zoom (beyond level 16) with more than 100 linked CircleMarkers ? I also wonder why performances are dropping so badly when zooming, while marker amount stay the same...
Thank you in advance for your answers,
Lenalys.
Cannot get the PolylineDecorator plugin to work on jsfiddle.
But here is the code that generate markers :
Map initialization :
var map;
function initializeMap(){
"use strict";
var layer;
var layer2;
function layerUrl(key, layer) {
return "http://wxs.ign.fr/" + key
+ "/geoportail/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&"
+ "LAYER=" + layer + "&STYLE=normal&TILEMATRIXSET=PM&"
+ "TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg";
}
layer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
{
attribution: '© OpenStreetMap contributors',
maxZoom: 18
});
layer2 = L.tileLayer(
layerUrl(IGN_AMBIENTE_KEY, "GEOGRAPHICALGRIDSYSTEMS.MAPS"),
{attribution: '© IGN'}
);
var baseMaps = {
"Terrestre": layer,
"Bathymetrique": layer2
};
map = L.map('map', {
layers: [layer],
zoom: 8,
center: [42.152796, 9.139150],
zoomControl: false
});
L.control.layers(baseMaps).addTo(map);
//add zoom control with your options
L.control.zoom({
position:'topright' //topleft
}).addTo(map);
L.control.scale({
position:'bottomleft',
imperial : false
}).addTo(map);
}
Data Sample :
var jsonData ={"12":[{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657283333333","longitude":"9.42362","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 15:37:12"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657381666667","longitude":"9.42365","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 16:42:16"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657381666667","longitude":"9.4236933333333","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 17:47:21"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657283333333","longitude":"9.4237383333333","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 19:57:23"}],
"13":[{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.61683","longitude":"9.4804633333333","temperature":"17.45","pulse":null,"battery":"80","date_time":"2015-04-08 07:45:20"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.538858333333","longitude":"9.48169","temperature":"14.37","pulse":null,"battery":"80","date_time":"2015-04-08 08:00:29"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.458748333333","longitude":"9.500225","temperature":"14.46","pulse":null,"battery":"80","date_time":"2015-04-08 08:15:49"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.3302","longitude":"9.5374583333333","temperature":"15.19","pulse":null,"battery":"80","date_time":"2015-04-08 08:31:05"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.170133333333","longitude":"9.5272116666667","temperature":"15.48","pulse":null,"battery":"80","date_time":"2015-04-08 08:46:20"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07959","longitude":"9.47688","temperature":"15.97","pulse":null,"battery":"80","date_time":"2015-04-08 09:01:31"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.076163333333","longitude":"9.4828633333333","temperature":"20.42","pulse":null,"battery":"80","date_time":"2015-04-08 09:16:59"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07194","longitude":"9.4908866666667","temperature":"17.36","pulse":null,"battery":"80","date_time":"2015-04-08 09:32:17"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.072583333333","longitude":"9.4901516666667","temperature":"17.36","pulse":null,"battery":"80","date_time":"2015-04-08 09:47:32"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07238","longitude":"9.4904266666667","temperature":"19.38","pulse":null,"battery":"80","date_time":"2015-04-08 10:02:42"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.072298333333","longitude":"9.4904983333333","temperature":"17.46","pulse":null,"battery":"80","date_time":"2015-04-08 10:17:55"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.095093333333","longitude":"9.5148383333333","temperature":"17.47","pulse":null,"battery":"80","date_time":"2015-04-08 10:33:12"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.112881666667","longitude":"9.5133133333333","temperature":"19.3","pulse":null,"battery":"80","date_time":"2015-04-08 10:48:23"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.112875","longitude":"9.513285","temperature":"22.71","pulse":null,"battery":"80","date_time":"2015-04-08 11:03:57"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.141096666667","longitude":"9.5078216666667","temperature":"23.73","pulse":null,"battery":"80","date_time":"2015-04-08 11:19:12"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.282186666667","longitude":"9.5505183333333","temperature":"18.97","pulse":null,"battery":"80","date_time":"2015-04-08 11:34:28"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.405126666667","longitude":"9.531145","temperature":"20.71","pulse":null,"battery":"80","date_time":"2015-04-08 11:49:42"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.482063333333","longitude":"9.480665","temperature":"21.7","pulse":null,"battery":"80","date_time":"2015-04-08 12:05:07"}]}
var oJSON = JSON.parse(jsonData);
var colors = [
"#400080",
"#008000",
"#EC7600",
"#E40341",
"#0D5E5E",
"#919191",
"#FF3C9D",
"#A70A0E",
"#00BFBF",
"#7171FF"
];
var classes = [
"color1",
"color2",
"color3",
"color4",
"color5",
"color6",
"color7",
"color8",
"color9",
"color10"
];
var lastMarkers = [];
var layers = new Array();
var polyline;
var decorator;
window.graphicsDevices = [];
var offsetLatitude = 0.003333;
var offsetLongitude = 0.011666;
Marker instanciation :
function printGPSOnMap(oJSON){
var nbKeys = 0;
for (var key in oJSON) {
nbKeys++;
var classe = classes[(key-1)%classes.length];
var color = colors[(key-1)%colors.length];
var positionInfo = [];
if (oJSON.hasOwnProperty(key)) {
var aInfo = oJSON[key];
var marker;
var latlngs = Array();
var startMarker = lastMarkers[key];
if(startMarker !== undefined && startMarker != null) {
var myIcon = L.divIcon({className: "myCircle "+classe, iconSize : [ 20, 20 ] });
startMarker.setIcon(myIcon);
latlngs.push(startMarker.getLatLng());
}
for(var i = 0; i < aInfo.length; i++) {
var oInfos = aInfo[i];
var sIdIndividual = oInfos["id_individual"];
var sLongitude = oInfos["longitude"];
var sLatitude = oInfos["latitude"];
var sTemperature = oInfos["temperature"];
var sPulse = oInfos["pulse"];
var sBattery = oInfos["battery"];
var sDatetime = oInfos["date_time"];
var sIndividualName = oInfos["individual_name"];
var id_device = oInfos["id_stm_device"];
var popupMsg = "...";
latlngs.push(L.marker([sLatitude,sLongitude]).getLatLng());
marker = new MyCustomMarker([sLatitude,sLongitude], {
icon : L.divIcon({
className : "myCircle "+classe + ((i == aInfo.length-1) ? ' myCircleEnd' : ''),
iconSize : [ 20, 20 ]
})
});
marker.bindPopup(popupMsg, {
showOnMouseOver: true
});
marker.bindLabel(key, {
noHide: true,
direction: 'middle',
offset: [offset[0], offset[1]]
});
positionInfo.push(marker);
}
lastMarkers[key] = marker;
}
if(latlngs.length > 1)
{
polyline = L.polyline(latlngs, {className: classe, weight: 2,opacity: 0.4}).addTo(map);
decorator = L.polylineDecorator(polyline, {
patterns: [
// define a pattern of 10px-wide arrows, repeated every 20px on the line
{offset: 0, repeat: '25px', symbol: new L.Symbol.arrowHead({pixelSize: 10, pathOptions: {fillOpacity:
0.76, color: color, weight: 1}})}
]}).addTo(map);
}
if(!window.graphicsDevices.hasOwnProperty(key))
window.graphicsDevices[key] = [];
for(var i = 0; i < positionInfo.length; i++) {
window.graphicsDevices[key].push(positionInfo[i]);
positionInfo[i].addTo(map);
if(latlngs.length > 1){
window.graphicsDevices[key].push(polyline);
polyline.addTo(map);
window.graphicsDevices[key].push(decorator);
decorator.addTo(map);
}
}
}//foreach key
}
Code for the custom marker :
var MyCustomMarker = L.Marker.extend({
bindPopup: function(htmlContent, options) {
if (options && options.showOnMouseOver) {
// call the super method
L.Marker.prototype.bindPopup.apply(this, [htmlContent, options]);
// unbind the click event
this.off("click", this.openPopup, this);
// bind to mouse over
this.on("mouseover", function(e) {
// get the element that the mouse hovered onto
var target = e.originalEvent.fromElement || e.originalEvent.relatedTarget;
var parent = this._getParent(target, "leaflet-popup");
// check to see if the element is a popup, and if it is this marker's popup
if (parent == this._popup._container)
return true;
// show the popup
this.openPopup();
}, this);
// and mouse out
this.on("mouseout", function(e) {
// get the element that the mouse hovered onto
var target = e.originalEvent.toElement || e.originalEvent.relatedTarget;
// check to see if the element is a popup
if (this._getParent(target, "leaflet-popup")) {
L.DomEvent.on(this._popup._container, "mouseout", this._popupMouseOut, this);
return true;
}
// hide the popup
this.closePopup();
}, this);
}
},
_popupMouseOut: function(e) {
// detach the event
L.DomEvent.off(this._popup, "mouseout", this._popupMouseOut, this);
// get the element that the mouse hovered onto
var target = e.toElement || e.relatedTarget;
// check to see if the element is a popup
if (this._getParent(target, "leaflet-popup"))
return true;
// check to see if the marker was hovered back onto
if (target == this._icon)
return true;
// hide the popup
this.closePopup();
},
_getParent: function(element, className) {
var parent = null;
if(element != null) parent = element.parentNode;
while (parent != null) {
if (parent.className && L.DomUtil.hasClass(parent, className))
return parent;
parent = parent.parentNode;
}
return false;
}
});
Have you gauged performance in canvas mode?
Use L_PREFER_CANVAS = true before initializing your leaflet map container.Might be able to help you possibly.

AmpersandJS: How to transform model data before displaying in a view

In an AmpersandJS view, is there a convention for transforming data from a model? For example, I'd like to be able to format a user.joined date within the view before it's displayed. I'd rather not have to do this in the model.
Maybe something like:
var View = require('ampersand-view');
var formatDate = require('../helpers/format-date');
module.exports = View.extend({
bindings: {
'model.joined': {
hook: 'joined',
transform: function(date) { return formatDate(date) }
}
}
});
You are not limited to model when using bindings. You can also bind to properties on the view and since AmpersandView extends AmpersandState these properties may also be derived, like:
var View = require('ampersand-view');
var formatDate = require('../helpers/format-date');
module.exports = View.extend({
derived: {
formattedJoinedDate: {
deps: ['model.joined'],
fn: function() { return formatDate(this.model.joined) }
}
}
bindings: {
'this.formattedJoinedDate': {
hook: 'joined'
}
}
});
Docs that cover bindings to local properties
Don't forget that View extends State, so you can setup properties in your view definition to bind to. Inside your View.extend, you can instantiate things in the props hash, just like you would with ampersand-model or ampersand-state, and then setup bindings on them!
var View = require('ampersand-view');
var formatDate = require('../helpers/format-date');
module.exports = View.extend({
props: { someDate: 'date' },
bindings: {
someDate: {
hook: 'joined',
transform: function(date) { return formatDate(date) }
}
},
initialize: function() { this.someDate = new Date(); }
});

How can I automatically map a json object to fields based off a viewmodel mapped to fields?

I have a view that is loaded with a blank viewmodel initially. I want to populate that already rendered view with a json object (obtained view ajax post) that was based off the viewmodel for that view.
Is there a way of automatically doing this?
Is there a way of doing it in reverse? (fields to matching viewmodel json object)
The only way I am aware of taking data return from an ajax call and putting it in a field is manually
$('#TextField1').val(result.TextField1);
etc..
to send it back to the controller you can do
data: $('form').serialize(),
this will take all of the fields in that form and send them back to the controller
Ok it looks like this will suit my needs.
I need to follow a convention of naming containers the same name as their respective properties as well as putting a class on them to indicate that they contain subfields.
function MapJsonObjectToForm(obj, $container) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var $field = $container.find('#' + key);
if ($field.is('div')) {
MapJsonObjectToForm(obj[key], $field);
} else {
if (obj[key] == null) {
if ($field.hasClass('select2-offscreen')) {
$field.select2('val', '');
$field.select2().trigger('change');
} else {
$field.val("");
}
} else {
if ($field.hasClass('select2-offscreen')) {
$field.select2('val', obj[key]);
$field.select2().trigger('change');
} else {
$field.val(obj[key]);
}
}
}
}
}
}
function MapFormToJsonObject(containerid) {
var obj = {};
$('.dataitem').each(function () {
var exclude = "s2id";
if ($(this).attr("ID").substring(0, exclude.length) !== exclude) {
var parents = $(this).parents(".has-sub-fields");
if (parents.length > 0) {
obj = FindParents(obj, parents.get(), $(this).attr("ID"), $(this).val());
} else {
obj[$(this).attr("ID")] = $(this).val();
}
}
});
return obj;
}
function FindParents(obj, arr, id, value) {
if (arr.length == 0) {
obj[id] = value;
return obj;
}
var parentID = $(arr[arr.length - 1]).attr("ID");
arr.pop();
if (obj[parentID] == null) {
obj[parentID] = {};
}
obj[parentID] = FindParents(obj[parentID], arr, id, value);
return obj;
}

Resources