Parcourir la source

Merge remote-tracking branch 'origin/dev' into removeHackery

Conflicts:
	src/renderers/WebGLRenderer.js
OpenShift guest il y a 11 ans
Parent
commit
1e30568ee4

+ 9 - 15
src/core/Geometry.js

@@ -678,12 +678,13 @@ THREE.Geometry.prototype = {
 		return function ( usesFaceMaterial, maxVerticesInGroup ) {
 
 			var f, fl, face, materialIndex,
-				groupHash, hash_map = {};
+				groupHash, hash_map = {},geometryGroup;
 
 			var numMorphTargets = this.morphTargets.length;
 			var numMorphNormals = this.morphNormals.length;
 
 			this.geometryGroups = {};
+			this.geometryGroupsList = [];
 
 			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
 
@@ -700,8 +701,9 @@ THREE.Geometry.prototype = {
 
 				if ( ! ( groupHash in this.geometryGroups ) ) {
 
-					this.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
-
+					geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+					this.geometryGroups[ groupHash ] = geometryGroup;
+					this.geometryGroupsList.push(geometryGroup);
 				}
 
 				if ( this.geometryGroups[ groupHash ].vertices + 3 > maxVerticesInGroup ) {
@@ -711,8 +713,10 @@ THREE.Geometry.prototype = {
 
 					if ( ! ( groupHash in this.geometryGroups ) ) {
 
-						this.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
-
+						geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+						this.geometryGroups[ groupHash ] = geometryGroup;
+						this.geometryGroupsList.push(geometryGroup);
+						
 					}
 
 				}
@@ -722,16 +726,6 @@ THREE.Geometry.prototype = {
 
 			}
 
-			this.geometryGroupsList = [];
-
-			for ( var g in this.geometryGroups ) {
-
-				this.geometryGroups[ g ].id = geometryGroupCounter ++;
-
-				this.geometryGroupsList.push( this.geometryGroups[ g ] );
-
-			}
-
 		};
 
 	} )(),

+ 9 - 8
src/renderers/WebGLRenderer.js

@@ -623,9 +623,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( geometry.geometryGroups !== undefined ) {
 
-				for ( var g in geometry.geometryGroups ) {
+				for ( var i = 0,l = geometry.geometryGroupsList.length; i<l;i++ ) {
 
-					var geometryGroup = geometry.geometryGroups[ g ];
+					var geometryGroup = geometry.geometryGroupsList[ i ];
 
 					if ( geometryGroup.numMorphTargets !== undefined ) {
 
@@ -2734,7 +2734,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					}
 
-					_gl.drawElements( _gl.LINES, index.array.length, type, 0 ); // 2 bytes per Uint16Array
+					_gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array
 
 					_this.info.render.calls ++;
 					_this.info.render.vertices += index.array.length; // not really true, here vertices can be shared
@@ -2760,7 +2760,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						// render indexed lines
 
-						_gl.drawElements( _gl.LINES, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array
+						_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array
 
 						_this.info.render.calls ++;
 						_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
@@ -3780,12 +3780,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					addBuffer( scene.__webglObjects, geometry, object );
 
-				} else {
-					
-					for ( g in geometry.geometryGroups ) {
+				} else if ( geometry instanceof THREE.Geometry ) {
 
-						geometryGroup = geometry.geometryGroups[ g ];
+					for ( var i = 0,l = geometry.geometryGroupsList.length; i<l;i++ ) {
+	
+						geometryGroup = geometry.geometryGroupsList[ i ];
 						addBuffer( scene.__webglObjects, geometryGroup, object );
+						
 					}
 				}
 

+ 1097 - 0
utils/exporters/max/ThreeJSExporter_MorphTargets_v0.ms

@@ -0,0 +1,1097 @@
+-------------------------------------------------------------------------------------
+-- ThreeJSExporter.ms
+-- Exports geometry from 3ds max to Three.js models in ASCII JSON format v3
+-- By alteredq / http://alteredqualia.com
+--
+--	2014.06.25
+--	Add vertex export from each frame
+-------------------------------------------------------------------------------------
+function eav_attime obj t =
+(
+	local i
+	local s_out = ""
+	s_out = s_out as stringstream
+	
+	local zmesh = at time t (SnapshotAsMesh obj)
+	local n = zmesh.numverts
+	
+	local vrs_ar = #()
+	local v = [0,0,0]
+	
+	for i = 1 to n do
+	(
+		v = (GetVert zmesh i)
+		append vrs_ar v
+	)
+
+	for i = 1 to vrs_ar.count do
+	(
+		v = vrs_ar[i]
+		format "%, %, %" v.x v.z -v.y to:s_out
+		
+		if i < vrs_ar.count then
+		(
+			format ",  " to:s_out
+		)
+	)
+
+	return (s_out as string)
+)
+
+
+/*
+TODO 2014.06.25
+Export animation from modifiers
+*/
+function eav_get_range_from_trans_con obj &i_t1 &i_t2 =
+(
+--	Get keys range from Pos, rotation, scale controllers	
+	local i
+	local con
+	local t1min = 0, t2max = 0
+	
+	for i = 1 to 3 do
+	(
+		con = obj.controller[i]
+		
+		format "\nController: %" obj.controller[i].name
+		format " (keys count: %)" con.keys.count
+		
+		if con.keys.count == 0 then
+		(
+			continue
+		)
+		
+		t1 = con.keys[1].time.frame as integer
+		t2 = (con.keys[con.keys.count].time.frame) as integer
+		
+		if i == 1 then
+		(
+			t1min = t1
+			t2max = t2
+		)
+		
+		if t1 < t1min then
+		(
+			t1min = t1
+		)
+		
+		if t2 > t2max then
+		(
+			t2max = t2
+		)
+	)
+	
+	i_t1 = t1min
+	i_t2 = t2max
+	
+	if( i_t1 == 0 )and( i_t2 == 0 )then
+	(
+		return(false)
+	)
+	else
+	(
+		return(true)
+	)
+)
+
+function eav_get_range_from_mods_con obj &i_t1 &i_t2 =
+(
+	local i
+	local cmod, mod_con
+	local props, pr
+	local t1min = 0, t2max = 0
+	
+--	format "\n\nModifiers:\n"
+	
+	for i = 1 to obj.modifiers.count do
+	(
+		cmod = obj.modifiers[i]
+		
+	--	format "\n%: \"%\" (%)\n" i (cmod.name) (classof cmod)
+		
+		props = getpropnames cmod
+		
+		for pr in props do
+		(
+			mod_con = (getPropertyController cmod pr)
+			
+			if mod_con == undefined then
+			(
+				continue
+			)
+			
+			if mod_con.keys.count <= 0 then
+			(
+				continue
+			)
+			
+		--	format "\t%\t(keys: %)\n" pr (mod_con.keys.count)
+			
+			t1 = mod_con.keys[1].time.frame as integer
+			t2 = (mod_con.keys[mod_con.keys.count].time.frame) as integer
+			
+			if i == 1 then
+			(
+				t1min = t1
+				t2max = t2
+			)
+			
+			if t1 < t1min then
+			(
+				t1min = t1
+			)
+			
+			if t2 > t2max then
+			(
+				t2max = t2
+			)
+		)
+	)
+	
+	i_t1 = t1min
+	i_t2 = t2max
+	
+	if( i_t1 == 0 )and( i_t2 == 0 )then
+	(
+		return(false)
+	)
+	else
+	(
+		return(true)
+	)
+	
+)
+
+function eav_exp_obj obj ostream =
+(
+	local i, t1, t2, t1_m, t2_m
+	local b_ran_set = false
+	local b_ran_mod_set = false
+	format "\n\n-----------------------------\nObject: \"%\"\n" obj.name
+
+	-- Total range:
+/*	local frames_num = animationRange.end.frame - animationRange.start.frame
+	frames_num = frames_num as integer
+*/
+	
+	-- Range detection between keys:
+	b_ran_set = eav_get_range_from_trans_con obj &t1 &t2
+	b_ran_mod_set = eav_get_range_from_mods_con obj &t1_m &t2_m
+	
+	format "\n\nKey ranges detected:\n"
+	format "  transform: (% to %) - %\n" t1 t2 b_ran_set
+	format "  modifiers: (% to %) - %\n" t1_m t2_m b_ran_mod_set
+	
+	if b_ran_set and b_ran_mod_set then
+	(
+	--	format "\nAll ranges set - compare\n"
+		-- Set smallest first key, and latest final key
+		if t1_m < t1 then
+		(
+			t1 = t1_m
+		)
+		
+		if t2_m > t2 then
+		(
+			t2 = t2_m
+		)
+	)
+	else if( not b_ran_set )and( b_ran_mod_set )then
+	(
+	--	format "\nTrans range not set\n"
+		t1 = t1_m
+		t2 = t2_m
+	)
+	else if( not b_ran_mod_set )and( b_ran_set )then
+	(
+	--	format "\nmods range not set\n"
+		-- all values t1, t2 - save in initial state
+	)
+	else if( not b_ran_set )and( not b_ran_mod_set )then
+	(
+		format "\n  No key range set. Exit function\n"
+		return(false)
+	)
+	
+	format "\n  final range for export: (% to %)\n" t1 t2
+
+	---- Output
+	format "\n\"morphTargets\": [" to:ostream
+	
+	for i = t1 to t2 do
+	(
+		format "\n{\"name\": \"FRAME000\", \"vertices\": [" to:ostream
+		format "%]}" (eav_attime obj i) to:ostream
+		
+		if i < t2 then
+		(
+			format "," to:ostream
+		)
+	)
+	format " ],\n\n" to:ostream
+	
+	format "\n\n\"morphColors\": [],\n\n\n" to:ostream
+)
+
+function exp_anim_verts_sel ostream =
+(
+	Clearlistener()
+	format "\n---- Export verts:\n"
+	
+	for obj in selection do
+	(
+		if superclassof obj != geometryclass then continue
+		eav_exp_obj obj ostream
+	)
+	format "\n----\n"
+)
+----
+
+
+
+
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+rollout ThreeJSExporter "ThreeJSExporter"
+(
+	-- Variables
+
+	local ostream,
+	headerFormat = "\"metadata\":{\"sourceFile\": \"%\",\"generatedBy\": \"3ds max ThreeJSExporter\",\"formatVersion\": 3.1,\"vertices\": %,\"normals\": %,\"colors\": %,\"uvs\": %,\"triangles\": %,\"materials\": %},",
+
+	vertexFormat = "%,%,%",
+
+	vertexNormalFormat = "%,%,%",
+	UVFormat = "%,%",
+
+	triFormat = "%,%,%,%",
+	triUVFormat = "%,%,%,%,%,%,%",
+	triNFormat = "%,%,%,%,%,%,%",
+	triUVNFormat = "%,%,%,%,%,%,%,%,%,%",
+
+	footerFormat = "}"
+
+	-------------------------------------------------------------------------------------
+	-- User interface
+
+	group "ThreeJSExporter  v0.8"
+	(
+		label msg "Exports selected meshes in Three.js ascii JSON format" align:#left
+		hyperLink lab1 "Original source at GitHub" address:"https://github.com/alteredq/three.js/blob/master/utils/exporters/max/ThreeJSExporter.ms" color:(color 255 120 0) align:#left
+
+		label dummy1 "--------------------------------------------------------" align:#left
+
+		checkbox exportColor "Export vertex colors" checked:false enabled:true
+		checkbox exportUv "Export uvs" checked:true enabled:true
+		checkbox exportNormal "Export normals" checked:true enabled:true
+		checkbox smoothNormal "Use vertex normals" checked:false enabled:true
+
+		label dummy2 "--------------------------------------------------------" align:#left
+
+		checkbox flipYZ "Flip YZ" checked:true enabled:true
+		checkbox flipUV "Flip UV" checked:false enabled:true
+		checkbox flipFace "Flip all faces" checked:false enabled:true
+		checkbox autoflipFace "Try fixing flipped faces" checked:false enabled:true
+
+		label dummy3 "--------------------------------------------------------" align:#left
+		
+		checkbox cb_exp_mt "Export Morph Targets" checked:true enabled:true
+		
+		label dummy4 "--------------------------------------------------------" align:#left
+		button btn_export "Export selected objects"
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Dump vertices
+	function DumpVertices src =
+	(
+		Format "\"vertices\": [" to:ostream
+		num = src.count
+
+		if num > 0 then
+		(
+			for i = 1 to num do
+			(
+				vert = src[i]
+
+				if flipYZ.checked then
+				(
+					x = vert.x
+					y = vert.z
+					z = vert.y
+
+					z *= -1
+				)
+				else
+				(
+					x = vert.x
+					y = vert.y
+					z = vert.z
+				)
+
+				Format vertexFormat x y z to:ostream
+				if i < num then Format "," to:ostream
+			)
+		)
+		Format "],\n\n" to:ostream
+	)
+
+	----	2014.06.25	16:15
+	function dump_morph_targets =
+	(
+		Clearlistener()
+		format "\n---- dump_morph_targets():\n"
+		
+		if not cb_exp_mt.state then
+		(
+			format "\nNot checked\n"
+			return()
+		)
+		
+		exp_anim_verts_sel ostream
+
+		format "\n----\n"
+	)
+	
+	
+	-------------------------------------------------------------------------------------
+	-- Dump colors
+	function DumpColors src useColors =
+	(
+		Format "\"colors\": [" to:ostream
+		num = src.count
+
+		if num > 0 and useColors then
+		(
+			for i = 1 to num do
+			(
+				col = src[i]
+
+				r = col.r as Integer
+				g = col.g as Integer
+				b = col.b as Integer
+
+				hexNum = ( bit.shift r 16 ) + ( bit.shift g 8 ) + b
+
+				-- hexColor = formattedPrint hexNum format:"#x"
+				-- Format "%" hexColor to:ostream
+
+				decColor = formattedPrint hexNum format:"#d"
+				Format "%" decColor to:ostream
+
+				if i < num then Format "," to:ostream
+			)
+		)
+		Format "],\n\n" to:ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Dump normals
+	function DumpNormals src =
+	(
+		Format "\"normals\": [" to:ostream
+		num = src.count
+
+		if num > 0 and exportNormal.checked then
+		(
+			for i = 1 to num do
+			(
+				normal = src[i]
+				normal = normalize normal as point3
+
+				if flipYZ.checked then
+				(
+					x = normal.x
+					y = normal.z
+					z = normal.y
+
+					z *= -1
+				)
+				else
+				(
+					x = normal.x
+					y = normal.y
+					z = normal.z
+				)
+
+				Format vertexNormalFormat x y z to:ostream
+				if i < num then Format "," to:ostream
+			)
+		)
+		Format "],\n\n" to:ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Dump uvs
+	function DumpUvs src =
+	(
+		Format "\"uvs\": [[" to:ostream
+		num = src.count
+
+		if num > 0 and exportUv.checked then
+		(
+			for i = 1 to num do
+			(
+				uvw = src[i]
+
+				u = uvw.x
+
+				if flipUV.checked then
+				(
+					v = 1 - uvw.y
+				)
+				else
+				(
+					v = uvw.y
+				)
+
+				Format UVFormat u v to:ostream
+				if i < num then Format "," to:ostream
+			)
+		)
+		Format "]],\n\n" to:ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Dump faces
+	function DumpFaces src useColors =
+	(
+		Format "\"faces\": [" to:ostream
+		num = src.count
+
+		if num > 0 then
+		(
+			for i = 1 to num do
+			(
+				zface = src[i]
+
+				fv  = zface[1]
+				fuv = zface[2]
+				m   = zface[3] - 1
+				fc  = zface[4]
+
+				needsFlip = zface[5]
+
+				isTriangle = true
+				hasMaterial = true
+				hasFaceUvs = false
+				hasFaceVertexUvs = ((classof fuv == Point3) and exportUv.checked)
+				hasFaceNormals = false
+				hasFaceVertexNormals = (exportNormal.checked)
+				hasFaceColors = false
+				hasFaceVertexColors = ((classof fc == Point3) and useColors)
+
+				faceType = 0
+				faceType = bit.set faceType 1 (not isTriangle)
+				faceType = bit.set faceType 2 hasMaterial
+				faceType = bit.set faceType 3 hasFaceUvs
+				faceType = bit.set faceType 4 hasFaceVertexUvs
+				faceType = bit.set faceType 5 hasFaceNormals
+				faceType = bit.set faceType 6 hasFaceVertexNormals
+				faceType = bit.set faceType 7 hasFaceColors
+				faceType = bit.set faceType 8 hasFaceVertexColors
+
+				if i > 1 then
+				(
+					Format "," faceType to:ostream
+				)
+
+				Format "%" faceType to:ostream
+
+				if isTriangle then
+				(
+					va = (fv.x - 1) as Integer
+					vb = (fv.y - 1) as Integer
+					vc = (fv.z - 1) as Integer
+
+					if flipFace.checked or needsFlip then
+					(
+						tmp = vb
+						vb = vc
+						vc = tmp
+					)
+
+					Format ",%,%,%" va vb vc to:ostream
+
+					if hasMaterial then
+					(
+						Format ",%" m to:ostream
+					)
+
+					if hasFaceVertexUvs then
+					(
+						ua = (fuv.x - 1) as Integer
+						ub = (fuv.y - 1) as Integer
+						uc = (fuv.z - 1) as Integer
+
+						if flipFace.checked or needsFlip then
+						(
+							tmp = ub
+							ub = uc
+							uc = tmp
+						)
+						Format ",%,%,%" ua ub uc to:ostream
+					)
+
+					if hasFaceVertexNormals then
+					(
+						if smoothNormal.checked then
+						(
+							-- normals have the same indices as vertices
+							na = va
+							nb = vb
+							nc = vc
+						)
+						else
+						(
+							-- normals have the same indices as face
+							na = i - 1
+							nb = na
+							nc = na
+						)
+
+						if flipFace.checked or needsFlip then
+						(
+							tmp = nb
+							nb = nc
+							nc = tmp
+						)
+						Format ",%,%,%" na nb nc to:ostream
+					)
+
+					if hasFaceVertexColors then
+					(
+						ca = (fc.x - 1) as Integer
+						cb = (fc.y - 1) as Integer
+						cc = (fc.z - 1) as Integer
+
+						if flipFace.checked or needsFlip then
+						(
+							tmp = cb
+							cb = cc
+							cc = tmp
+						)
+						Format ",%,%,%" ca cb cc to:ostream
+					)
+				)
+			)
+		)
+		Format "]\n\n" to:ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Dump color
+
+	function DumpColor pcolor label =
+	(
+		r = pcolor.r / 255
+		g = pcolor.g / 255
+		b = pcolor.b / 255
+
+		fr = formattedPrint r format:".4f"
+		fg = formattedPrint g format:".4f"
+		fb = formattedPrint b format:".4f"
+
+		Format "\"%\"  : [%, %, %],\n" label fr fg fb to:ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Dump map
+	function DumpMap pmap label =
+	(
+		if classof pmap == BitmapTexture then
+		(
+			bm = pmap.bitmap
+
+			if bm != undefined then
+			(
+				fname = filenameFromPath bm.filename
+				Format "\"%\"    : \"%\",\n" label fname to:ostream
+			)
+		)
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Export materials
+	function ExportMaterials zmaterials zcolors =
+	(
+		Format "\"materials\": [\n" to:ostream
+
+		totalMaterials = zmaterials.count
+
+		for i = 1 to totalMaterials do
+		(
+			mat = zmaterials[i]
+
+			Format "{\n" to:ostream
+
+			-- debug
+			Format "\"DbgIndex\" : %,\n" (i-1) to:ostream
+
+			if classof mat != BooleanClass then
+			(
+				useVertexColors = zcolors[i]
+
+				Format "\"DbgName\"  : \"%\",\n" mat.name to:ostream
+
+				-- colors
+				DumpColor mat.diffuse  "colorDiffuse"
+				DumpColor mat.ambient  "colorAmbient"
+				DumpColor mat.specular "colorSpecular"
+
+				t = mat.opacity / 100
+				s = mat.glossiness
+
+				Format "\"transparency\"  : %,\n" t to:ostream
+				Format "\"specularCoef\"  : %,\n" s to:ostream
+
+				-- maps
+				DumpMap mat.diffuseMap  "mapDiffuse"
+				DumpMap mat.ambientMap  "mapAmbient"
+				DumpMap mat.specularMap "mapSpecular"
+				DumpMap mat.bumpMap 	"mapBump"
+				DumpMap mat.opacityMap 	"mapAlpha"
+			)
+			else
+			(
+				useVertexColors = false
+				Format "\"DbgName\"  : \"%\",\n" "dummy" to:ostream
+				DumpColor red "colorDiffuse"
+			)
+
+			Format "\"vertexColors\" : %\n" useVertexColors to:ostream
+			Format "}" to:ostream
+
+			if ( i < totalMaterials ) then Format "," to:ostream
+			Format "\n\n" to:ostream
+		)
+		Format "],\n\n" to:ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Extract vertices from mesh
+	function ExtractVertices obj whereto =
+	(
+		n = obj.numVerts
+		for i = 1 to n do
+		(
+			v = GetVert obj i
+			append whereto v
+		)
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Extract vertex colors from mesh
+
+	function ExtractColors obj whereto =
+	(
+		nColors = GetNumCPVVerts obj
+
+		if nColors > 0 then
+		(
+			for i = 1 to nColors do
+			(
+				c = GetVertColor obj i
+				append whereto c
+			)
+		)
+	)
+
+
+	-------------------------------------------------------------------------------------
+	-- Extract normals from mesh
+
+	function ExtractNormals obj whereto needsFlip =
+	(
+		if smoothNormal.checked then
+		(
+			num = obj.numVerts
+
+			for i = 1 to num do
+			(
+				n = GetNormal obj i
+
+				if flipFace.checked or needsFlip then
+				(
+					n.x *= -1
+					n.y *= -1
+					n.z *= -1
+				)
+				append whereto n
+			)
+		)
+		else
+		(
+			num = obj.numFaces
+
+			for i = 1 to num do
+			(
+				n = GetFaceNormal obj i
+
+				if flipFace.checked or needsFlip then
+				(
+					n.x *= -1
+					n.y *= -1
+					n.z *= -1
+				)
+
+				append whereto n
+			)
+		)
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Extract uvs from mesh
+
+	function ExtractUvs obj whereto =
+	(
+		n = obj.numTVerts
+		for i = 1 to n do
+		(
+			v = GetTVert obj i
+			append whereto v
+		)
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Extract faces from mesh
+	function ExtractFaces objMesh objMaterial whereto allMaterials needsFlip hasVColors offsetVert offsetUv offsetColor =
+	(
+		n = objMesh.numFaces
+		hasUVs = objMesh.numTVerts > 0
+
+		useMultiMaterial = false
+		materialIDList = #()
+
+		materialClass = classof objMaterial
+
+		if materialClass == StandardMaterial then
+		(
+			fm = findItem allMaterials objMaterial
+		)
+		else if materialClass == MultiMaterial then
+		(
+			useMultiMaterial = true
+			for i = 1 to n do
+			(
+				mID = GetFaceMatID objMesh i
+				materialIndex = findItem objMaterial.materialIDList mID
+
+				if materialIndex > 0 then
+				(
+					subMaterial = objMaterial.materialList[materialIndex]
+
+					mMergedIndex = findItem allMaterials subMaterial
+
+					if mMergedIndex > 0 then
+					(
+						materialIDList[mID] = mMergedIndex
+					)
+					else
+					(
+						materialIDList[mID] = findItem allMaterials false
+					)
+				)
+				else
+				(
+					materialIDList[mID] = findItem allMaterials false
+				)
+			)
+		)
+		else
+		(
+			-- undefined material
+			fm = findItem allMaterials false
+		)
+
+		for i = 1 to n do
+		(
+			zface = #()
+
+			fv = GetFace objMesh i
+
+			fv.x += offsetVert
+			fv.y += offsetVert
+			fv.z += offsetVert
+
+			if useMultiMaterial then
+			(
+				mID = GetFaceMatID objMesh i
+				fm = materialIDList[mID]
+			)
+
+			if hasUVs then
+			(
+				fuv = GetTVFace objMesh i
+
+				fuv.x += offsetUv
+				fuv.y += offsetUv
+				fuv.z += offsetUv
+			)
+			else
+			(
+				fuv = false
+			)
+
+			if hasVColors then
+			(
+				fc = GetVCFace objMesh i
+
+				fc.x += offsetColor
+				fc.y += offsetColor
+				fc.z += offsetColor
+			)
+			else
+			(
+				fc = false
+			)
+
+			append zface fv
+			append zface fuv
+			append zface fm
+			append zface fc
+			append zface needsFlip
+
+			append whereto zface
+		)
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Extract materials from eventual multi-material
+	function ExtractMaterials objMesh objMaterial whereto wheretoColors zname hasVColors =
+	(
+		materialClass = classof objMaterial
+
+		if materialClass == StandardMaterial then
+		(
+			if ( findItem whereto objMaterial ) == 0 then
+			(
+				append whereto objMaterial
+				append wheretoColors hasVColors
+			)
+		)
+		else if materialClass == MultiMaterial then
+		(
+			n = objMesh.numFaces
+
+			for i = 1 to n do
+			(
+				mID = getFaceMatId objMesh i
+				materialIndex = findItem objMaterial.materialIDList mID
+
+				if materialIndex > 0 then
+				(
+					subMaterial = objMaterial.materialList[materialIndex]
+
+					if ( findItem whereto subMaterial ) == 0 then
+					(
+						append whereto subMaterial
+						append wheretoColors hasVColors
+					)
+				)
+			)
+		)
+		else
+		(
+			-- unknown or undefined material
+			append whereto false
+			append wheretoColors false
+		)
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Hack to figure out if normals are messed up
+	function NeedsFaceFlip node =
+	(
+		needsFlip = false
+		local tmp = Snapshot node
+		face_normal = normalize ( getfacenormal tmp 1 )
+		face = getface tmp 1
+
+		va = getvert tmp face[1]
+		vb = getvert tmp face[2]
+		vc = getvert tmp face[3]
+
+		computed_normal = normalize ( cross (vc - vb)  (va - vb) )
+		if distance computed_normal face_normal > 0.1 then needsFlip = true
+		delete tmp
+		return needsFlip
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Extract only things that either already are or can be converted to meshes
+	function ExtractMesh node =
+	(
+		if SuperClassOf node == GeometryClass then
+		(
+			needsFlip = false
+			hasVColors = false
+
+			zmesh = SnapshotAsMesh node
+
+			if autoflipFace.checked then
+			(
+				needsFlip = NeedsFaceFlip node
+			)
+
+			if exportColor.checked and ( getNumCPVVerts zmesh ) > 0 then
+			(
+				hasVColors = true
+			)
+			return #( zmesh, node.name, node.material, needsFlip, hasVColors )
+		)
+		-- Not geometry ... could be a camera, light, etc.
+		return #( false, node.name, 0, false, false )
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Export scene
+	function ExportScene =
+	(
+		-- Extract meshes
+		meshObjects = #()
+
+		mergedVertices = #()
+		mergedNormals = #()
+		mergedColors = #()
+
+		mergedUvs = #()
+		mergedFaces = #()
+
+		mergedMaterials = #()
+		mergedMaterialsColors = #()
+
+		sceneHasVColors = false
+
+		for obj in selection do
+		(
+			result = ExtractMesh obj
+			meshObj = result[1]
+
+			if ClassOf meshObj == TriMesh then
+			(
+				meshName     = result[2]
+				meshMaterial = result[3]
+				needsFlip    = result[4]
+				hasVColors   = result[5]
+
+				sceneHasVColors = sceneHasVColors or hasVColors
+
+				append meshObjects result
+
+				vertexOffset = mergedVertices.count
+				uvOffset = mergedUvs.count
+				colorOffset = mergedColors.count
+
+				ExtractMaterials meshObj meshMaterial mergedMaterials mergedMaterialsColors meshName hasVColors
+
+				ExtractVertices meshObj mergedVertices
+				ExtractNormals meshObj mergedNormals needsFlip
+				ExtractColors meshObj mergedColors
+
+				ExtractUvs meshObj mergedUvs
+
+				ExtractFaces meshObj meshMaterial mergedFaces mergedMaterials needsFlip hasVColors vertexOffset uvOffset colorOffset
+			)
+		)
+
+		totalVertices = mergedVertices.count
+		totalFaces = mergedFaces.count
+		totalMaterials = mergedMaterials.count
+
+		totalColors = 0
+		totalNormals = 0
+		totalUvs = 0
+
+		useColors = false
+
+		if sceneHasVColors and exportColor.checked then
+		(
+			totalColors = mergedColors.count
+			useColors = true
+		)
+
+		if exportNormal.checked then
+		(
+			totalNormals = mergedNormals.count
+		)
+
+		if exportUv.checked then
+		(
+			totalUvs = mergedUvs.count
+		)
+
+
+		-- Dump objects (debug)
+		-- Format "// Source objects:\n\n" to:ostream
+		-- i = 0
+		-- for obj in meshObjects do
+		-- (
+		-- 	meshName = obj[2]
+		-- 	Format "// %: %\n" i meshName to:ostream
+		-- 	i += 1
+		-- )
+
+		-- Dump model
+		Format "{\n\n" to:ostream
+
+		-- Dump header
+		Format headerFormat maxFileName totalVertices totalNormals totalColors totalUvs totalFaces totalMaterials to:ostream
+
+		-- Dump all materials in the scene
+		ExportMaterials mergedMaterials mergedMaterialsColors
+
+		-- Dump merged data from all selected geometries
+		DumpVertices mergedVertices
+		
+		----	2014.06.25	16:14
+		dump_morph_targets()
+		----
+		
+		DumpNormals mergedNormals
+		DumpColors mergedColors useColors
+		DumpUvs mergedUvs
+		DumpFaces mergedFaces useColors
+
+		-- Dump footer
+		Format footerFormat to:ostream
+	)
+
+
+	-------------------------------------------------------------------------------------
+	-- Open and prepare a file handle for writing
+	function GetSaveFileStream =
+	(
+		zname = getFilenameFile maxFileName
+		zname += ".js"
+
+		fname = GetSaveFileName filename:zname types:"JavaScript file (*.js)|*.js|All Files(*.*)|*.*|"
+		if fname == undefined then
+		(
+			return undefined
+		)
+
+		ostream = CreateFile fname
+		if ostream == undefined then
+		(
+			MessageBox "Couldn't open file for writing !"
+			return undefined
+		)
+		return ostream
+	)
+
+	-------------------------------------------------------------------------------------
+	-- Export button click handler
+	on btn_export pressed do
+	(
+		ostream = GetSaveFileStream()
+		if ostream != undefined then
+		(
+			ExportScene()
+			close ostream
+		)
+	)
+)
+createDialog ThreeJSExporter width:300