polygon-website-foss/js/3D/ThreeDeeThumbnails.js

258 lines
9.2 KiB
JavaScript

/*
Usage:
<div class="thumbnail-holder">
<span class="thumbnail-span" data-3d-url="urlIsHere">
<- Canvas will be added here
<img>2D Image</img>
</span>
</div>
$("element").load3DThumbnail(function(canvas) {
//complete!
});
The element being called must have a data-3d-url attribute
While it's loading the OBJ & MTL files, it creates a CSS loading spinner inside the new div.
*/
(function () {
// constants
var fieldOfView = 70;
var minRenderDistance = 0.1;
var maxRenderDistance = 1000;
var loadingWaitBeforeShowingSpinner = 500; // milliseconds - for UX, don't show the spinner immediately
var maximumPollsToGet3DThumbnailJson = 10;
var retryInterval = 2 * 1000;
//WebGLRenderer can be reused by multiple scenes
var renderer;
//hack because IE8 doesn't like that three.min.js uses getters and setters.
//Adding these scripts in the PageScript would break the whole page.
function getCachedScript(url) {
// Use $.ajax() since it is more flexible than $.getScript
// Return the jqXHR object so we can chain callbacks
return $.ajax({
dataType: "script",
cache: true,
url: url
});
};
function getScripts(inserts, callback) {
var nextInsert = inserts.shift();
if (nextInsert != undefined) {
getCachedScript(nextInsert).done(function () {
getScripts(inserts, callback);
});
}
else {
if (callback != undefined)
callback();
}
};
var scriptsLoaded = false;
function loadScriptsIfNeeded(callback, scriptsToLoadCsv) {
if (scriptsLoaded) {
callback();
}
else {
// Bcause IE8 doesn't like that three.min.js uses getters and setters.
// Adding these scripts in the PageScript would break the whole page.
var scriptsToLoad = scriptsToLoadCsv.split(",");
getScripts(scriptsToLoad, function () {
scriptsLoaded = true;
callback();
});
}
}
function addLightsToScene(scene) {
//TODO get these values in the thumbnail JSON from obj exporter
var ambient = new THREE.AmbientLight(0x878780);
scene.add(ambient);
var sunLight = new THREE.DirectionalLight(0xACACAC);
sunLight.position.set(-0.671597898, 0.671597898, 0.312909544).normalize();
scene.add(sunLight);
var backLight = new THREE.DirectionalLight(0x444444);
var backLightPos = new THREE.Vector3().copy(sunLight.position).negate().normalize(); //inverse of sun direction
backLight.position.set(backLightPos);
scene.add(backLight);
}
function clearContainer(container) {
container.find(".thumbnail-spinner").remove();
container.find("canvas").remove();
}
$.fn.load3DThumbnail = function (callback, onError) {
var cancelled = false;
function cancelLoading() {
cancelled = true;
}
function getThumbnailJson(url, callbackAfterJsonRetrieved) {
var retries = 0;
$.get(url, function (data) {
if (data.Final) {
$.getJSON(data.Url, function (json) {
callbackAfterJsonRetrieved(json);
})
.fail(function () {
onError("3D Thumbnail failed to load");
});
}
else {
if (retries < maximumPollsToGet3DThumbnailJson && (cancelled == false)) {
setTimeout(function () {
getThumbnailJson(url, callbackAfterJsonRetrieved);
}, retryInterval);
retries += 1;
}
}
});
}
function loadObjAndMtl(modelHash, mtlHash, container, json, callbackAfterLoaderIsDone) {
var baseUri = "/thumbnail/resolve-hash/"; //server should provide
var containerHeight = container.width(); //container.height();
var containerWidth = container.width();
var camera = new THREE.PerspectiveCamera(fieldOfView, containerWidth / containerHeight, minRenderDistance, maxRenderDistance);
var scene = new THREE.Scene();
var controls;
function render() {
renderer.render(scene, camera);
}
function animate() {
if (controls.enabled) {
controls.update();
}
TWEEN.update();
render();
requestAnimationFrame(animate);
}
function createCanvas() {
renderer.setSize(containerWidth, containerHeight);
var canvas = renderer.domElement;
container.resize(function () {
camera.aspect = container.width() / container.height();
camera.updateProjectionMatrix();
renderer.setSize(container.width(), container.height());
controls.handleResize();
});
window.onbeforeunload = function () {
// canvas goes black when navigating to another page
$(canvas).hide();
$(container).find("img").show();
}
return canvas;
}
function initializeControls() {
// The controller that lets us spin the camera around an object
var orbitControls = new THREE.OrbitControls(camera, container.get(0), json);
orbitControls.rotateSpeed = 1.5;
orbitControls.zoomSpeed = 1.5;
orbitControls.dynamicDampingFactor = 0.3;
orbitControls.addEventListener("change", render);
return orbitControls;
}
function objAndMtlLoaded(modelObject) {
addLightsToScene(scene);
scene.add(modelObject);
var canvas = createCanvas();
controls = initializeControls();
render();
animate();
callbackAfterLoaderIsDone(canvas);
};
// ReSharper disable once InconsistentNaming
var loader = new THREE.OBJMTLLoader();
loader.load(baseUri, modelHash, mtlHash, objAndMtlLoaded, undefined, onError);
}
this.each(function () {
try {
var container = $(this);
// var scriptsCsv = container.data("js-files");
var loadThreeDee = function () {
//Renderer can be reused by multiple scenes
if (!renderer) {
// ReSharper disable once InconsistentNaming
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
}
var jsonUrl = container.data("3d-url");
clearContainer(container);
var placeholder = $("<div class='thumbnail-spinner'></div>");
placeholder.appendTo(container);
var loaderVisible = false;
function startLoading() {
loaderVisible = true;
placeholder.height(container.width());
placeholder.show();
placeholder.empty();
setTimeout(function () {
if (loaderVisible) {
placeholder.addClass("text-center");
// placeholder.html("<div class='loader' style='line-height:" + container.height().toString() + "px'>Loading</div><div></div>");
placeholder.html("<div class='loader' style='line-height:" + container.width().toString() + "px'>Loading</div><div></div>");
}
}, loadingWaitBeforeShowingSpinner);
}
function endLoading() {
loaderVisible = false;
placeholder.hide();
placeholder.empty();
placeholder.removeClass("text-center");
}
startLoading();
function updateContainer(json) {
loadObjAndMtl(json.obj, json.mtl, container, json, function (canvas) {
clearContainer(container);
if (cancelled == false) {
endLoading();
container.append(canvas);
callback(canvas);
}
});
};
getThumbnailJson(jsonUrl, updateContainer);
};
// loadScriptsIfNeeded(loadThreeDee, scriptsCsv);
loadThreeDee();
}
catch (e) {
onError(e);
}
});
return {
cancel: cancelLoading
}
}
})();