mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
100.38k stars 35.2k forks source link

Moving to a modular architecture #4776

Closed kumavis closed 7 years ago

kumavis commented 10 years ago

Browserify Moving to this architecture has advantages and disadvantages. Please add your thoughts.

Note: this does not require three.js consumers to use browserify.

kumavis commented 10 years ago

One advantage is that this would enforce a modular architecture for the ongoing development of three.js.

The common style in node/browserify has each file declare its dependencies at the top, and considers global variables an anti-pattern.

Here is an example snippet:

// src/geometry/BoxGeometry.js
var Geometry = require('./Geometry.js');
var Vector3 = require('../core/Vector3.js');
module.exports = BoxGeometry;

function BoxGeometry() {
  // ...
}

BoxGeometry.prototype = Geometry.prototype;

Another advantage is that consumers of three.js using browserify would be able to pick and choose the parts they want. They could just import Scene, BoxGeometry, PerspectiveCamera, and WebGLRenderer, get the dependencies for all those automatically ( Object3D etc ), and have a small bundle of javascript that supports just the feature set they want.

This could be done in a way that imposes no breaking changes. At the top level, we would export all classes we consider to be part of the standard package

// src/three.js
var THREE = { rev: 101 }
module.exports = THREE

THREE.Geometry = require('./geometry/Geometry.js')
THREE.BoxGeometry = require('./geometry/BoxGeometry.js')
// ...

note: I'm not exactly requiring the dependencies at the top in this example, because this file would almost exclusively be require statements.

Finally we would wrap that in a Universal Module Definition that detects if a module system (node/browserify, AMD) is in use, and if so exports it, or otherwise appends it to the global object ( window ).

Lets review:

kumavis commented 10 years ago

This would require replacing the build system, but the new one would be pretty straight forward.

shi-314 commented 10 years ago

Some other advantages:

kumavis commented 10 years ago

@shi-314 I guess I'm a little confused, I feel like You can structure your code and You can build for production are things that you can do without the architectural shift? Are you talking about three.js source or things built using three.js?

ghost commented 10 years ago

One practice that three.js uses that makes it tricky to use in commonjs environments is the use of instanceof: https://github.com/mrdoob/three.js/blob/master/src/core/Geometry.js#L82

This is because in an application you often end up with different versions of the same library in your source tree, so checking instanceof doesn't work between different versions of the same library. It would be good in preparation for a move to a commonjs module system to replace those instanceof checks with feature-checking behind a Geometry.isGeometry(geom) style interface.

shi-314 commented 10 years ago

@kumavis I am talking about things built in three.js. Let's say you want to create your own material with your shaders etc. At the moment you need to extend the global THREE object to stay consistent with the rest of the three.js code:

THREE.MeshMyCoolMaterial = function (...) { ... }

But if we had Browserify than you could do:

var MeshLambertMaterial = require('./../MeshLambertMaterial');
var MeshMyCoolMaterial = function (...) {...}

So your namespace stays consistent and you don't need to use THREE.MeshLambertMaterial and MeshMyCoolMaterial in your code.

And with You can build for production I basicly meant the same thing you mentioned: allows three.js consumers using browserify to pick and choose functionality.

kumavis commented 10 years ago

@shi-314 thank you, that is more clear. That does impact my proposed general solution to deserialization consumer-defined classes:

// given that `data` is a hash of a serialized object
var ObjectClass = THREE[ data.type ]
new ObjectClass.fromJSON( data )

This is from my proposed serialization / deserialization refactor https://github.com/mrdoob/three.js/pull/4621

gero3 commented 10 years ago

Performance shouldn't be affected by a change like this.

mattdesl commented 10 years ago

This is a pretty huge change but I'm also in favour of it.

Some other major advantages:

To @mrdoob and the other authors; if you don't have much experience with NPM/Browserify I would suggest making a couple little projects with it and getting a feel for its "philosophy." It's very different from ThreeJS architecture; rather than big frameworks it encourages lots of small things.

