Upload
josh-staples
View
1.570
Download
4
Tags:
Embed Size (px)
Citation preview
Josh Staples @cubicleDowns blog.tempt3d.com github.com/cubicleDowns/ng-three-viewer
WHO? Software Engineer @ napofearth.com“Explore Music Visually” PRIVATE BETA (#ngmeetup)
TODAY
1. (v1) Angular & Three.js 3D Model Viewer
2. (v2) “Controller as” & prototypal Angular components
3. Grunt & Closure compilation approaches
4. (v3*) Scoped scene graph
WHY?Great pattern(s) with low barrier of entry
& high productivity
All web devs can easily be 3D web devs
Isolate THREE code from Angular UI/UX code!
THREE WHAT?threejs.org three features
● Renderers● Scene Graph● Cameras● Animation● Lights● Materials● Shaders● Objects● Geometry● Loaders● Utilities● Export/Import● Support● Examples
● WebVR● DIYVR
Started by Mr. Doob on April 23, 2010
ANGULAR WHO?
Demo v1
http://localhost:8000/v1/dist/
CONTROLLERFileLoaderController
CONTROLLERAppController
UI Elements and Controllers
DIRECTIVE INCLUDEfile-loader.html
DIRECTIVE INCLUDEabout.html
DIRECTIVE INCLUDEchrome.html
DIRECTIVE INCLUDEtoolbar.html
CONTROLLERFileLoaderController
CONTROLLERAppController
DIRECTIVE select
SERVICEStorageService
SERVICEMessageBus
FACTORYViewer
Dependency Injections Across Angular Components
Viewer.factory('ViewerFactory', … ) {
init() home = new Viewer.Scene()
animate () render ()
makeSelection () loadOBJMTL () loadGLTF () loadOBJ () loadJSON () scale () rotate ()
/** Translate the model along an axis * @param {number} x * @param {number} y * @param {number} z */ translate (x,y,z) home.wrangler.currentModel.position.set(x, y, z);
DIRECTIVE select● makeSelection ()
CONTROLLERAppController● init ()● rotate ()● scale ()● translate ()
SERVICEMessageBus
CONTROLLERFileLoaderController● loadOBJMTL ()● loadGLTF ()● loadOBJ ()● loadJSON ()
Viewer Factory Interface
Viewer Factory Singleton
Viewer.Scene()
this.sceneTHREE.Scene()
Viewer Factory Architecture
this.rendererTHREE.WebGLRenderer()
this.wranglerViewer.Wrangler()
this.camerasViewer.Cameras()
this.setupViewer.Setup()
this.controlsTHREE.OrbitControls()
this.raycasterTHREE.Raycaster()
function animate () { requestAnimationFrame(animate); render(); }
function init(params) { home = new Viewer.Scene(params); animate(); }
Angular Directive, <canvas select>
elem.on(tap, function(e) x = e.gesture.center.x;
y = e.gesture.center.y;
// creating NDC coordinates for ray intersection. mouseDown.x = (x / width) * 2 - 1; mouseDown.y = -(y / height) * 2 + 1; ViewerFactory.makeSelection(mouseDown);
Viewer Factory, makeSelection
makeSelection(mouse): var vector = new THREE.Vector3( mouse.x, mouse.y, 1).unproject(home.cameras.liveCam); home.raycaster.set(home.cameras.liveCam.position, vector.sub(home.cameras.liveCam.position).normalize());
var intersected = home.raycaster.intersectObjects(home.wrangler.currentModel, true);
MessageBus.trigger('objectSelected', intersected[0])
Angular Controller/Factory
$scope.$on(‘objectSelected’, function () { // Do Something. });
USE CASE - User Click, Intersect 3D Model, Return Model Information
MOST PROFITABLE MOVIE?
MOST PROFITABLE MOVIE?
THE SEQUEL!
STARRING
&“Controller as”
as ctrl
Annotationsas SNAFU
Sequel (v2)
http://localhost:8000/v2/dist/
Controller as<div id="file-loader" ng-controller="FileLoaderController as loader" ng-show=”loader.visible”> <input type="text" ng-model="loader.data.obj" placeholder="obj file url"> <input type="text" ng-model="loader.data.name" placeholder="unique name">
<button ng-click="otherLoader.loadOBJMTL()">Load OBJ/MTL</button> <button ng-click="loader.loadSampleOBJMTL()">SAMPLE OBJ-MTL</button>
Controller level scope:)
<div id="file-loader" ng-controller="FileLoaderController" ng-show=”visible”>- - -
<input type="text" ng-model="data.obj" placeholder="obj file url"> <input type="text" ng-model="data.mtl" placeholder="mtl file url">
<button ng-click="loadOBJMTL()">Load OBJ/MTL</button> <button ng-click="loadSampleOBJMTL()">SAMPLE OBJ-MTL</button>
nearest scope :(
Viewer.ViewerService
.prototypeinitlistenersanimaterendermakeSelectionloadOBJMTLloadOBJloadGLTFloadJSONrotatetranslatescale
● No More Factories○ closure pattern
● Instead, prototypal Service○ ‘new’ and this○ .bind() for callbacks
● Saves Memory, Time, Searches (sorry)
● Single Pattern For Everything!
● IMHO, the best way to code JS
Service, !Factory
/** @ngInject */Viewer.ViewerService = function($timeout, MessageBus){
this.timeout = $timeout;this.MessageBus = MessageBus;
};
Viewer.ViewerService.prototype.init = function (params){this.home = new Viewer.Scene(params);this.MessageBus.trigger(‘app-ready’);animate();
};
Viewer.factory('ViewerFactory', ['MessageBus', function (MessageBus)
function init () {}
function makeSelection() {}
return { init: init, makeSelection: makeSelection}
closure style,ng-annotate:\
prototypal:)
Prototypal Angular
Controller asAnnotations
/** Service which initiates the THREE.js scene and * provides methods to interact with that scene * * @param {angular.$timeout} $timeout * @param {!Viewer.MessageBus} MessageBus * @constructor * @ngInject */
Viewer.ViewerService = function($timeout, MessageBus){ this.timeout = $timeout; this.MessageBus = MessageBus; };
/** * Translate the model along an axis * @param {number} x * @param {number} y * @param {number} z */Viewer.ViewerService.prototype.translate = function(x, y, z){ this.home.wrangler.currentModel.position.set(x, y, z)};
/** * @param {number} s */Viewer.ViewerService.prototype.scale = function(s) { this.home.wrangler.currentModel.scale.set(s, s, s);};
The Closure Compiler can use data type information about JavaScript variables to provide enhanced optimization and warnings.
APP INIT (app.js)angular.module('ThreeViewer', ['ngHammer', 'ngRoute', 'LocalStorageModule']) .config(['localStorageServiceProvider',function(localStorageServiceProvider){
…. .config(['$locationProvider', function($locationProvider) {
…. $locationProvider.html5Mode(true); .config(['$routeProvider', function($routeProvider){
angular.module('ThreeViewer', ['ngRoute', 'LocalStorageModule']) .config(ThreeViewer.ConfigLocation)
…. .directive('select', ['ViewerService', ThreeViewer.SelectDirective.factory])
…. .filter('forceInt', ThreeViewer.ForceInt.factory)
…. .service('ViewerService', [MessageBus', ThreeViewer.ViewerService])
…. .controller('AppController', ['$scope', 'ViewerService', ThreeViewer.AppController]);
v2:)
v1:\
uglify: { ng3: { options: { compress: { drop_console: true }, sourceMap: true, }, files: { 'dist/app.min.js': ['<%= concat.ng3.dest %>'] } } },
command: 'java -jar closure/compiler.jar ' + '--compilation_level SIMPLE_OPTIMIZATIONS' + '--language_in ECMASCRIPT5_STRICT ' + '--angular_pass ' + '--externs closure/externs/angular-1.3.js ' + '--externs closure/externs/three.js ' + '--generate_exports ' + '--manage_closure_dependencies ' + '--js closure/library/base.js ' + '--js <%= app %> ' + '--js <%= ng %> ' + '--js <%= three %> ' + '--js_output_file dist/app.min.js'
Minify or Closure Compilation?
Closure Compiler● type checking● ngInject● goog.provide / require
Grunt ng-annotate● uglify● ng-annotate
NG to THREEONE APP, TWO PATTERNSV1
● Most Common Angular Pattern● Grunt uglify / minify● Factories● Services● Filters● Directives● Init controllers from DOM
V2
● Prototypal Pattern for Everything!● Bridge to Angular 2.0● Controller as (local scope)● Closure Compilation
○ type checking○ -- angular_pass○ dependency chains○ minification
● App.js Initialization● No closure pattern (factories)
NG to THREEJOIN US! Mobile Developer - Backend GuruUI/UX Designer - QAnapofearth.com/jobs #ngmeetup