ソースを参照

DDS: Support for RGB, Luminance and cube file format

David Catuhe 11 年 前
コミット
1f9cf60c26

+ 1 - 1
Babylon/Lights/Shadows/babylon.shadowGenerator.js

@@ -129,7 +129,7 @@
                 attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
                 attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
                 defines.push("#define BONES");
-                defines.push("#define BonesPerMesh " + mesh.skeleton.bones.length);
+                defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
             }
 
             // Instances

+ 1 - 1
Babylon/Lights/Shadows/babylon.shadowGenerator.ts

@@ -139,7 +139,7 @@
                 attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
                 attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
                 defines.push("#define BONES");
-                defines.push("#define BonesPerMesh " + mesh.skeleton.bones.length);
+                defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
             }
 
             // Instances

+ 1 - 1
Babylon/Materials/babylon.standardMaterial.js

@@ -249,7 +249,7 @@ var BABYLON;
                     attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
                     attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
                     defines.push("#define BONES");
-                    defines.push("#define BonesPerMesh " + mesh.skeleton.bones.length);
+                    defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
                     defines.push("#define BONES4");
                     optionalDefines.push(defines[defines.length - 1]);
                 }

+ 1 - 1
Babylon/Materials/babylon.standardMaterial.ts

@@ -252,7 +252,7 @@
                     attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
                     attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
                     defines.push("#define BONES");
-                    defines.push("#define BonesPerMesh " + mesh.skeleton.bones.length);
+                    defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
                     defines.push("#define BONES4");
                     optionalDefines.push(defines[defines.length - 1]);
                 }

+ 4 - 1
Babylon/Materials/textures/babylon.videoTexture.js

@@ -19,7 +19,10 @@ var BABYLON;
             this.wrapU = BABYLON.Texture.WRAP_ADDRESSMODE;
             this.wrapV = BABYLON.Texture.WRAP_ADDRESSMODE;
 
-            this._texture = scene.getEngine().createDynamicTexture(size, size, generateMipMaps, samplingMode);
+            var requiredWidth = size.width || size;
+            var requiredHeight = size.height || size;
+
+            this._texture = scene.getEngine().createDynamicTexture(requiredWidth, requiredHeight, generateMipMaps, samplingMode);
             var textureSize = this.getSize();
 
             this.video = document.createElement("video");

+ 5 - 2
Babylon/Materials/textures/babylon.videoTexture.ts

@@ -5,7 +5,7 @@
         private _autoLaunch = true;
         private _lastUpdate: number;
 