CharlotteGore commented 10 years ago

Another advantage of this approach is that there can be an ecosystem of open source, third party Three.JS modules, especially shaders, geometries, model loaders etc. Published through NPM or Github/Component which people can then easily just reference and use. At the moment stuff is shared by hosting a demo which people then 'view source' on. Three.JS deserves better!

I think one of the problems I have with Three.JS is how quickly code becomes incompatible with the current version of Three.JS. Another advantage of switching to something like this is being able to specify specific versions of bits of Three.JS would be very powerful and handy.

+1

cecilemuller commented 10 years ago

+1 for a CommonJS/browserify architecture, it would make the core more lightweight and extensions would fit even if they come from third-parties

erno commented 10 years ago

Fragmenting three.js into little modules has a lot of costs as well. The current system allows pretty simple third party addons (witness eg. jetienne's THREEx modules). There's a lot to be said about the simplicity of the current setup, as long as the JS module systems are just wrappers around build systems.

Another way of minimizing build size is what ClojureScript does. They follow some conventions to allow Google's Closure compiler to do whole-program analysis and dead code elimination.

repsac commented 10 years ago

+1 for the unappreciated, and often overlooked, elegance of simplicity

JosephClay commented 10 years ago

+1

mattdesl commented 10 years ago

Fragmenting three.js into little modules has a lot of costs as well. The current system allows pretty simple third party addons (witness eg. jetienne's THREEx modules).

The idea here is that a UMD build would still be provided for non-Node environments. Plugins like THREEx would work the same way for those depending on ThreeJS with simple <script> tags.

The tricky thing will be: how do we require() a particular plugin if we are in a CommonJS environment? Maybe browserify-shim could help.

There's a lot to be said about the simplicity of the current setup, as long as the JS module systems are just wrappers around build systems.

ThreeJS's current plugin/extension system is pretty awful to work with, and far from "simple" or easy. Most ThreeJS projects tend to use some form of plugin or extension, like EffectComposer, or FirstPersonControls, or a model loader, or one of the other many JS files floating around in the examples folder. Right now the only way to depend on these plugins:

Now, imagine, with browserify you could do something like this:

var FirstPersonControls = require('threejs-controls').FirstPersonControls;

//more granular, only requiring necessary files
var FirstPersonControls = require('threejs-controls/lib/FirstPersonControls');

Those plugins will require('threejs') and anything else that they may need (like GLSL snippets or text triangulation). The dependency/version management is all hidden to the user, and there is no need for manually maintained grunt/gulp concat tasks.

CharlotteGore commented 10 years ago

The tricky thing will be: how do we require() a particular plugin if we are in a CommonJS environment?

I've been using CommonJS for THREE.js projects for a bit now. It's a bit of a manual process, converting chunks of other people's code into modules and I don't think there'll be an easy way to avoid that for legacy code that isn't converted by the authors or contributors.

The important bit is that there's a module exporting the entire 'standard' THREE object, which can then be required by anything that wishes to extend it.

var THREE = require('three');

THREE.EffectComposer = // ... etc, remembering to include copyright notices :)

This has worked pretty well for me, especially as the project grows and I start adding my own shaders and geometries into their own modules etc.

As long as there's a 'threejs-full' or 'threejs-classic' npm package then this becomes a pretty viable way of working with old Three.js stuff in a CommonJS environment but I suspect this is pretty niche!

smrjans commented 10 years ago

+1 I believe once fragmented threejs modules are available in npm, plugin developers will love to migrate to CommonJS env. On Jun 5, 2014 9:19 PM, "Charlotte Gore" notifications@github.com wrote:

The tricky thing will be: how do we require() a particular plugin if we are in a CommonJS environment?

I've been using CommonJS for THREE.js projects for a bit now. It's a bit of a manual process, converting chunks of other people's code into modules and I don't think there'll be an easy way to avoid that for legacy code that isn't converted by the authors or contributors.

The important bit is that there's a module exporting the entire 'standard' THREE object, which can then be required by anything that wishes to extend it.

var THREE = require('three'); THREE.EffectComposer = // ... etc, remembering to include copyright notices :)

This has worked pretty well for me, especially as the project grows and I start adding my own shaders and geometries into their own modules etc.

As long as there's a 'threejs-full' or 'threejs-classic' npm package then this becomes a pretty viable way of working with old Three.js stuff in a CommonJS environment but I suspect this is pretty niche!

— Reply to this email directly or view it on GitHub https://github.com/mrdoob/three.js/issues/4776#issuecomment-45236911.

cecilemuller commented 10 years ago

It could also make the shaders made modular as well, e.g. using glslify. Even things like making an Express middleware that generates shaders on demand becomes easier then.

mrdoob commented 10 years ago

Some months ago I moved frame.js to require.js and I finally understood how this AMD stuff works.

I still need to learn, however, how to "compile" this. What's the tool/workflow for generating a three.min.js out of a list of modules?

shi-314 commented 10 years ago

I prefer gulp.js as a build system with the gulp-browserify plugin. It's really easy to understand and the code looks cleaner than grunt in my opinion. Check this out: http://travismaynard.com/writing/no-need-to-grunt-take-a-gulp-of-fresh-air :wink:

zz85 commented 10 years ago

some thoughts: (based on my limited experience with node, npm, browserify of course)

  1. i think node.js modules are great (that's npm, modules, and require()s)
  2. i think browserify is also great

that said, following the discussion on this thread, i'm not sure if everyone had the same understanding of browserify (browserify, commonjs, requirejs, amd, umd are somewhat related although they may not necessary be the same thing).

now if you may follow my chain of thoughts a little.

  1. JS is great, it run fast across browsers.
  2. wow, now JS runs on the server side too.
  3. that's node.js, it's cool, so let's code stuff in node.js
  4. but I don't wish to write/ can't write everything/ find something to use.
  5. no worries, now run npm install modules
  6. now require these cool modules so we can use them.
  7. works great!
  8. now wait, we have just wrote a whole bunch of stuff in JS that runs on node.js
  9. isn't js supposed in browsers? how do we make these code run there again?

There's where Browserify comes into the picture. Well, technically one can use requireJS in the browser. But you wish to bundle js files together without making too many network calls (unlike file system require()s which are fast). There's where Browserify does some cool stuff like static analysis to see which modules needs to be imported and creates build which are more optimized for your application. (There are limitations of course, it probably can't parse require('bla' + variable)) it can even swap out parts which requires an emulation layer for node.js dependent stuff. yeah, it generates a js build which i can now include in my browser.

Here are some of the stuff browserify can do https://github.com/substack/node-browserify#usage

Sounds like everything's great so far... but there are a few points I thought worth considering we move to a "browserify architectural"

So if we see this diversity and convenient module loading (mainly riding on the npm ecosystem) along with customized builds a great thing, then it might be worth a short is having a change in paradigm, refactoring code and changing our current build system.

guybrush commented 10 years ago

@mrdoob some tools around browserify are listed here: https://github.com/substack/node-browserify/wiki/browserify-tools.

regarding the three.min.js, you would not use the minified code in your project. all you do is var three = require('three') in your project.js and then run browserify project.js > bundle.js && uglifyjs bundle.js > bundle.min.js. note: you still can ship minified code for <script src="min.js">.

i am currently wrapping three.js with

if ('undefined' === typeof(window))
  var window = global && global.window ? global.window : this
var self = window

and

module.exports = THREE

then i wrap extensions with

module.exports = function(THREE) { /* extension-code here */ }

so i can require it like that:

var three = require('./wrapped-three.js')
require('./three-extension')(three)

so this is not optimal, but i personally can actually live with it and think its not so bad - though @kumavis proposal would be a huge advantage.

but maybe it would make sense to fork three and put all the things in seperate modules just to see how it would work out.

also checkout http://modules.gl/ which is heavily based on browserify (though you can use every module on its own without browserify).

mattdesl commented 10 years ago

@mrdoob @shi-314 gulp-browserify has been blacklisted in favour of just using browserify directly (i.e. via vinyl-source-stream).

Tools like grunt/gulp/etc are constantly in flux, and you'll find lots of differing opinions. In the end it doesn't matter which you choose, or whether you just do it with a custom script. The more important questions are: how will users consume ThreeJS, and how much backward-compatibility do you want to maintain?

After some more thought, I think it will be really hard to modularize everything without completely refactoring the framework and its architecture. Here are some problems:

My suggestion? We consider two things moving forward:

  1. Start small; pull out a few essential and reusable features like Vector/Quaternion, color conversions, triangulation, etc. These things are good candidates for NPM since they are useful outside of the scope of ThreeJS. They can also have their own test suite, versioning, and issue tracking.
  2. When new code needs to be added to ThreeJS, like a new feature, or a dependency (e.g. poly2tri/Tess2), consider pulling it out as a separate module and depending on it via NPM.

I'd love to see everything modularized, but I'm not sure of an approach that's realistic for ThreeJS. Maybe somebody should some experiments in a fork to see how feasible things are.

mrdoob commented 10 years ago

Thanks for the explanations guys!

What I fear is complicating things to people that are just starting. Forcing them to learn this browserify/modules stuff may not be a good idea...

repsac commented 10 years ago

Would have to agree with @mrdoob here. I, and a lot of colleagues, are not web programmers (rather VFX/animation TDs). Picking up WebGL and Three has certainly been enough work as is on top of our current workload (and in some cases some of us had to learn js on the spot). Much of what I have read in this thread, at times, makes me shudder thinking about how much more work would be added to my plate if Three moved to this structure. I could be wrong but that is certainly how it reads to me.

domenic commented 10 years ago

With a precompiled UMD (browserify --umd) build in the repo, there's no change to the workflow for existing developers.

JosephClay commented 10 years ago

@mrdoob The idea of a dependency management system is simplicity. Reading dozens of posts on options and build systems may be overwhelming, but ultimately the current system is not sustainable. Anytime one file depends on another, that's a hunt-and-search any new developer has to perform to find a reference. With browserify, the dependency is explicit and there's a path to the file.

@repsac A dependency system should make Three more accessible to users of other languages as it avoids global scope, load order nightmares and follows a paradigm similar to other popular languages. var foo = require('./foo'); is (loosly) akin to C#'s using foo; or Java's import foo;

CharlotteGore commented 10 years ago

I'd love to see everything modularized, but I'm not sure of an approach that's realistic for ThreeJS. Maybe somebody should some experiments in a fork to see how feasible things are

I think this is the way to go, really. Get the work done, show how it works.

And consuming the API would be pretty ugly: require('three/src/math/Vector2')

As an experiment I just converted the 'getting started' from the Three docs to this new modular approach. I can imagine there being a lot of references unless people are pretty strict about breaking up their code into tiny modules.

The main advantage of doing this would be that the resulting build size would be a tiny fraction of the size of the full Three.js because you'd only be including the things specifically referenced here and then the things that those things depend on.

I guess referencing all the dependencies you need (and installing them all individually) could prove a bit too awful in practice.

If you're explicitly targeting mobile devices then this highly granular approach would be perfect but in reality I suspect we'll need packages that export the entire THREE api which will work as normal, then smaller packages that encapsulate all the bonus geometry, all the renderers, all the math, all the materials etc, then down to the individual module level so that developers can decide for themselves.

And yes, coding for the web is a pain.

ANyway, on with the experiment...

Install our dependencies..

npm install three-scene three-perspective-camera three-webgl-renderer three-cube-geometry three-mesh-basic-material three-mesh three-raf

Write our code...

// import our dependencies..
var Scene = require('three-scene'),
  Camera = require('three-perspective-camera'),
  Renderer = require('three-webgl-renderer'),
  CubeGeometry = require('three-cube-geometry'),
  MeshBasicMaterial = require('three-mesh-basic-material'),
  Mesh = require('three-mesh'),
  requestAnimationFrame = require('three-raf');

// set up our scene...
var scene = new Scene();
var camera = new Camera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new Renderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// create the cube...
var geometry = new CubeGeometry(1, 1, 1);
var material = new MeshBasicMaterial({color: 0x00ff00});
var cube = new Mesh(geometry, material);
scene.add(cube);
// position the camera...
camera.position.z = 5;
// animate the cube..
var render = function () {
  requestAnimationFrame(render);
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  renderer.render(scene, camera);
};
// begin!
render();

then build our file

browserify entry.js -o scripts/hello-world.js

then include it in our page

<script src="/scripts/hello-world.js" type="text/javascript"></script>
JosephClay commented 10 years ago

I guess referencing all the dependencies you need (and installing them all individually) could prove a bit too awful in practice.

The end user doesn't necessarily need to use browserify in their project in order for Three to use browserify to manage its codebase. Three can be exposed as the global THREE as it is now...include the build file and run with it.

mattdesl commented 10 years ago

@repsac @mrdoob the changes would be backward-compatible, so that current users do not need to change anything if they don't want to. These suggestions are to improve the long-term maintainability and longevity of ThreeJS's sprawling and monolithic codebase. Things like dependency and version management might sound like a headache to the uninitiated, but they are awesome for those developing tools, frameworks, plugins, and large scale websites on top of ThreeJS.

i.e. End-user code can still look the same, and the examples don't need to change at all:

<script src="three.min.js"></script>

<script>
var renderer = new THREE.WebGLRenderer();
</script>

For more ambitious developers who are looking for a modular build, or for those looking to develop long-term solutions on top of ThreeJS (i.e. and take advantage of version/dependency management), it might look more like this: npm install three-vecmath --save

Then, in code:

var Vector2 = require('three-vecmath').Vector2;

//.. do something with Vector2

And, furthermore, this allows people to use things like ThreeJS's vector math, color conversions, triangulation, etc. outside of the scope of ThreeJS.

erno commented 10 years ago

Even though I think the require() mess is a bad idea and bad tradeoff, it would be an even worse idea to expose users to two different kinds of three.js code, telling users one is the fancy module system flavour and other is the simpler (but second-class) module system flavour.

kumavis commented 10 years ago

@erno I think you've missed the point, three.js would be organized by a module structure internally, but that is used to produce a build file no different from the current setup.

The primary gain is improving the experience of developing and maintaining three.js.

antont commented 10 years ago

@kumavis - no @erno did not actually miss that, but I understood(*) that he makes the point that if three.js is sometimes used via require and sometimes not it can be confusing. For example someone looks both at the three source and then some 3rd party examples and encounters differences in how it all is and works.

(*)we talked about this on irc earlier today.

I think that's a kind of valid point but am not sure whether / how it works out in the end -- whether is an issue with the module & build thing usages really. But certainly seems worth a thought and overall has seemed good to me that the overall matter has been considered here carefully, thanks for infos and views so far from my part.

kumavis commented 10 years ago

@antont I see. People have suggested a variety of different approaches here, I was assuming that we would mainly provide documentation for top-level usage (pulling everything off of THREE), but others may create examples that would not follow this and it may lead to some confusion. And thats a valid concern.

kumavis commented 10 years ago

I think I was a little confused by the language.

and other is the simpler (but second-class) module system flavour.

This is just referring to the build file, yes?

antont commented 10 years ago

In my understanding, yes. Can't imagine what else but can be missing something though.

erno commented 10 years ago

antont, kumavis: The proposals here have talked about exposing the require() style code to end users too, see eg. mattdesl's most recent comment.

"For more ambitious developers who are looking for a modular build, or for those looking to develop long-term solutions on top of ThreeJS (i.e. and take advantage of version/dependency management) [...]"

zz85 commented 10 years ago

one way to having a more optimized builds is actually to have a script which figure out your dependencies automatically and churn out the required modules.

right now bower & browserify does out with require, but they aren't the only solutions. i don't know if there are other off-the-shelf open source projects which does that (maybe like ng-dependencies) but i've written such tools before that i think there would be other approaches to solving these pains.

kumavis commented 10 years ago

google's closure compiler might be such a tool?

erichlof commented 10 years ago

On the user's side, might this be of some help? http://marcinwieprzkowicz.github.io/three.js-builder/

zz85 commented 10 years ago

that's pretty interesting @erichlof :) i wonder if @marcinwieprzkowicz generated this by hand... https://github.com/marcinwieprzkowicz/three.js-builder/blob/gh-pages/threejs-src/r66/modules.json

valette commented 9 years ago

One practice that three.js uses that makes it tricky to use in commonjs environments is the use of instanceof: https://github.com/mrdoob/three.js/blob/master/src/core/Geometry.js#L82

This is because in an application you often end up with different versions of the same library in your source tree, so checking instanceof doesn't work between different versions of the same library. It would be good in preparation for a move to a commonjs module system to replace those instanceof checks with feature-checking behind a Geometry.isGeometry(geom) style interface.

in git/three.js/src:

grep -r instanceof . | wc -l 
164

in git/three.js/examples:

grep -r instanceof . | wc -l 
216

so there are in total 380 uses of instanceof in three.js. What would be the best implementation as a replacement?

mrdoob commented 9 years ago

I recently added a type property which can be used to replace most of those.

valette commented 9 years ago

I recently added a type property which can be used to replace most of those.

nice! Will prepare a PR.

actionnick commented 9 years ago

For an example of how this is handled in another popular and large JS library take a look at https://github.com/facebook/react. The codebase is structured using the node style based module system (which browserify implements) but is built for release using grunt. This solution is flexible for 3 use cases.

  1. Pure consumer of Three.js writing vanilla JS can still just use the build file as always. There is no change for this use case.
  2. Consumer of Three.js using browserify can declare Three.js as a dependency in a project and only require specific dependencies. The benefits of proper dependency management have been well documented.
  3. Contributing to Three.js should now be simpler as dependencies between components is explicitly documented.
rasteiner commented 9 years ago

I made some research...

Yesterday I hacked together a (rather stupid) script that transforms the Three.js sourcecode to use CommonJS require() statements to declare dependencies between files. Just to see what happens... This:

  1. It ends up having rather ridiculous require statements like this (from WebGLRenderer):

    var THREE = require('../Three.js');
    require('../math/Color.js');
    require('../math/Frustum.js');
    require('../math/Matrix4.js');
    require('../math/Vector3.js');
    require('./webgl/WebGLExtensions.js');
    require('./webgl/plugins/ShadowMapPlugin.js');
    require('./webgl/plugins/SpritePlugin.js');
    require('./webgl/plugins/LensFlarePlugin.js');
    require('../core/BufferGeometry.js');
    require('./WebGLRenderTargetCube.js');
    require('../materials/MeshFaceMaterial.js');
    require('../objects/Mesh.js');
    require('../objects/PointCloud.js');
    require('../objects/Line.js');
    require('../cameras/Camera.js');
    require('../objects/SkinnedMesh.js');
    require('../scenes/Scene.js');
    require('../objects/Group.js');
    require('../lights/Light.js');
    require('../objects/Sprite.js');
    require('../objects/LensFlare.js');
    require('../math/Matrix3.js');
    require('../core/Geometry.js');
    require('../extras/objects/ImmediateRenderObject.js');
    require('../materials/MeshDepthMaterial.js');
    require('../materials/MeshNormalMaterial.js');
    require('../materials/MeshBasicMaterial.js');
    require('../materials/MeshLambertMaterial.js');
    require('../materials/MeshPhongMaterial.js');
    require('../materials/LineBasicMaterial.js');
    require('../materials/LineDashedMaterial.js');
    require('../materials/PointCloudMaterial.js');
    require('./shaders/ShaderLib.js');
    require('./shaders/UniformsUtils.js');
    require('../scenes/FogExp2.js');
    require('./webgl/WebGLProgram.js');
    require('../materials/ShaderMaterial.js');
    require('../scenes/Fog.js');
    require('../lights/SpotLight.js');
    require('../lights/DirectionalLight.js');
    require('../textures/CubeTexture.js');
    require('../lights/AmbientLight.js');
    require('../lights/PointLight.js');
    require('../lights/HemisphereLight.js');
    require('../math/Math.js');
    require('../textures/DataTexture.js');
    require('../textures/CompressedTexture.js');

    We would need some major refactoring, maybe splitting the WebGLRenderer (and such) into multiple modules (atm the file is over 6000 lines long).

  2. We need need to find a solution for the GLSL chunks. Atm those files get compiled into THREE.ShaderChunk at compile time and then into THREE.ShaderLib at runtime (joining toghether arrays of THREE.ShaderChunks) which is rather tricky to do with only browserify. I presume it would require a browserify transform that does the same.

React.js uses commoner to lookup their modules without having to refer to them by file path. Maybe we could do the same and also define custom rules that allow us to require GLSL files transforming them to JS syntax.

kumavis commented 9 years ago

@rasteiner you may be very happy to learn about https://github.com/stackgl/glslify, it comes from the growing http://stack.gl family

mattdesl commented 9 years ago

I've had a fair bit of experience with modules and the "unixy" approach in the past couple months and my thought now is that it's too little too late, and refactoring threejs for modularity or npm modules would be an unrealistic goal.

Here's what I'm currently doing to tackle the problem of modularity/reusability:

My new projects tend to use "three" on npm just to get up and running. It would be pretty awesome if ThreeJS officially published the build to npm using version tags that align with the release numbers.

PS: to those interested in bringing reusable/modular shaders to their workflow:
https://gist.github.com/mattdesl/b04c90306ee0d2a412ab

Sent from my iPhone

On Nov 20, 2014, at 7:42 AM, aaron notifications@github.com wrote:

@rasteiner you may be very happy to learn about https://github.com/stackgl/glslify, it comes from the growing http://stack.gl family

— Reply to this email directly or view it on GitHub.

poshaughnessy commented 9 years ago

In case it helps others who might be looking how to use Three.js with browserify, and stumble on this thread, the way I've just set it up myself is to use browserify-shim.

Following the README section on "You will sometimes a) Expose global variables via global", I included a separate script tag for Three.js and configured it to expose the global THREE variable.

And then just the bit that I had to work out myself was how to include extras like ColladaLoader, OrbitControls etc. I did this like so:

From package.json:

    "browser": {
        "three": "bower_components/threejs/build/three.js"
    },
    "browserify-shim": "browserify-shim-config.js",
    "browserify": {
        "transform": [ "browserify-shim" ]
    }

browserify-shim-config.js:

    module.exports = {
        "three": { exports: "global:THREE" },
        "./vendor/threejs-extras/ColladaLoader.js": { depends: {"three": null}, exports: "global:THREE.ColladaLoader" },
        "./vendor/threejs-extras/OrbitControls.js": { depends: {"three": null}, exports: "global:THREE.OrbitControls" }
    };

Then in my own script, main.js:

    require('../vendor/threejs-extras/ColladaLoader.js');
    require('../vendor/threejs-extras/OrbitControls.js');

    var loader = new THREE.ColladaLoader(),
        controls = new THREE.OrbitControls(camera);
...
ChiChou commented 9 years ago

Browserify requires rebuilding the entire script when you modify even on bytes. I once use browserify to pack a project that requires THREE.js, then it takes more than two seconds to build a boundle and blocks the livereload every time I make a change. That's too frustrating.