Need help loading an OBJ and MTL file in Three.js - javascript

I'm experiencing an odd issue while trying to load an OBJ and MTL file in Three.js using it's OBJMTLLoader() function and just can't seem to figure it out, or find a solution on Google. I have my index.html file inside of a folder, and also in that same folder is my js folder, which includes all external js files that I wrote for my project along with the three.js r68 library. Also in my root project folder is a folder named 'obj', which includes all of my obj files and their corresponding mtl files.
I initially started my project under a Windows system and was able to load everything just fine, no problem at all, but now I switched to my personal Linux system and I started seeing this problem and am unable to load any of my obj/mtl files any longer.
The error I am receiving is 'Uncaught TypeError: undefined is not a function' and I've tried printing out my 'loader' variable and it simply shows me in the console that it's an object of type OBJMTLLoader. Any thoughts on this?
EDIT: The first file is js/main.js, and the next one is index.html.
const WIDTH = 1200;
const HEIGHT = 700;
const VIEW_ANGLE = 45;
const ASPECT_RATIO = WIDTH / HEIGHT;
const NEAR = 0.1;
const FAR = 10000;
var renderer,
camera,
scene;
var pointLight, pointLight2;
var xrot = 0.0025,
yrot = 0.0025;
$(document).ready(function() {
// setup renderer, camera, perspective, etc.
initScene();
$('#loadfile').change(getLoadedFile);
$('input[type=range]').eq(0).change(showYRot);
// lights
pointLight = new THREE.PointLight(0xffffff);
pointLight.position.x = -10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
pointLight2 = new THREE.PointLight(0xffffff);
pointLight2.position.x = 0;
pointLight2.position.y = 50;
pointLight2.position.z = 500;
scene.add(pointLight2);
var render = function() {
window.requestAnimationFrame(render);
$(scene.children).each(function() {
if (this !== camera && this !== pointLight) {
this.rotation.x = xrot;
this.rotation.y += yrot;
}
});
renderer.render(scene, camera);
};
render();
});
function initScene() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT_RATIO, NEAR, FAR);
scene = new THREE.Scene();
scene.add(camera);
camera.position.z = 250;
renderer.setSize(WIDTH, HEIGHT);
$('div#container').append(renderer.domElement);
}
function getLoadedFile(evt) {
var fileList = evt.target.files;
var filename = fileList[0].name;
if (filename.substring(filename.length - 4, filename.length) == '.obj') {
var obj = filename;
var mtl = filename.substring(0, filename.length - 4) + '.mtl';
console.log('adding ' + filename + ' to scene.');
loadOBJMTLModel(obj, mtl);
}
}
function loadOBJMTLModel(obj, mtl) {
var loader = new THREE.OBJMTLLoader();
loader.load('obj/' + obj, 'obj/' + mtl, function(object) {
scene.add(object);
});
}
<script src="https://threejs.org/examples/js/loaders/OBJMTLLoader.js"></script>
<script src="https://threejs.org/examples/js/loaders/OBJLoader.js"></script>
<script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OBJ Model Previewer</title>
<style>
body {
background-color: lightgrey;
}
div#container {
width: 1200px;
height: 700px;
border: 1px solid grey;
}
</style>
<!-- <script src="js/main.js"></script> //-->
<body>
<h3>Choose file to load</h3>
<input id="loadfile" type="file" />
<br />
<div id="container"></div>
<br />
<span></span><br />
<span></span><br />
<br />
<span>Y Rotation: </span><input type="range" min="0.0" max="0.1" step="0.001" />
<span id="yrot"></span>
</body>
</html>

As requested.
OBJMTLLoader is deprecated. you must use MTLLoader and OBJLoader together. You can view a traditional example here: view-source:threejs.org/examples/webgl_loader_obj_mtl.html
There have also been numerous updates to objloader2.js available # https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/OBJLoader2.js
For a little more digging, see my previous answer for automated MTL loading here: Is there a way to load a mtl using the path in the obj file?

Related

Three.js canvas not appearing to render, no errors