-        constructor(name: string, urls: string[], size: number, scene: Scene, generateMipMaps: boolean, invertY: boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
+        constructor(name: string, urls: string[], size: any, scene: Scene, generateMipMaps: boolean, invertY: boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
             super(null, scene, !generateMipMaps, invertY);
 
             this.name = name;
@@ -13,7 +13,10 @@
             this.wrapU = BABYLON.Texture.WRAP_ADDRESSMODE;
             this.wrapV = BABYLON.Texture.WRAP_ADDRESSMODE;
 
-            this._texture = scene.getEngine().createDynamicTexture(size, size, generateMipMaps, samplingMode);
+            var requiredWidth = size.width || size;
+            var requiredHeight = size.height || size;
+
+            this._texture = scene.getEngine().createDynamicTexture(requiredWidth, requiredHeight, generateMipMaps, samplingMode);
             var textureSize = this.getSize();
 
             this.video = document.createElement("video");

+ 123 - 32
Babylon/Tools/babylon.tools.dds.js

@@ -40,6 +40,15 @@
 
         var off_pfFlags = 20;
         var off_pfFourCC = 21;
+        var off_RGBbpp = 22;
+        var off_RMask = 23;
+        var off_GMask = 24;
+        var off_BMask = 25;
+        var off_AMask = 26;
+        var off_caps1 = 27;
+        var off_caps2 = 28;
+
+        ;
 
         var DDSTools = (function () {
             function DDSTools() {
@@ -55,11 +64,65 @@
                 return {
                     width: header[off_width],
                     height: header[off_height],
-                    mipmapCount: mipmapCount
+                    mipmapCount: mipmapCount,
+                    isFourCC: (header[off_pfFlags] & DDPF_FOURCC) === DDPF_FOURCC,
+                    isRGB: (header[off_pfFlags] & DDPF_RGB) === DDPF_RGB,
+                    isLuminance: (header[off_pfFlags] & DDPF_LUMINANCE) === DDPF_LUMINANCE,
+                    isCube: (header[off_caps2] & DDSCAPS2_CUBEMAP) === DDSCAPS2_CUBEMAP
                 };
             };
 
-            DDSTools.UploadDDSLevels = function (gl, ext, arrayBuffer, loadMipmaps) {
+            DDSTools.GetRGBAArrayBuffer = function (width, height, dataOffset, dataLength, arrayBuffer) {
+                var byteArray = new Uint8Array(dataLength);
+                var srcData = new Uint8Array(arrayBuffer);
+                var index = 0;
+                for (var y = height - 1; y >= 0; y--) {
+                    for (var x = 0; x < width; x++) {
+                        var srcPos = dataOffset + (x + y * width) * 4;
+                        byteArray[index + 2] = srcData[srcPos];
+                        byteArray[index + 1] = srcData[srcPos + 1];
+                        byteArray[index] = srcData[srcPos + 2];
+                        byteArray[index + 3] = srcData[srcPos + 3];
+                        index += 4;
+                    }
+                }
+
+                return byteArray;
+            };
+
+            DDSTools.GetRGBArrayBuffer = function (width, height, dataOffset, dataLength, arrayBuffer) {
+                var byteArray = new Uint8Array(dataLength);
+                var srcData = new Uint8Array(arrayBuffer);
+                var index = 0;
+                for (var y = height - 1; y >= 0; y--) {
+                    for (var x = 0; x < width; x++) {
+                        var srcPos = dataOffset + (x + y * width) * 3;
+                        byteArray[index + 2] = srcData[srcPos];
+                        byteArray[index + 1] = srcData[srcPos + 1];
+                        byteArray[index] = srcData[srcPos + 2];
+                        index += 3;
+                    }
+                }
+
+                return byteArray;
+            };
+
+            DDSTools.GetLuminanceArrayBuffer = function (width, height, dataOffset, dataLength, arrayBuffer) {
+                var byteArray = new Uint8Array(dataLength);
+                var srcData = new Uint8Array(arrayBuffer);
+                var index = 0;
+                for (var y = height - 1; y >= 0; y--) {
+                    for (var x = 0; x < width; x++) {
+                        var srcPos = dataOffset + (x + y * width);
+                        byteArray[index] = srcData[srcPos];
+                        index++;
+                    }
+                }
+
+                return byteArray;
+            };
+
+            DDSTools.UploadDDSLevels = function (gl, ext, arrayBuffer, info, loadMipmaps, faces) {
                 var header = new Int32Array(arrayBuffer, 0, headerLengthInt), fourCC, blockBytes, internalFormat, width, height, dataLength, dataOffset, byteArray, mipmapCount, i;
 
                 if (header[off_magic] != DDS_MAGIC) {
@@ -67,28 +130,30 @@
                     return;
                 }
 
-                if ((header[off_pfFlags] & DDPF_FOURCC) !== DDPF_FOURCC) {
-                    BABYLON.Tools.Error("Unsupported format, must contain a FourCC code");
+                if (!info.isFourCC && !info.isRGB && !info.isLuminance) {
+                    BABYLON.Tools.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");
                     return;
                 }
 
-                fourCC = header[off_pfFourCC];
-                switch (fourCC) {
-                    case FOURCC_DXT1:
-                        blockBytes = 8;
-                        internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT;
-                        break;
-                    case FOURCC_DXT3:
-                        blockBytes = 16;
-                        internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT3_EXT;
-                        break;
-                    case FOURCC_DXT5:
-                        blockBytes = 16;
-                        internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
-                        break;
-                    default:
-                        console.error("Unsupported FourCC code:", Int32ToFourCC(fourCC));
-                        return;
+                if (info.isFourCC) {
+                    fourCC = header[off_pfFourCC];
+                    switch (fourCC) {
+                        case FOURCC_DXT1:
+                            blockBytes = 8;
+                            internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+                            break;
+                        case FOURCC_DXT3:
+                            blockBytes = 16;
+                            internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+                            break;
+                        case FOURCC_DXT5:
+                            blockBytes = 16;
+                            internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+                            break;
+                        default:
+                            console.error("Unsupported FourCC code:", Int32ToFourCC(fourCC));
+                            return;
+                    }
                 }
 
                 mipmapCount = 1;
@@ -96,17 +161,43 @@
                     mipmapCount = Math.max(1, header[off_mipmapCount]);
                 }
 
-                width = header[off_width];
-                height = header[off_height];
-                dataOffset = header[off_size] + 4;
-
-                for (i = 0; i < mipmapCount; ++i) {
-                    dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
-                    byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
-                    gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray);
-                    dataOffset += dataLength;
-                    width *= 0.5;
-                    height *= 0.5;
+                var bpp = header[off_RGBbpp];
+
+                for (var face = 0; face < faces; face++) {
+                    var sampler = faces == 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
+
+                    width = header[off_width];
+                    height = header[off_height];
+                    dataOffset = header[off_size] + 4;
+
+                    for (i = 0; i < mipmapCount; ++i) {
+                        if (info.isRGB) {
+                            if (bpp == 24) {
+                                dataLength = width * height * 3;
+                                byteArray = DDSTools.GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                                gl.texImage2D(sampler, i, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, byteArray);
+                            } else {
+                                dataLength = width * height * 4;
+                                byteArray = DDSTools.GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                                gl.texImage2D(sampler, i, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);
+                            }
+                        } else if (info.isLuminance) {
+                            dataLength = width * height;
+                            byteArray = DDSTools.GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                            gl.texImage2D(sampler, i, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, byteArray);
+                        } else {
+                            dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
+                            byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
+                            gl.compressedTexImage2D(sampler, i, internalFormat, width, height, 0, byteArray);
+                        }
+                        dataOffset += dataLength;
+                        width *= 0.5;
+                        height *= 0.5;
+
+                        if (width <= 2 || height <= 2) {
+                            break;
+                        }
+                    }
                 }
             };
             return DDSTools;

+ 117 - 18
Babylon/Tools/babylon.tools.dds.ts

@@ -67,9 +67,26 @@
 
     var off_pfFlags = 20;
     var off_pfFourCC = 21;
+    var off_RGBbpp = 22;
+    var off_RMask = 23;
+    var off_GMask = 24;
+    var off_BMask = 25;
+    var off_AMask = 26;
+    var off_caps1 = 27;
+    var off_caps2 = 28;
+
+    export interface DDSInfo {
+        width: number;
+        height: number;
+        mipmapCount: number;
+        isFourCC: boolean;
+        isRGB: boolean;
+        isLuminance: boolean;
+        isCube: boolean
+    };
 
     export class DDSTools {
-        public static GetDDSInfo(arrayBuffer: any): { width: number; height: number; mipmapCount: number } {
+        public static GetDDSInfo(arrayBuffer: any): DDSInfo {
             var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
 
             var mipmapCount = 1;
@@ -80,11 +97,65 @@
             return {
                 width: header[off_width],
                 height: header[off_height],
-                mipmapCount: mipmapCount
+                mipmapCount: mipmapCount,
+                isFourCC: (header[off_pfFlags] & DDPF_FOURCC) === DDPF_FOURCC,
+                isRGB: (header[off_pfFlags] & DDPF_RGB) === DDPF_RGB,
+                isLuminance: (header[off_pfFlags] & DDPF_LUMINANCE) === DDPF_LUMINANCE,
+                isCube: (header[off_caps2] & DDSCAPS2_CUBEMAP) === DDSCAPS2_CUBEMAP
             };
         }
 
-        public static UploadDDSLevels(gl: WebGLRenderingContext, ext: any, arrayBuffer: any, loadMipmaps?: boolean): void {
+        private static GetRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
+            var byteArray = new Uint8Array(dataLength);
+            var srcData = new Uint8Array(arrayBuffer);
+            var index = 0;
+            for (var y = height - 1; y >= 0; y--) {
+                for (var x = 0; x < width; x++) {
+                    var srcPos = dataOffset + (x + y * width) * 4;
+                    byteArray[index + 2] = srcData[srcPos];
+                    byteArray[index + 1] = srcData[srcPos + 1];
+                    byteArray[index] = srcData[srcPos + 2];
+                    byteArray[index + 3] = srcData[srcPos + 3];
+                    index += 4;
+                }
+            }
+
+            return byteArray;
+        }
+
+        private static GetRGBArrayBuffer(width: number, height: number, dataOffset:number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {            
+            var byteArray = new Uint8Array(dataLength);
+            var srcData = new Uint8Array(arrayBuffer);
+            var index = 0;
+            for (var y = height - 1; y >= 0; y--) {
+                for (var x = 0; x < width; x++) {
+                    var srcPos = dataOffset + (x + y * width) * 3;
+                    byteArray[index + 2] = srcData[srcPos];
+                    byteArray[index + 1] = srcData[srcPos + 1];
+                    byteArray[index] = srcData[srcPos + 2];
+                    index += 3;
+                }
+            }
+
+            return byteArray;
+        }
+
+        private static GetLuminanceArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
+            var byteArray = new Uint8Array(dataLength);
+            var srcData = new Uint8Array(arrayBuffer);
+            var index = 0;
+            for (var y = height - 1; y >= 0; y--) {
+                for (var x = 0; x < width; x++) {
+                    var srcPos = dataOffset + (x + y * width);
+                    byteArray[index] = srcData[srcPos];
+                    index++;
+                }
+            }
+
+            return byteArray;
+        }
+
+        public static UploadDDSLevels(gl: WebGLRenderingContext, ext: any, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number): void {
             var header = new Int32Array(arrayBuffer, 0, headerLengthInt),
                 fourCC, blockBytes, internalFormat,
                 width, height, dataLength, dataOffset,
@@ -95,13 +166,14 @@
                 return;
             }
 
-            if ((header[off_pfFlags] & DDPF_FOURCC) !== DDPF_FOURCC) {
-                Tools.Error("Unsupported format, must contain a FourCC code");
+            if (!info.isFourCC && !info.isRGB && !info.isLuminance) {
+                Tools.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");
                 return;
             }
 
-            fourCC = header[off_pfFourCC];
-            switch (fourCC) {
+            if (info.isFourCC) {
+                fourCC = header[off_pfFourCC];
+                switch (fourCC) {
                 case FOURCC_DXT1:
                     blockBytes = 8;
                     internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT;
@@ -117,6 +189,7 @@
                 default:
                     console.error("Unsupported FourCC code:", Int32ToFourCC(fourCC));
                     return;
+                }
             }
 
             mipmapCount = 1;
@@ -124,17 +197,43 @@
                 mipmapCount = Math.max(1, header[off_mipmapCount]);
             }
 
-            width = header[off_width];
-            height = header[off_height];
-            dataOffset = header[off_size] + 4;
-
-            for (i = 0; i < mipmapCount; ++i) {
-                dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
-                byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
-                gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray);
-                dataOffset += dataLength;
-                width *= 0.5;
-                height *= 0.5;
+            var bpp = header[off_RGBbpp];
+
+            for (var face = 0; face < faces; face++) {
+                var sampler = faces == 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
+
+                width = header[off_width];
+                height = header[off_height];
+                dataOffset = header[off_size] + 4;
+
+                for (i = 0; i < mipmapCount; ++i) {
+                    if (info.isRGB) {
+                        if (bpp == 24) {
+                            dataLength = width * height * 3;
+                            byteArray = DDSTools.GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                            gl.texImage2D(sampler, i, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, byteArray);
+                        } else { // 32
+                            dataLength = width * height * 4;
+                            byteArray = DDSTools.GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                            gl.texImage2D(sampler, i, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);
+                        }
+                    } else if (info.isLuminance) {
+                        dataLength = width * height;
+                        byteArray = DDSTools.GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                        gl.texImage2D(sampler, i, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, byteArray);
+                    } else {
+                        dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
+                        byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
+                        gl.compressedTexImage2D(sampler, i, internalFormat, width, height, 0, byteArray);
+                    }
+                    dataOffset += dataLength;
+                    width *= 0.5;
+                    height *= 0.5;
+
+                    if (width <= 2 || height <= 2) {
+                        break;
+                    }
+                }
             }
         }
     }

+ 64 - 31
Babylon/babylon.engine.js

@@ -932,10 +932,10 @@
                 BABYLON.Tools.LoadFile(url, function (data) {
                     var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
-                    var loadMipmap = info.mipmapCount > 1 && !noMipmap;
+                    var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
-                    prepareWebGLTexture(texture, _this._gl, scene, info.width, info.height, invertY, !loadMipmap, true, function () {
-                        BABYLON.Internals.DDSTools.UploadDDSLevels(_this._gl, _this.getCaps().s3tc, data, loadMipmap);
+                    prepareWebGLTexture(texture, _this._gl, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, function () {
+                        BABYLON.Internals.DDSTools.UploadDDSLevels(_this._gl, _this.getCaps().s3tc, data, info, loadMipmap, 1);
                     }, samplingMode);
                 }, null, scene.database, true);
             } else {
@@ -1110,43 +1110,76 @@
             texture.references = 1;
             this._loadedTexturesCache.push(texture);
 
-            cascadeLoad(rootUrl, 0, [], scene, function (imgs) {
-                var width = getExponantOfTwo(imgs[0].width, _this._caps.maxCubemapTextureSize);
-                var height = width;
+            var extension = rootUrl.substr(rootUrl.length - 4, 4).toLowerCase();
+            var isDDS = this.getCaps().s3tc && (extension === ".dds");
 
-                _this._workingCanvas.width = width;
-                _this._workingCanvas.height = height;
+            if (isDDS) {
+                BABYLON.Tools.LoadFile(rootUrl, function (data) {
+                    var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
-                var faces = [
-                    gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
-                    gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
-                ];
+                    var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
-                gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
 
-                for (var index = 0; index < faces.length; index++) {
-                    _this._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
-                    gl.texImage2D(faces[index], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, _this._workingCanvas);
-                }
+                    BABYLON.Internals.DDSTools.UploadDDSLevels(_this._gl, _this.getCaps().s3tc, data, info, loadMipmap, 6);
 
-                if (!noMipmap) {
-                    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                }
+                    if (!noMipmap && !info.isFourCC && info.mipmapCount == 1) {
+                        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+                    }
 
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+
+                    _this._activeTexturesCache = [];
+
+                    texture._width = info.width;
+                    texture._height = info.height;
+                    texture.isReady = true;
+                });
+            } else {
+                cascadeLoad(rootUrl, 0, [], scene, function (imgs) {
+                    var width = getExponantOfTwo(imgs[0].width, _this._caps.maxCubemapTextureSize);
+                    var height = width;
 
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+                    _this._workingCanvas.width = width;
+                    _this._workingCanvas.height = height;
 
-                _this._activeTexturesCache = [];
+                    var faces = [
+                        gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
+                        gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
+                    ];
 
-                texture._width = width;
-                texture._height = height;
-                texture.isReady = true;
-            }, extensions);
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
+
+                    for (var index = 0; index < faces.length; index++) {
+                        _this._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
+                        gl.texImage2D(faces[index], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, _this._workingCanvas);
+                    }
+
+                    if (!noMipmap) {
+                        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+                    }
+
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+
+                    _this._activeTexturesCache = [];
+
+                    texture._width = width;
+                    texture._height = height;
+                    texture.isReady = true;
+                }, extensions);
+            }
 
             return texture;
         };

+ 64 - 31
Babylon/babylon.engine.ts

@@ -963,10 +963,10 @@
                 BABYLON.Tools.LoadFile(url, data => {
                     var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
-                    var loadMipmap = info.mipmapCount > 1 && !noMipmap;
+                    var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
-                    prepareWebGLTexture(texture, this._gl, scene, info.width, info.height, invertY, !loadMipmap, true, () => {
-                        Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, loadMipmap);
+                    prepareWebGLTexture(texture, this._gl, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, () => {
+                        Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, info, loadMipmap, 1);
                     }, samplingMode);
                 }, null, scene.database, true);
             } else {
@@ -1139,43 +1139,76 @@
             texture.references = 1;
             this._loadedTexturesCache.push(texture);
 
-            cascadeLoad(rootUrl, 0, [], scene, imgs => {
-                var width = getExponantOfTwo(imgs[0].width, this._caps.maxCubemapTextureSize);
-                var height = width;
+            var extension = rootUrl.substr(rootUrl.length - 4, 4).toLowerCase();
+            var isDDS = this.getCaps().s3tc && (extension === ".dds");
 
-                this._workingCanvas.width = width;
-                this._workingCanvas.height = height;
+            if (isDDS) {
+                BABYLON.Tools.LoadFile(rootUrl, data => {
+                    var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
-                var faces = [
-                    gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
-                    gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
-                ];
+                    var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
-                gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
 
-                for (var index = 0; index < faces.length; index++) {
-                    this._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
-                    gl.texImage2D(faces[index], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._workingCanvas);
-                }
+                    Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, info, loadMipmap, 6);
 
-                if (!noMipmap) {
-                    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                }
+                    if (!noMipmap && !info.isFourCC && info.mipmapCount == 1) {
+                        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+                    }
 
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+
+                    this._activeTexturesCache = [];
+
+                    texture._width = info.width;
+                    texture._height = info.height;
+                    texture.isReady = true;
+                });
+            } else {
+                cascadeLoad(rootUrl, 0, [], scene, imgs => {
+                    var width = getExponantOfTwo(imgs[0].width, this._caps.maxCubemapTextureSize);
+                    var height = width;
 
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+                    this._workingCanvas.width = width;
+                    this._workingCanvas.height = height;
 
-                this._activeTexturesCache = [];
+                    var faces = [
+                        gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
+                        gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
+                    ];
 
-                texture._width = width;
-                texture._height = height;
-                texture.isReady = true;
-            }, extensions);
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
+
+                    for (var index = 0; index < faces.length; index++) {
+                        this._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
+                        gl.texImage2D(faces[index], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._workingCanvas);
+                    }
+
+                    if (!noMipmap) {
+                        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+                    }
+
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+
+                    this._activeTexturesCache = [];
+
+                    texture._width = width;
+                    texture._height = height;
+                    texture.isReady = true;
+                }, extensions);
+            }
 
             return texture;
         }

BIN
Exporters/3ds Max/Max2Babylon-0.5.1.zip


+ 2 - 2
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Camera.cs

@@ -34,8 +34,8 @@ namespace Max2Babylon
             }
 
             // Control
-            babylonCamera.speed = cameraNode._Node.GetFloatProperty("babylonjs_speed");
-            babylonCamera.inertia = cameraNode._Node.GetFloatProperty("babylonjs_inertia");
+            babylonCamera.speed = cameraNode._Node.GetFloatProperty("babylonjs_speed", 1.0f);
+            babylonCamera.inertia = cameraNode._Node.GetFloatProperty("babylonjs_inertia", 0.9f);
 
             // Collisions
             babylonCamera.checkCollisions = cameraNode._Node.GetBoolProperty("babylonjs_checkcollisions");

+ 23 - 17
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using Autodesk.Max;
 using BabylonExport.Entities;
 using MaxSharp;
+using System.Runtime.InteropServices;
 
 namespace Max2Babylon
 {
@@ -19,7 +20,7 @@ namespace Max2Babylon
 
             var babylonMesh = new BabylonMesh();
             int vx1, vx2, vx3;
-           
+
             babylonMesh.name = meshNode.Name;
             babylonMesh.id = meshNode.GetGuid().ToString();
             if (meshNode.HasParent())
@@ -53,25 +54,30 @@ namespace Max2Babylon
             var parts = Loader.Global.AffineParts.Create();
             Loader.Global.DecompAffine(wm, parts);
 
-            //var rotate = new float[3];
-
-            //IntPtr xPtr = Marshal.AllocHGlobal(sizeof(float));
-            //IntPtr yPtr = Marshal.AllocHGlobal(sizeof(float));
-            //IntPtr zPtr = Marshal.AllocHGlobal(sizeof(float));
-            //parts.Q.GetEuler(xPtr, yPtr, zPtr);
+            if (exportQuaternionsInsteadOfEulers)
+            {
+                babylonMesh.rotationQuaternion = parts.Q.ToArray();
+            }
+            else
+            {
+                var rotate = new float[3];
 
-            //Marshal.Copy(xPtr, rotate, 0, 1);
-            //Marshal.Copy(yPtr, rotate, 1, 1);
-            //Marshal.Copy(zPtr, rotate, 2, 1);
+                IntPtr xPtr = Marshal.AllocHGlobal(sizeof(float));
+                IntPtr yPtr = Marshal.AllocHGlobal(sizeof(float));
+                IntPtr zPtr = Marshal.AllocHGlobal(sizeof(float));
+                parts.Q.GetEuler(xPtr, yPtr, zPtr);
 
-            //var temp = rotate[1];
-            //rotate[0] = -rotate[0] * parts.F;
-            //rotate[1] = -rotate[2] * parts.F;
-            //rotate[2] = -temp * parts.F;
+                Marshal.Copy(xPtr, rotate, 0, 1);
+                Marshal.Copy(yPtr, rotate, 1, 1);
+                Marshal.Copy(zPtr, rotate, 2, 1);
 
-            //babylonMesh.rotation = rotate;
+                var temp = rotate[1];
+                rotate[0] = -rotate[0] * parts.F;
+                rotate[1] = -rotate[2] * parts.F;
+                rotate[2] = -temp * parts.F;
 
-            babylonMesh.rotationQuaternion = parts.Q.ToArray();
+                babylonMesh.rotation = rotate;                
+            }
 
             babylonMesh.scaling = parts.K.ToArraySwitched();
 
@@ -330,7 +336,7 @@ namespace Max2Babylon
 
             if (!ExportFloatController(meshNode._Node.VisController, "visibility", animations))
             {
-                ExportFloatAnimation("visibility", animations, key => new[] {meshNode._Node.GetVisibility(key, Interval.Forever._IInterval)});
+                ExportFloatAnimation("visibility", animations, key => new[] { meshNode._Node.GetVisibility(key, Interval.Forever._IInterval) });
             }
 
             babylonMesh.animations = animations.ToArray();

+ 3 - 1
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Texture.cs

@@ -105,6 +105,7 @@ namespace Max2Babylon
                 if (File.Exists(texture.MapName))
                 {
                     File.Copy(texture.MapName, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true);
+                    babylonTexture.isCube = Tools.IsTextureCube(texture.MapName);
                 }
                 else
                 {
@@ -112,6 +113,7 @@ namespace Max2Babylon
                     if (File.Exists(texturepath))
                     {
                         File.Copy(texturepath, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true);
+                        babylonTexture.isCube = Tools.IsTextureCube(texturepath);
                     }
                     else
                     {
@@ -121,7 +123,7 @@ namespace Max2Babylon
             }
             catch
             {
-                RaiseWarning(string.Format("Unable to copy {0} to output folder.", babylonTexture.name), true);
+                // silently fails
             }
 
             return babylonTexture;

+ 3 - 0
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs

@@ -25,6 +25,8 @@ namespace Max2Babylon
 
         public bool IsCancelled { get; set; }
 
+        private bool exportQuaternionsInsteadOfEulers;
+
         void ReportProgressChanged(int progress)
         {
             if (OnImportProgressChanged != null)
@@ -97,6 +99,7 @@ namespace Max2Babylon
             babylonScene.ambientColor = Loader.Core.GetAmbient(0, Interval.Forever._IInterval).ToArray();
 
             babylonScene.gravity = maxScene.RootNode._Node.GetVector3Property("babylonjs_gravity");
+            exportQuaternionsInsteadOfEulers = maxScene.RootNode._Node.GetBoolProperty("babylonjs_exportquaternions");
 
             // Cameras
             BabylonCamera mainCamera = null;

+ 27 - 12
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.Designer.cs

@@ -28,6 +28,7 @@
         /// </summary>
         private void InitializeComponent()
         {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExporterForm));
             this.butExport = new System.Windows.Forms.Button();
             this.label1 = new System.Windows.Forms.Label();
             this.txtFilename = new System.Windows.Forms.TextBox();
@@ -36,6 +37,8 @@
             this.progressBar = new System.Windows.Forms.ProgressBar();
             this.treeView = new System.Windows.Forms.TreeView();
             this.butCancel = new System.Windows.Forms.Button();
+            this.pictureBox1 = new System.Windows.Forms.PictureBox();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
             this.SuspendLayout();
             // 
             // butExport
@@ -43,7 +46,7 @@
             this.butExport.Anchor = System.Windows.Forms.AnchorStyles.Top;
             this.butExport.Enabled = false;
             this.butExport.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butExport.Location = new System.Drawing.Point(321, 50);
+            this.butExport.Location = new System.Drawing.Point(368, 51);
             this.butExport.Name = "butExport";
             this.butExport.Size = new System.Drawing.Size(80, 23);
             this.butExport.TabIndex = 0;
@@ -54,7 +57,7 @@
             // label1
             // 
             this.label1.AutoSize = true;
-            this.label1.Location = new System.Drawing.Point(12, 9);
+            this.label1.Location = new System.Drawing.Point(150, 9);
             this.label1.Name = "label1";
             this.label1.Size = new System.Drawing.Size(52, 13);
             this.label1.TabIndex = 2;
@@ -64,9 +67,9 @@
             // 
             this.txtFilename.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-            this.txtFilename.Location = new System.Drawing.Point(12, 25);
+            this.txtFilename.Location = new System.Drawing.Point(150, 25);
             this.txtFilename.Name = "txtFilename";
-            this.txtFilename.Size = new System.Drawing.Size(650, 20);
+            this.txtFilename.Size = new System.Drawing.Size(481, 20);
             this.txtFilename.TabIndex = 3;
             this.txtFilename.TextChanged += new System.EventHandler(this.txtFilename_TextChanged);
             // 
@@ -74,7 +77,7 @@
             // 
             this.butBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
             this.butBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butBrowse.Location = new System.Drawing.Point(668, 22);
+            this.butBrowse.Location = new System.Drawing.Point(637, 22);
             this.butBrowse.Name = "butBrowse";
             this.butBrowse.Size = new System.Drawing.Size(43, 23);
             this.butBrowse.TabIndex = 4;
@@ -92,9 +95,9 @@
             // 
             this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-            this.progressBar.Location = new System.Drawing.Point(12, 486);
+            this.progressBar.Location = new System.Drawing.Point(153, 418);
             this.progressBar.Name = "progressBar";
-            this.progressBar.Size = new System.Drawing.Size(613, 23);
+            this.progressBar.Size = new System.Drawing.Size(441, 23);
             this.progressBar.TabIndex = 5;
             // 
             // treeView
@@ -102,9 +105,9 @@
             this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-            this.treeView.Location = new System.Drawing.Point(12, 80);
+            this.treeView.Location = new System.Drawing.Point(153, 80);
             this.treeView.Name = "treeView";
-            this.treeView.Size = new System.Drawing.Size(699, 401);
+            this.treeView.Size = new System.Drawing.Size(527, 333);
             this.treeView.TabIndex = 6;
             // 
             // butCancel
@@ -112,7 +115,7 @@
             this.butCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
             this.butCancel.Enabled = false;
             this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butCancel.Location = new System.Drawing.Point(631, 486);
+            this.butCancel.Location = new System.Drawing.Point(600, 418);
             this.butCancel.Name = "butCancel";
             this.butCancel.Size = new System.Drawing.Size(80, 23);
             this.butCancel.TabIndex = 7;
@@ -120,11 +123,21 @@
             this.butCancel.UseVisualStyleBackColor = true;
             this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
             // 
+            // pictureBox1
+            // 
+            this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
+            this.pictureBox1.Location = new System.Drawing.Point(12, 12);
+            this.pictureBox1.Name = "pictureBox1";
+            this.pictureBox1.Size = new System.Drawing.Size(132, 302);
+            this.pictureBox1.TabIndex = 8;
+            this.pictureBox1.TabStop = false;
+            // 
             // ExporterForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-            this.ClientSize = new System.Drawing.Size(723, 525);
+            this.ClientSize = new System.Drawing.Size(692, 457);
+            this.Controls.Add(this.pictureBox1);
             this.Controls.Add(this.butCancel);
             this.Controls.Add(this.treeView);
             this.Controls.Add(this.progressBar);
@@ -133,13 +146,14 @@
             this.Controls.Add(this.txtFilename);
             this.Controls.Add(this.label1);
             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
-            this.MinimumSize = new System.Drawing.Size(500, 300);
+            this.MinimumSize = new System.Drawing.Size(500, 400);
             this.Name = "ExporterForm";
             this.ShowInTaskbar = true;
             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
             this.Text = "Babylon.js - Export scene to .babylon file";
             this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ExporterForm_FormClosed);
             this.Load += new System.EventHandler(this.ExporterForm_Load);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
             this.ResumeLayout(false);
             this.PerformLayout();
 
@@ -155,5 +169,6 @@
         private System.Windows.Forms.ProgressBar progressBar;
         private System.Windows.Forms.TreeView treeView;
         private System.Windows.Forms.Button butCancel;
+        private System.Windows.Forms.PictureBox pictureBox1;
     }
 }

+ 284 - 0
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx

@@ -120,4 +120,288 @@
   <metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        /9j/4AAQSkZJRgABAQEAAAAAAAD/7gAOQWRvYmUAZAAAAAAB/+EAnkV4aWYAAE1NACoAAAAIAAMBMQAC
+        AAAAHgAAADIBMgACAAAAGgAAAFCHaQAEAAAAAQAAAGoAAAAAQWRvYmUgUGhvdG9zaG9wIENTNiAoV2lu
+        ZG93cykAMjAxNC0wNi0xOVQwOTowNTo1MyswMjowMAAAA5AAAAcAAAAEMDIyMKACAAQAAAABAAAAgqAD
+        AAQAAAABAAABLAAAAAAAAP/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
+        AQEBAQICAgICAgICAgICAwMDAwMDAwMDA//bAEMBAQEBAQEBAgEBAgICAQICAwMDAwMDAwMDAwMDAwMD
+        AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA//AABEIASwAggMBEQACEQEDEQH/xAAfAAAA
+        BgIDAQAAAAAAAAAAAAAHCAYFBAkDCgIBAAv/xAC1EAACAQMEAQMDAgMDAwIGCXUBAgMEEQUSBiEHEyIA
+        CDEUQTIjFQlRQhZhJDMXUnGBGGKRJUOhsfAmNHIKGcHRNSfhUzaC8ZKiRFRzRUY3R2MoVVZXGrLC0uLy
+        ZIN0k4Rlo7PD0+MpOGbzdSo5OkhJSlhZWmdoaWp2d3h5eoWGh4iJipSVlpeYmZqkpaanqKmqtLW2t7i5
+        usTFxsfIycrU1dbX2Nna5OXm5+jp6vT19vf4+fr/xAAfAQAABgMBAQEAAAAAAAAAAAAGBQQDBwIIAQkA
+        Cgv/xAC1EQACAQMCBAQDBQQEBAYGBW0BAgMRBCESBTEGACITQVEHMmEUcQhCgSORFVKhYhYzCbEkwdFD
+        cvAX4YI0JZJTGGNE8aKyJjUZVDZFZCcKc4OTRnTC0uLyVWV1VjeEhaOzw9Pj8ykalKS0xNTk9JWltcXV
+        5fUoR1dmOHaGlqa2xtbm9md3h5ent8fX5/dIWGh4iJiouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqq
+        usra6vr/2gAMAwEAAhEDEQA/ANxJQ2o+prXP5a/1t9SSfp7917qZGrcDU3H+J+v9ffq9e6nRq1h6m/1t
+        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
+        P99f/Yf19+611LST36nXiPXqSkn+w/2PJ/5F79w49e6kLJx/vX9P95PvVOvHrKJf8eP99/tr+9+XXv8A
+        D1y8n+8/4/059+699nXjJ/j/AL7ke/de64mT6f77/effuvDrC0n9Tb/ff4e/U691geQ/1/2H9P8Aebe/
+        U68euHl/61/4fX36nXugLV/U3P8Aab+v/E/4e/de6nxP9OffuvdOET/4/wC+P0/Pv3+Hr3U+J+Pz/wAV
+        t+ffuvdT45P99f8A1/8AeffuvdTY5P8Aef8Akf8AUe/de/w9S0k/x4vf+v8AxPHv3XupayH/AHx/4p79
+        17qSkn4v/vP1/wBt/re/de6zrJ/vuDYjn3rr3WQSW/P1+vPB/wCKe98evdc/Jxz/AL78e9fZ1rroy/4/
+        7z731vPXEy/X/bf0t7917rE0n5/331+nN/fuvHrA0nv3XuuPk/x/3X79+XWqdAcrkufz6m/p/qj791vq
+        fFJ9Of8AYf77+nv3Xup8cn4P++/wtf37j17qfG/0/r/yL/H6+/de6mxyf4/0/wB9z9ffuvdTI5P99/xv
+        j37r3+DqYkn+Pv3XupSy/wBPp/vufr79x+3r3HqQsv5/1uPfuvdZll+nP0/4n3vr1Osol+v+35/2/vXD
+        r3Xfl5+v+P8AsLfX37r3Xfl9+699vXAy/wCP+2/31vfuvdYml+vI/P8AxX/D37r3+HrC0v8Aj7917geu
+        vIP6/wBj/D6/1/1veuvU6BISetrH+01v9uefe/Pr3U2N/p+P99/sffv8PXup8cn++/1v8Pfvn17y+XU6
+        OT/jfP8Avvz791759TY5P9tx7917qYknH/FP+J9+691LST/H/kQ9++XXq9SUl/1vfuvdZ1l/1v8Aff8A
+        G/fuvdZll/x/px/vPv2fPr1fLrKJf999f+Ne/de678v+P/Ef4+/de49e8v8Aja/+I+nv3XuuJl/x9+PX
+        usTS/wCP++/4j37r3WJpfyD/AL37917jx695f9f9P++HvX59er0Cqyetr/6pv97/ANh73177Opscn+P+
+        3/3j37/B14dTY5Pz/S3+Hv1OvdTY5P8AfD/e/r78evdTUl+nP+w/31x79Tr3UtJPpz/vvp/r+/de6lLL
+        wP8Aff776e/de6kpL/j/AL7/AHn37r3WdZf8eP8AH/X9+611mEv+9/19+631zE30P++/43795dePp59d
+        +X8397699nXfl/x/339PeuvdcTN/jf8AP+2/oPfuvfZx6xmX/ej/ALH/AI37917rC0v/ABP+9/6/4974
+        9e695f8AH+x/vvx71U+nXugaD+pv8WP9eeT/ALz79Xr3UuOT/ef99/xPvx691Pjk/wCI/wB9+bDn37rw
+        6lxy/wC++vv3W+piSf77/e+OPr791rqWkv0/3j6f8U9+/wAHXupKy/77/kVvfvt691IWW39P9f8A5H79
+        1v8Aw9ZxL791rrKst/z/ALG//E+/dbx1kE3+N/8AY+/eXWuu/N/j+f8AYf73/X34de695v8AEfgf8i/w
+        9+p175dcTL/xr37r3WMzfn8n/ePfj/Lr3WJpv8f99/xT37r2fy678p/6x/4fX/inv3+r/V/n63/n6B4S
+        ep+R+pv+hvfutD06lxyfj36vn17qZHIP9f8Ax/43z7917qYkv+v+P99+fz7917qWkv0/41/xT37/AA9b
+        4/b1KSX37/B1rqSstrcj8fn37r3UhZf+Nf7A+/de8uswl/xv/vHv3XuPWQS/7H/ff8U9+69X9nWQTf4/
+        77/jfv3WuuQm/wAf+I/3v37rfXXm/wB9/r/7x9PfuvdcTN79Tr3XAzf4/wC+/wBh/re/dep1iMv+Nv8A
+        kV7e/de6yeX/AFv8x/T8/wBP+Ne9V63ToHxJ6m/xYn/ef9h731r7OpSSf4/0/wBj7916nU1JP99/vv6e
+        /de6lJLx/wAV9+8+vdS0l/33/G/fuvdSVl/3319+z17qSsvv3Xuswl/xt79Tr3WYS/Tn/ff63+Hv3Xus
+        gl/x/wB79+691zEv+P8Ar/X/AFv979+4fZ1rrvy/1Nv98Pfut9d+X/X9+691xMv+P5/3359+z17rgZv+
+        R/15/wBf8+9nr3WIy/42/wBj7116vXPy/wCJ/wCA/vfW+gl12dx/Rm/3v/be9da6kpL9Bf6f7H6+/de+
+        3qYkn+9/Xn37r32dSkl/xPv3XupSS2/33+2Pv3Xs9SFl/wB9/wAi+vv3Xvt6kLL/AI/77/be/f4OvdZ1
+        l/F+Ofp7917rKsv+P/Ff979+691kE3++/wBf/effuvdc/L/Qj/ff7b37r3Xfl/x/1v8Ae/fuvU9eu/N/
+        iebc/wC++vv3Xq+R64Gb3vr3XEzf77/ifevs69TrGZfx/vvp/vH196691k8p/wDVe30H0/p73j59e6Ck
+        yfuP/wAHb6/S9z79Xr3HqQkn+P8Avv8AkQ9+698+pSS/4/8AIrf63v3XupSSf77/AI37917qQkn+P+2/
+        4nn37r3y6krL/wAU4Pv3WvLrOs3+w/3se/db6zLLyObf7z791rrKJf8AEf77/e/fut9ZBL/U/wDGz/yL
+        37r3XLzf4/77+n9Pfs9e65eX/H37r1eujN/j/T37r3XRm/x/5F/r+/Yr17rgZv8AH/W/3s+/de6xtL/i
+        f99/xr37PXus3lP/AKrW+vv1R16v+HoLDJ+5JyP1t/0Mffvt69/h6zpJ/jz/AMT7917qUkn+3Hv3XupK
+        yf4/719fr/re/de6kpL/AI/77+n+8e/de6zLL/j+f6/74+9+XXupCy/Tn/eveuvdZRL/AL7+lvfv8PXu
+        swl/4j/iOPfvmevdcxN/j/xr/jXv3XqdcvN/jz/vX/G/fuvdcvL/AK/+w/w9+69115f+Nf1/2w9+698h
+        10Zvrz/vubf7b37r3XAzf4/8V/1/fuvdcDN/X/e/fuvY8usvk/x/3R/h/wAU96qPn17PQYtJ+4//AAdv
+        8fyePe+vdZlk/wBf/iffscOvdSVk/wAf9hx/xX+nv3XupKyfTn/fC/v3XiepCy/76/8AxT/X9+691nWU
+        8WP/ABP+++nv3Xv8HWZZfp/xX/effvs691mEv+J96z17rIJf8f8Aeve+vYHXITf4/wC+/p7317/B1zEt
+        /wA/4/7371w69135f8f99/vXv3Xuveb/AB9+6910ZR/X/ff7Dj37r3XHy/7x/vj/ALH375de6xmX/H/f
+        fX37r3WbzD/D/Mf7z/X36nXqH59Bq0n7j/8AB3/H19R/4p79177esySf4j/fD37y691IWT6f77/ffT37
+        r3UhZfp/sP8AiP8Ab+/de6kLLz9f98f6e/eXXuswl/x9++fl17rKsv0H/E+/de6yiW9j9P8AfW9++Xl1
+        7z6yiX/ff7H349e65iX83/pb/iPfs8OvefXfl/4r/wAj/px791r5dd+b/H+tv9v/ALx791vr3l/qf6f8
+        Rc/7f37/AA9e66Mp/r/vF/8AeffuvD09euPl+vPH++/p7917rgZf96P+w/3n37r3UjX/AO4ur8fT/inv
+        3W89Bu0n7j/8Hb/oY+/da6zLJ/rfX/in+Hv3Xus6yf7697f4cX/HvfXus6yf8R7117rOsn+J/wB8Pe+v
+        dZhL/j7117rKJP8AYf763+8+/de6yiX/AHv/AH3+39+/wder69ZBL/j/AMb+v09+691yEv8Asf8AeT79
+        16vXIS/71/xQ8+/U8+vcOu/L/vv8OPfq9e695f8Aefp7117y668v+J/P++/2HvfXuuPm/wBf/ff8j9+6
+        91wMv9f99/xXn37/AA9b6leRv6/8on+8/wBffsdeqOg7Zz5Ht/q2/wChvfsda49ZFkt+f6e/efXvl1nW
+        Tj8+/dexx6zLJ/X/AH3IP+Fre/deI6zLJ/vh7917rMJP97/3v/iOffuvdZRL/vv9v7914fz6yCX/AB/B
+        /wAD/wAU/Pv3XuuYlP8Avv8AH/ivv3Xj1z8v+PH9b/1H/Ee/Y6912JffuvddmX/H/if9h7917HXhJ/j7
+        8evddGX/AG3+39+691xMn9SP9jyP9f8A23v3l17HXEzf4/7z7917qT5v9q/5Q/6f77n37r3SBd7SP/wd
+        v9vqPv3Xhjrmr/7z/wAV/wBj+ffuvefWZZB7917rMJP8f9b+vv2OvY6yiQ/7b/inv3Xusyyf4/8AEe/d
+        e+3rIJPxe/5H/Ijxx7914dcxL/X/AI178fl17rIJP999P9j79Xr1P2dcvJ/j/wAU9+691y8n+P8AxT37
+        r3XvL/j/AL7j37r3XvL/AI39+8+vddGX/ffUfT/H37h17roy/wCP++/2/v3XuuBk/wAR/t/999PfuvdS
+        vJ/tX/KFb6+/V69jpCu/7j/8Hb8/X1f7z79177euav7917/D1lWT/H349e6yrIP+J/31/wDW9+691lEn
+        ++P/ABI9+63nrKsn++/xP4/2Hv3Wusgk9+61norsfyB7C3Dh81vXrXojLb666xNZmqfG5ht9be25uzfd
+        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
+</value>
+  </data>
 </root>

+ 41 - 10
Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.Designer.cs

@@ -29,11 +29,14 @@
         private void InitializeComponent()
         {
             this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.gravityControl = new Max2Babylon.Vector3Control();
             this.label3 = new System.Windows.Forms.Label();
             this.butCancel = new System.Windows.Forms.Button();
             this.butOK = new System.Windows.Forms.Button();
-            this.gravityControl = new Max2Babylon.Vector3Control();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.chkQuaternions = new System.Windows.Forms.CheckBox();
             this.groupBox1.SuspendLayout();
+            this.groupBox2.SuspendLayout();
             this.SuspendLayout();
             // 
             // groupBox1
@@ -41,13 +44,23 @@
             this.groupBox1.Controls.Add(this.gravityControl);
             this.groupBox1.Controls.Add(this.label3);
             this.groupBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.groupBox1.Location = new System.Drawing.Point(12, 12);
+            this.groupBox1.Location = new System.Drawing.Point(12, 105);
             this.groupBox1.Name = "groupBox1";
             this.groupBox1.Size = new System.Drawing.Size(319, 87);
             this.groupBox1.TabIndex = 0;
             this.groupBox1.TabStop = false;
             this.groupBox1.Text = "Collisions";
             // 
+            // gravityControl
+            // 
+            this.gravityControl.Location = new System.Drawing.Point(21, 44);
+            this.gravityControl.Name = "gravityControl";
+            this.gravityControl.Size = new System.Drawing.Size(294, 28);
+            this.gravityControl.TabIndex = 5;
+            this.gravityControl.X = 0F;
+            this.gravityControl.Y = 0F;
+            this.gravityControl.Z = 0F;
+            // 
             // label3
             // 
             this.label3.AutoSize = true;
@@ -82,15 +95,28 @@
             this.butOK.UseVisualStyleBackColor = true;
             this.butOK.Click += new System.EventHandler(this.butOK_Click);
             // 
-            // gravityControl
+            // groupBox2
             // 
-            this.gravityControl.Location = new System.Drawing.Point(21, 44);
-            this.gravityControl.Name = "gravityControl";
-            this.gravityControl.Size = new System.Drawing.Size(294, 28);
-            this.gravityControl.TabIndex = 5;
-            this.gravityControl.X = 0F;
-            this.gravityControl.Y = 0F;
-            this.gravityControl.Z = 0F;
+            this.groupBox2.Controls.Add(this.chkQuaternions);
+            this.groupBox2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox2.Location = new System.Drawing.Point(12, 12);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(319, 87);
+            this.groupBox2.TabIndex = 5;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "Exportation";
+            // 
+            // chkQuaternions
+            // 
+            this.chkQuaternions.AutoSize = true;
+            this.chkQuaternions.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkQuaternions.Location = new System.Drawing.Point(21, 27);
+            this.chkQuaternions.Name = "chkQuaternions";
+            this.chkQuaternions.Size = new System.Drawing.Size(221, 17);
+            this.chkQuaternions.TabIndex = 1;
+            this.chkQuaternions.Text = "Export quaternions instead of Euler angles";
+            this.chkQuaternions.ThreeState = true;
+            this.chkQuaternions.UseVisualStyleBackColor = true;
             // 
             // ScenePropertiesForm
             // 
@@ -99,6 +125,7 @@
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.CancelButton = this.butCancel;
             this.ClientSize = new System.Drawing.Size(343, 390);
+            this.Controls.Add(this.groupBox2);
             this.Controls.Add(this.butCancel);
             this.Controls.Add(this.butOK);
             this.Controls.Add(this.groupBox1);
@@ -109,6 +136,8 @@
             this.Load += new System.EventHandler(this.ScenePropertiesForm_Load);
             this.groupBox1.ResumeLayout(false);
             this.groupBox1.PerformLayout();
+            this.groupBox2.ResumeLayout(false);
+            this.groupBox2.PerformLayout();
             this.ResumeLayout(false);
 
         }
@@ -120,5 +149,7 @@
         private System.Windows.Forms.Label label3;
         private System.Windows.Forms.Button butCancel;
         private System.Windows.Forms.Button butOK;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.CheckBox chkQuaternions;
     }
 }

+ 2 - 0
Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.cs

@@ -14,11 +14,13 @@ namespace Max2Babylon
         private void butOK_Click(object sender, EventArgs e)
         {
             Tools.UpdateVector3Control(gravityControl, Kernel.Scene.RootNode._Node, "babylonjs_gravity");
+            Tools.UpdateCheckBox(chkQuaternions, Kernel.Scene.RootNode._Node, "babylonjs_exportquaternions");
         }
 
         private void ScenePropertiesForm_Load(object sender, EventArgs e)
         {
             Tools.PrepareVector3Control(gravityControl, Kernel.Scene.RootNode._Node, "babylonjs_gravity", 0, -0.9f, 0);
+            Tools.PrepareCheckBox(chkQuaternions, Kernel.Scene.RootNode._Node, "babylonjs_exportquaternions");
         }
     }
 }

