Here is a reproducible example, I have in my js file just the THREE import and after that the CanvasRenderer code as taken from here:
import * as THREE from 'three';
import './CanvasRenderer'; // or Projector
The error in the console is
Uncaught TypeError: Cannot set property d of #<Object> which has only a getter.
which happens when you assign the function on this line
THREE.CanvasRenderer = function ( parameters ) {
Basically says that THREE.CanvasRenderer
already exists and is not configurable, and indeed it does exist as a deprecation warning in Three.Legacy.js.
The same happens for Projector. As a bundler I use [email protected].
Sorry I wouldn't know what to export instead of that function in order to bypass this error.
Hmm, yeah... we still don't know how to modularise the stuff in the examples folder without breaking traditional workflows...
What do you mean modularise stuff? Do you need any help?
@marcofugaro This thread might provide some context about the modules in examples issue: https://github.com/mrdoob/three.js/issues/9562
Basically, js from the examples folder aren't ES Modules, so they can't be be used with the "import" keyword yet.
Oh, the other day I did this library because I needed some Three.js addons in my workflow, maybe could be of any help.
Of course the only issue is with the two deprecation notice functions, as this issue reports.
In the case of CanvasRenderer, the issue here is the conflict with the export from Three.Legacy.js: should CanvasRenderer be part of the core or only be in examples ?
But as for modularising examples in general: as long as we only have one file per example, it won't be usable both as simple
Closing. As @cecilemuller mentioned, #9562 is the leading issue for this topic.
In the meantime, if you use Webpack, you can manually alias files from "examples" to import
this way, so even if it's not as clean as examples being ES Modules, at least it's in the same bundle instead of having to have a <script>
for each file:
In webpack.config.js
:
resolve: {
alias: {
'three/OrbitControls': path.join(__dirname, 'node_modules/three/examples/js/controls/OrbitControls.js'),
'three/OBJLoader': path.join(__dirname, 'node_modules/three/examples/js/loaders/OBJLoader.js')
// ...
}
},
//...
plugins:[
new webpack.ProvidePlugin({
'THREE': 'three'
}),
//...
]
In application.js
:
import 'three';
import 'three/OrbitControls';
/* global THREE */
console.log(THREE.OrbitControls);
I found that @cecilemuller's excellent workaround only worked with r82 in my situation (with Webpack 2 and using a legacy example).
In particular, cf975178 causes Webpack 2 to export the THREE
properties in a way that can not be replaced and CanvasRenderer
(unlike OrbitControls
in @cecilemuller's example) has a pre-existing definition on THREE
.
> THREE.CanvasRenderer
function CanvasRenderer() {
console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );
this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', …
> THREE.CanvasRenderer = 42
42
> THREE.CanvasRenderer
function CanvasRenderer() {
console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );
this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', …
As far as I can tell, the builtin THREE
functions are exported using getters instead of object properties. In my case, cf975178 is the difference between these two _compiled_ export styles:
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CanvasRenderer", function() { return CanvasRenderer; });
// or
exports.CanvasRenderer = CanvasRenderer;
To work with r83 and above, I just had to explicitly load the UMD version in the ProvidesPlugin
:
new webpack.ProvidePlugin({
'THREE': 'three/build/three'
}),
The only way for me without changing the sources or using an outdated 'custom' npm package was (r89, Webpack3, ES6):
window.THREE = Object.unfreeze(require('three'))
require('three/examples/js/renderers/Projector')
require('three/examples/js/renderers/CanvasRenderer')
// After that you can do :
let renderer = new THREE.CanvasRenderer()
With Object.unfreeze declared before (found : here )
Object.unfreeze = function (o) {
var oo = undefined
if (o instanceof Array) {
oo = []
var clone = function (v) {
oo.push(v)
}
o.forEach(clone)
}
else if (o instanceof String) {
oo = new String(o).toString()
}
else if (typeof o == 'object') {
oo = {}
for (var property in o) {
oo[property] = o[property]
}
}
return oo
}
I use three.js with RequireJS (my preferred AMD loader) and have been able to successfully reincorporate CanvasRenderer.js and Projector.js into build/three.js in R90 if anyone wants a copy.
My use case was trying to use it to render a model of the solar system in Chrome on a Raspberry Pi 2 Model B, which simply not playing nice with WebGLRenderer in any way, shape, or form, including any of the examples on the threejs.org site, even after enabling the "Software override rendering list." :(
@pmaster06 My dude. I've been working on this for like 4 hours, and your solution is the only thing that's worked!
I just realized I could attach a file here. Just use CanvasRenderer like you used to.
three.js.zip
Thank you, very helpful!
@pmaster06 thx,very helpful
Most helpful comment
The only way for me without changing the sources or using an outdated 'custom' npm package was (r89, Webpack3, ES6):
With Object.unfreeze declared before (found : here )