I've been wanting to use Three.js in a project but I can't get anything to render on my screen.
After a while of troubleshooting, I thought I would just ask and see if anyone can help me.
DETAILS:
Hosting on XAMPP,
here's a complete Three.js example that should work, to my knowledge, but evidently does not:
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="c"></canvas>
<script type="module">
import {
Camera,
Material,
Texture,
} from "https://cdn.skypack.dev/three#0.132.2";
import { OrbitControls } from "https://cdn.skypack.dev/three#0.132.2/examples/jsm/controls/OrbitControls.js";
function main() {
const canvas = document.getElementById('c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
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();
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const material = new THREE.MeshBasicMaterial({color: 0x44aa88});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
}
</script>
</body>
</html>
I've linked through a cdn here, but other methods such as installing with npm and linking that way have also had the same confounding results.
In any case it's not throwing any errors, so I'm at a loss.
All I end up getting is a blank white page.
The function main() is not gonna call itself. When you declare a function, you have to make sure you execute it, otherwise it'll just sit there, unused.
// First we declare the function
function main() {
// Do a bunch of stuff
}
// Then we call the function
main();

Three.js "Context Lost" and "Multiple instances being imported"?

I'm trying to create a 3D editor, where the user can edit a 3D scene, and then hit a "play" button and see the result. To render the result, I'm using an iframe. Here is my HTML code:
<iframe id="testing_frame" class="ui"></iframe>
The class ui is just position: absolute; top: 0;. I don't want to have a URL or FILE as the src to this iframe, instead I want to write directly to it. Here is how I do it:
generatedCode += `
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"><\/script>
</head>
<body style="margin: 0;">
<script async>"use strict"
function init(){
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 1000);
var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({
color: 0xff0000
}));
scene.add(mesh);
camera.position.z = -5;
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
window.onload = init;
<\/script>
</body>
</html>`;
That code is stored in the variable generatedCode, which is what I will write to the iframe, here:
var iframe = document.getElementById("testing_frame");
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
iframeDocument.open();
iframeDocument.write(generatedCode);
iframeDocument.close();
This works fine.
My Problem: I have a start/stop button, which runs this code everytime it is clicked. Each time I hit stop, it says WARNING: Multiple instances of Three.js being imported., and if I start and stop the testing iframe around 10 times, it says THREE.WebGLRenderer: Context Lost.
Here I have a video demonstrating my problem. (Don't worry about the things in the console before I start doing anything)
Thanks!
Edit: Here is the start/stop code:
<button id='play_btn' onClick='test_iframe();';>Play</button>
This is the button, below is the test_iframe() function:
function test_iframe() {
let code = generateCodeFromProjectData();
var iframe = document.getElementById("testing_frame");
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
//Write to iframe
iframeDocument.open();
iframeDocument.write(code);
iframeDocument.close();
//If they press play, change it to stop and show the iframe.
if(document.getElementById("play_btn").innerHTML == "Play"){
document.getElementById("testing_frame").style.display = "block";
document.getElementById("play_btn").innerHTML = "Stop";
} else { //If they press stop, hide the iframe and change it to play.
document.getElementById("testing_frame").style.display = "none";
document.getElementById("play_btn").innerHTML = "Play";
}
}
Finally, here is the generateCodeFromProjectData() function, which receives the code:
function generateCodeFromProjectData(){
generatedCode = "";
//Opening
generatedCode += `
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"><\/script>
</head>
<body style="margin: 0;">
<script async>"use strict"
function init(){
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 1000);
var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({
color: 0xff0000
}));
scene.add(mesh);
camera.position.z = -5;
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
window.onload = init;
<\/script>
</body>
</html>`;
return generatedCode;
}
Edit 2: Here's my new code for the `iframe`
generatedCode += `
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"><\/script>
</head>
<body style="margin: 0;">
<script>
function init(){
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 1000);
var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({
color: 0xff0000
}));
scene.add(mesh);
camera.position.z = -5;
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
function destroy() {
scene = null;
camera = null;
mesh = null;
renderer.dispose()
}
return {destroy};
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
window.onload = () => {
var instance = init();
window.onbeforeunload = () => {
instance.destroy();
}
}
<\/script>
</body>
</html>`;
Now I get no error for Context Lost, but the red cube in the center does not show up.
It seems like a memory leak to me. Anything happened inside <iframe /> stays in the memory unless it is deallocated manually or the tab(window) is closed. I've experienced exact same problem when using storybook with different frontend libraries; storybook also uses <iframe /> as an isolated demonstration, but it always needed manual deallocation.
Manual memory deallocation, or dispose() function is necessary in Three.JS as well. Here's an excerpt from the official Three.js document:
Why can't three.js dispose objects automatically?
This question was asked many times by the community so it's important to clarify this matter. Fact is that three.js does not know the lifetime or scope of user-created entities like geometries or materials. This is the responsibility of the application. For example even if a material is currently not used for rendering, it might be necessary for the next frame. So if the application decides that a certain object can be deleted, it has to notify the engine via calling the respective dispose() method.
It says the dispose is necessary for Three.Object3D and says nothing about WebGLRender. Interestingly enough, the dispose() function is listed as WebGLRenderer's spec.
https://threejs.org/docs/?q=renderer#api/en/renderers/WebGLRenderer
to sync the dispose action to iframes, use window.onbeforeunload event handler or add dispose function to your page move action.
// add this code to your iframe code.
function init () {
var scene = // ...
var camera = // ...
var mesh = // ...
var renderer = // ...
/* ... */
function destroy() {
scene = null;
camera = null;
mesh = null;
renderer.dispose()
}
return {destroy};
}
window.onload = () => {
var instance = init();
window.onbeforeunload = () => {
instance.destroy();
}
}
edit: the answer only solves the context lost issue. It has no problem in terms of destroying the instance. Multiple instances being imported should be regarded as another problem. Anything that's added inside <iframe/> is considered in the same scope as its parent. adding <script src="..." /> inside iframe again and again causes multiple instances being imported. So to solve this issue, code must be provided from its parent.
generatedCode should not include the <script />
code should be provided in the same scope as the iframe controller code, so that it can sync the lifecycle of iframe and three.js renderer.
it's little bit more complex than disposing. here's the working code and working demo at codepen
<script src="your three.js url"></script>
<body>
<div id="root">
</div>
<button id='play_btn' onClick='test_iframe()'>Play</button>
</body>
// multiple instances being imported & context lost issue solved
function init(width, height) {
console.log("init", width, height);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(90, width / height, 0.1, 1000);
var mesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({
color: 0xff0000
})
);
scene.add(mesh);
camera.position.z = 5;
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
function destroy() {
console.log("destroy");
scene = null;
camera = null;
mesh = null;
renderer.dispose();
}
function animate() {
if (!renderer) return;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
return { destroy, domElement: renderer.domElement };
}
let iframe;
const getIframe = () => {
const code = generateCodeFromProjectData();
const root = document.getElementById("root");
const iframe = document.createElement("iframe");
iframe.setAttribute("id", "testing_frame");
iframe.setAttribute("class", "ui");
iframe.style.width = "300px";
iframe.style.height = "300px";
root.appendChild(iframe);
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
iframeDoc.open();
iframeDoc.write(code);
iframeDoc.close();
const body = iframeDoc.querySelector("body");
const instance = init(body.clientWidth, body.clientHeight);
body.appendChild(instance.domElement);
function destroy() {
root.removeChild(iframe);
instance.destroy();
}
return { iframe, iframeDoc, destroy };
};
function test_iframe() {
if (!iframe) {
// init
console.log("init");
iframe = getIframe();
document.getElementById("play_btn").innerHTML = "Stop";
return;
}
console.log("dispose");
// disposing
document.getElementById("play_btn").innerHTML = "Play";
iframe.destroy();
iframe = null;
}
function generateCodeFromProjectData() {
generatedCode = "";
//Opening
generatedCode += `
<!DOCTYPE html>
<html>
<body style="margin: 0;width: 300px;height:300px;">
</body>
</html>`;
return generatedCode;
}

Replace 3d model (json)

I found an example online and in this example that shows a 3D model (a Ferris wheel) placed on a parking lot with a Google Street View Panorama as the background.
Now I want to replace the Ferris wheel with another 3d object — an asteroid — that I download as a 3DS and OBJ. file. I converted the 3DS file to JSON through Blender but when I replace the file it just shows a blank screen.
How can I do it? Here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Street View Overlay - EINA</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
overflow: hidden;
}
p {
font-family:sans-serif;
font-size:11px;
font-weight:bold;
color:#111111;
}
</style>
</head>
<body>
<div id="streetviewpano" style="position: absolute; top:0; bottom:0; left:0; right:0; z-index: 0">
</div>
<div id="container" style="position: absolute; top:0; bottom:100px; left: 0; right: 0; z-index: 100;">
</div>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
<script src="lib/jquery-2.0.3.js"></script>
<script src="lib/jquery.mousewheel.js"></script>
<script src="lib/three.min.js"></script>
<script src="lib/Detector.js"></script>
<script src="src/streetviewoverlay.js"></script>
<script>
var METERS2DEGREES = 0.0000125;
var objectPosition = [48.804828,2.1203071];
function hackMapProjection(lat, lon, originLat, originLon) {
var lonCorrection = 1.5;
var rMajor = 6378137.0;
function lonToX(lon) {
return rMajor * (lon * Math.PI / 180);
}
function latToY(lat) {
if (lat === 0) {
return 0;
} else {
return rMajor * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI / 180) / 2));
}
}
var x = lonToX(lon - originLon) / lonCorrection;
var y = latToY(lat - originLat);
return {'x': x, 'y': y};
}
function latLon2ThreeMeters(lat, lon) {
var xy = hackMapProjection(lat, lon, objectPosition[0], objectPosition[1]);
return {'x': xy.x, 'y': 0, 'z': -xy.y};
}
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load( "model3d/wheel.js", loadWheel );
function loadWheel(geometry, materials) {
var mesh;
mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
meshPos = latLon2ThreeMeters(objectPosition[0], objectPosition[1]);
mesh.geometry.computeBoundingBox();
var xsize = mesh.geometry.boundingBox.max.x - mesh.geometry.boundingBox.min.x;
var ysize = mesh.geometry.boundingBox.max.y - mesh.geometry.boundingBox.min.y;
var zsize = mesh.geometry.boundingBox.max.z - mesh.geometry.boundingBox.min.z;
var desiredXSize = 10;
var desiredYSize = 10;
var desiredZSize = 10;
mesh.scale.x = desiredXSize / xsize;
mesh.scale.y = desiredYSize / ysize;
mesh.scale.z = desiredZSize / zsize;
mesh.position.x = meshPos.x;
mesh.position.y = meshPos.y - 2; // the parking lot is sligthly under the street level
mesh.position.z = meshPos.z;
mesh.rotation.y = Math.PI/2;
mesh.castShadow = true;
var streetViewOverlay = StreetViewOverlay();
streetViewOverlay.load({streetView: true, objects3D: true, webGL:true}, mesh,48.8049084,2.120357);
}
</script>
</body>
</html>
So here's the thing.. you can covert to three.json format, and that's fine, but the version of your conversion tools have to match the version of three you're using.. you also want to be on close to the most recent three.js version..
but a large issue is that the three.json format is still somewhat in flux between versions, so I have had situations in the past where previously exported models go stale.
If I we're you .. I would use the OBJLoader and load your OBJ directly, or use a format like GLTF or Collada, and their respective loaders. Those formats are less likely to age. Especially GTLF looks like its heading toward being widely supported and has a lot of nice features that make it very efficient for realtime 3d.
r.e. your specific problem: First I would ask if you see any errors in the console from when you load your model. Second, I would put a debug breakpoint on the line inside the loader, and visually inspect the scene and its .children,
OR log it in the code with a scene.traverse((elem)=>{console.log(elem.type,elem);});
If that doesn't show at least one "Mesh" it means your conversion may have had problems.. if it does show, it could mean the mesh is too tiny to see, too Huge to see, or maybe the material is accidently transparent or many other possibilities. At that point, you can look at the mesh.geometry and make sure it has vertices and elements within it...
Failing all that:
If you try with OBJLoader or some other loader, and still can't get it to work.. check back with us. :)