+ 88 - 18
Exporters/3ds Max/Max2Babylon/Tools/Tools.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Windows.Forms;
 using Autodesk.Max;
@@ -13,6 +14,60 @@ namespace Max2Babylon
     {
         public const float Epsilon = 0.001f;
 
+        public static bool IsTextureCube(string filepath)
+        {
+            try
+            {
+                var data = File.ReadAllBytes(filepath);
+                var intArray = new int[data.Length / 4];
+
+                Buffer.BlockCopy(data, 0, intArray, 0, data.Length);
+
+                return (intArray[28] & 0x200) == 0x200;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        public static Vector3 ToEulerAngles(this IQuat q)
+        {
+            // Store the Euler angles in radians
+            var pitchYawRoll = new Vector3();
+
+            double sqw = q.W * q.W;
+            double sqx = q.X * q.X;
+            double sqy = q.Y * q.Y;
+            double sqz = q.Z * q.Z;
+
+            // If quaternion is normalised the unit is one, otherwise it is the correction factor
+            double unit = sqx + sqy + sqz + sqw;
+            double test = q.X * q.Y + q.Z * q.W;
+
+            if (test > 0.4999f * unit)                              // 0.4999f OR 0.5f - EPSILON
+            {
+                // Singularity at north pole
+                pitchYawRoll.Y = 2f * (float)Math.Atan2(q.X, q.W);  // Yaw
+                pitchYawRoll.X = (float)Math.PI * 0.5f;             // Pitch
+                pitchYawRoll.Z = 0f;                                // Roll
+                return pitchYawRoll;
+            }
+            if (test < -0.4999f * unit)                        // -0.4999f OR -0.5f + EPSILON
+            {
+                // Singularity at south pole
+                pitchYawRoll.Y = -2f * (float)Math.Atan2(q.X, q.W); // Yaw
+                pitchYawRoll.X = -(float)Math.PI * 0.5f;            // Pitch
+                pitchYawRoll.Z = 0f;                                // Roll
+                return pitchYawRoll;
+            }
+            pitchYawRoll.Y = (float)Math.Atan2(2f * q.Y * q.W - 2f * q.X * q.Z, sqx - sqy - sqz + sqw);       // Yaw
+            pitchYawRoll.X = (float)Math.Asin(2f * test / unit);                                             // Pitch
+            pitchYawRoll.Z = (float)Math.Atan2(2f * q.X * q.W - 2f * q.Y * q.Z, -sqx + sqy - sqz + sqw);      // Roll
+
+            return pitchYawRoll;
+        }
+
         public static void PreparePipeline(IINode node, bool deactivate)
         {
             var obj = node.ObjectRef;
@@ -120,7 +175,7 @@ namespace Max2Babylon
 
         public static Quaternion ToQuat(this IQuat value)
         {
-            return new Quaternion(value.X, value.Z, value.Y, value.W );
+            return new Quaternion(value.X, value.Z, value.Y, value.W);
         }
         public static float[] ToArray(this IQuat value)
         {
@@ -364,37 +419,52 @@ namespace Max2Babylon
             return new[] { state0, state1, state2 };
         }
 
+        public static bool PrepareCheckBox(CheckBox checkBox, IINode node, string propertyName, int defaultState = 0)
+        {
+            var state = node.GetBoolProperty(propertyName, defaultState);
+
+            if (checkBox.CheckState == CheckState.Indeterminate)
+            {
+                checkBox.CheckState = state ? CheckState.Checked : CheckState.Unchecked;
+            }
+            else
+            {
+                if (!state && checkBox.CheckState == CheckState.Checked ||
+                    state && checkBox.CheckState == CheckState.Unchecked)
+                {
+                    checkBox.CheckState = CheckState.Indeterminate;
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         public static void PrepareCheckBox(CheckBox checkBox, List<IINode> nodes, string propertyName, int defaultState = 0)
         {
             checkBox.CheckState = CheckState.Indeterminate;
             foreach (var node in nodes)
             {
-                var state = node.GetBoolProperty(propertyName, defaultState);
-
-                if (checkBox.CheckState == CheckState.Indeterminate)
-                {
-                    checkBox.CheckState = state ? CheckState.Checked : CheckState.Unchecked;
-                }
-                else
+                if (PrepareCheckBox(checkBox, node, propertyName, defaultState))
                 {
-                    if (!state && checkBox.CheckState == CheckState.Checked ||
-                        state && checkBox.CheckState == CheckState.Unchecked)
-                    {
-                        checkBox.CheckState = CheckState.Indeterminate;
-                        break;
-                    }
+                    break;
                 }
             }
         }
 
+        public static void UpdateCheckBox(CheckBox checkBox, IINode node, string propertyName)
+        {
+            if (checkBox.CheckState != CheckState.Indeterminate)
+            {
+                node.SetUserPropBool(ref propertyName, checkBox.CheckState == CheckState.Checked);
+            }
+        }
+
         public static void UpdateCheckBox(CheckBox checkBox, List<IINode> nodes, string propertyName)
         {
             foreach (var node in nodes)
             {
-                if (checkBox.CheckState != CheckState.Indeterminate)
-                {
-                    node.SetUserPropBool(ref propertyName, checkBox.CheckState == CheckState.Checked);
-                }
+                UpdateCheckBox(checkBox, node, propertyName);
             }
         }
 

ファイルの差分が大きいため隠しています
+ 9 - 9
babylon.1.12-rc.js