JavaScript runtime error: Unable to get property 'geometries' of undefined or null reference when importing 3D models into a three.js viewport

I am trying to import my own 3D models into a three.js viewport but I keep getting this error:
0x800a138f - JavaScript runtime error: Unable to get property
'geometries' of undefined or null reference
My code:
<html>
<head>
<title>My first Three.js app</title>
<style>
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script>
// Our Javascript will go here.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var json = THREE.JSONLoader("teapot.js"); // I Think this loads the model
var loader = new THREE.JSONLoader();
var result = loader.parse(json, texturePath);
var mesh = new THREE.Mesh(result.geometry, result.materials);
scene.add(mesh);
camera.position.z = 5;
function render() {
requestAnimationFrame(render);
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
You're loading it wrong, when using the loader, first you declare the loader as such:
var loader = new THREE.JSONLoader();
Then you tell the loader to load your model, since this takes a while, you attach a function to handle adding it to the scene AFTER the loading is done
loader.load('path_to_model', function (geometry) {
var material = new THREE.MeshLambertMaterial({
//whatever material properties you want to use
});
// create a mesh with models geometry and material
var mesh = new THREE.Mesh(
geometry,
material
);
scene.add(mesh);
});
That should do the trick

Three js embed html

Hy!
I'm working with three js(webgl). I was trying to embed html webpage, but somehow it don't wants to work.
Tutorials that I was checking:
http://learningthreejs.com/blog/2013/04/30/closing-the-gap-between-html-and-webgl/
http://stemkoski.github.io/Three.js/CSS3D.html
The stemkoski one was working with three js v58, also v60. But it don't realy wants to work using v68(newest version). I don't get any error message, just a black screen where the website should be.
Also I'm able to do something similiar like http://threejs.org/examples/#css3d_youtube .
My question is is there a workaround, or "hot fix" for this problem because I'm out of ideas.
Thanks for your answers.
---UPDATE---
Then let's say I'm using this code:
<!doctype html>
<html lang="en">
<head>
<title>CSS3D (Three.js)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel=stylesheet href="css/base.css"/>
</head>
<body>
<script src="js/Three58.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.WindowResize.js"></script>
<!-- new for this example -->
<script src="js/CSS3DRenderer.js"></script>
<!-- jQuery code to display an information button and box when clicked. -->
<script src="js/jquery-1.9.1.js"></script>
<script src="js/jquery-ui.js"></script>
<link rel=stylesheet href="css/jquery-ui.css" />
<link rel=stylesheet href="css/info.css"/>
<script src="js/info.js"></script>
<div id="infoButton"></div>
<div id="infoBox" title="Demo Information">
This three.js demo is part of a collection at
http://stemkoski.github.io/Three.js/
</div>
<!-- ------------------------------------------------------------ -->
<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
<script>
/*
Three.js "tutorials by example"
Author: Lee Stemkoski
Date: July 2013 (three.js v58)
This demo is based on the work of Jerome Etienne:
http://learningthreejs.com/blog/2013/04/30/closing-the-gap-between-html-and-webgl/
*/
// MAIN
// standard global variables
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
// custom global variables
var rendererCSS;
init();
animate();
// FUNCTIONS
function init()
{
// SCENE
scene = new THREE.Scene();
// CAMERA
var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0,150,400);
camera.lookAt(scene.position);
// RENDERER
if ( Detector.webgl )
renderer = new THREE.WebGLRenderer( {antialias:true} );
else
renderer = new THREE.CanvasRenderer();
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
container = document.getElementById( 'ThreeJS' );
container.appendChild( renderer.domElement );
// EVENTS
THREEx.WindowResize(renderer, camera);
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
// CONTROLS
controls = new THREE.OrbitControls( camera, renderer.domElement );
// STATS
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// LIGHT
var light = new THREE.PointLight(0xffffff);
light.position.set(0,250,0);
scene.add(light);
// FLOOR
var floorTexture = new THREE.ImageUtils.loadTexture( 'images/checkerboard.jpg' );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 10, 10 );
var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.y = -0.5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
////////////
// CUSTOM //
////////////
var planeMaterial = new THREE.MeshBasicMaterial({color: 0x000000, opacity: 0.1, side: THREE.DoubleSide });
var planeWidth = 360;
var planeHeight = 120;
var planeGeometry = new THREE.PlaneGeometry( planeWidth, planeHeight );
var planeMesh= new THREE.Mesh( planeGeometry, planeMaterial );
planeMesh.position.y += planeHeight/2;
// add it to the standard (WebGL) scene
scene.add(planeMesh);
// create a new scene to hold CSS
cssScene = new THREE.Scene();
// create the iframe to contain webpage
var element = document.createElement('iframe')
// webpage to be loaded into iframe
element.src = "index.html";
// width of iframe in pixels
var elementWidth = 1024;
// force iframe to have same relative dimensions as planeGeometry
var aspectRatio = planeHeight / planeWidth;
var elementHeight = elementWidth * aspectRatio;
element.style.width = elementWidth + "px";
element.style.height = elementHeight + "px";
// create a CSS3DObject to display element
var cssObject = new THREE.CSS3DObject( element );
// synchronize cssObject position/rotation with planeMesh position/rotation
cssObject.position = planeMesh.position;
cssObject.rotation = planeMesh.rotation;
// resize cssObject to same size as planeMesh (plus a border)
var percentBorder = 0.05;
cssObject.scale.x /= (1 + percentBorder) * (elementWidth / planeWidth);
cssObject.scale.y /= (1 + percentBorder) * (elementWidth / planeWidth);
cssScene.add(cssObject);
// create a renderer for CSS
rendererCSS = new THREE.CSS3DRenderer();
rendererCSS.setSize( window.innerWidth, window.innerHeight );
rendererCSS.domElement.style.position = 'absolute';
rendererCSS.domElement.style.top = 0;
rendererCSS.domElement.style.margin = 0;
rendererCSS.domElement.style.padding = 0;
document.body.appendChild( rendererCSS.domElement );
// when window resizes, also resize this renderer
THREEx.WindowResize(rendererCSS, camera);
renderer.domElement.style.position = 'absolute';
renderer.domElement.style.top = 0;
// make sure original renderer appears on top of CSS renderer
renderer.domElement.style.zIndex = 1;
rendererCSS.domElement.appendChild( renderer.domElement );
}
function animate()
{
requestAnimationFrame( animate );
render();
update();
}
function update()
{
if ( keyboard.pressed("z") )
{
// do something
}
controls.update();
stats.update();
}
function render()
{
// remember to call both renderers!
rendererCSS.render( cssScene, camera );
renderer.render( scene, camera );
}
</script>
</body>
</html>
As I mentioned above, this is the code from http://stemkoski.github.io/Three.js/CSS3D.html, and he made his code from another tutorial: http://learningthreejs.com/blog/2013/04/30/closing-the-gap-between-html-and-webgl/ this one.
The problem is, theese are working on three js r58-r60(Let's say theese are old codes). I want to do the same thing with three js r68(the newest version). But somehow, when I launch the same code on r68 that screen goes black instead of showing a html page. This is a known bug, you can check it http://learningthreejs.com/blog/2013/04/30/closing-the-gap-between-html-and-webgl/ here:
Hi Jerome, thanks for your interest. Here are a couple of jsfiddles
copied from stemkoski's github showing the results of Three r58 and
r68. Observe that the only difference between the samples is the
library in use: Threejs 58 (working) - http://jsfiddle.net/jL48v/2/
Threejs 68 (broken) - http://jsfiddle.net/jL48v/3/
I've tried several approaches to get this technique working in r68,
but without much luck so far.
Just realized that I messed up the dependency load order. Here's a
"fixed" broken version of r68 - http://jsfiddle.net/jL48v/4/
But he/she forgot to write the solution. and the v4 not working either.

Categories