浏览代码

geometry compression example

LeonYuanYao 5 年之前
父节点
当前提交
45e8b770f0

+ 1 - 0
.gitignore

@@ -8,3 +8,4 @@ npm-debug.log
 .jshintrc
 .vs/
 test/unit/three.*.unit.js
+test/

+ 1 - 1
examples/files.js

@@ -25,6 +25,7 @@ var files = {
 		"webgl_geometries_parametric",
 		"webgl_geometry_colors",
 		"webgl_geometry_colors_lookuptable",
+		"webgl_geometry_compression", 
 		"webgl_geometry_convex",
 		"webgl_geometry_cube",
 		"webgl_geometry_dynamic",
@@ -128,7 +129,6 @@ var files = {
 		"webgl_lod",
 		"webgl_marchingcubes",
 		"webgl_materials",
-		"webgl_materials_packed_geometry",
 		"webgl_materials_blending",
 		"webgl_materials_blending_custom",
 		"webgl_materials_bumpmap",

+ 754 - 0
examples/jsm/utils/GeometryCompressionUtils.js

@@ -0,0 +1,754 @@
+/**
+ * @author YY
+ */
+
+import * as THREE from "../../../build/three.module.js";
+
+var GeometryCompressionUtils = {
+
+    packNormals: function (mesh, encodeMethod) {
+
+        if (!mesh.geometry) {
+            console.error("Mesh must contain geometry property. ");
+        }
+
+        let normal = mesh.geometry.attributes.normal;
+
+        if (!normal) {
+            console.error("Geometry must contain normal attribute. ");
+        }
+
+        if (normal.isPacked) return;
+
+        if (normal.itemSize != 3) {
+            console.error("normal.itemSize is not 3, which cannot be packed. ");
+        }
+
+        let array = normal.array;
+        let count = normal.count;
+
+        let result;
+        if (encodeMethod == "BASIC") {
+
+            result = new Uint16Array(count * 2);
+
+            for (let idx = 0; idx < array.length; idx += 3) {
+
+                let encoded;
+
+                encoded = this.EncodingFuncs.uInt16Encode(array[idx], array[idx + 1], array[idx + 2]);
+
+                result[idx / 3 * 2 + 0] = encoded[0];
+                result[idx / 3 * 2 + 1] = encoded[1];
+
+            }
+
+            mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
+
+        } else if (encodeMethod == "OCT") {
+
+            result = new Int8Array(count * 2);
+
+            for (let idx = 0; idx < array.length; idx += 3) {
+
+                let encoded;
+
+                encoded = this.EncodingFuncs.octEncodeBest(array[idx], array[idx + 1], array[idx + 2]);
+
+                result[idx / 3 * 2 + 0] = encoded[0];
+                result[idx / 3 * 2 + 1] = encoded[1];
+
+            }
+
+            mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
+
+        } else if (encodeMethod == "DEFAULT") {
+
+            result = new Uint8Array(count * 3);
+
+            for (let idx = 0; idx < array.length; idx += 3) {
+
+                let encoded;
+
+                encoded = this.EncodingFuncs.defaultEncode(array[idx], array[idx + 1], array[idx + 2], 1);
+
+                // let decoded = GeometryEncodingUtils.defaultDecode(encoded);
+
+                // let angle = Math.acos(Math.min(array[idx] * decoded[0] + array[idx + 1]  * decoded[1] + array[idx + 2] * decoded[2], 1.0)) * 180 / Math.PI;
+
+                // Math.abs(array[idx + 2]) < 0.05 && console.log([array[idx], array[idx + 1], array[idx + 2]], encoded, decoded, angle)
+
+                result[idx + 0] = encoded[0];
+                result[idx + 1] = encoded[1];
+                result[idx + 2] = encoded[2];
+
+            }
+
+            mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 3, true));
+
+        } else {
+
+            console.error("Unrecognized encoding method, should be `DEFAULT` or `BASIC` or `OCT`. ");
+
+        }
+
+        mesh.geometry.attributes.normal.needsUpdate = true;
+        mesh.geometry.attributes.normal.isPacked = true;
+        mesh.geometry.attributes.normal.packingMethod = encodeMethod;
+
+        // modify material
+        if (!(mesh.material instanceof PackedPhongMaterial)) {
+            mesh.material = new PackedPhongMaterial().copy(mesh.material);
+        }
+
+        if (encodeMethod == "BASIC") {
+            mesh.material.defines.USE_PACKED_NORMAL = 0;
+        }
+        if (encodeMethod == "OCT") {
+            mesh.material.defines.USE_PACKED_NORMAL = 1;
+        }
+        if (encodeMethod == "DEFAULT") {
+            mesh.material.defines.USE_PACKED_NORMAL = 2;
+        }
+
+    },
+
+
+    packPositions: function (mesh) {
+
+        if (!mesh.geometry) {
+            console.error("Mesh must contain geometry property. ");
+        }
+
+        let position = mesh.geometry.attributes.position;
+
+        if (!position) {
+            console.error("Geometry must contain position attribute. ");
+        }
+
+        if (position.isPacked) return;
+
+        if (position.itemSize != 3) {
+            console.error("position.itemSize is not 3, which cannot be packed. ");
+        }
+
+        let array = position.array;
+        let count = position.count;
+
+        let result = this.EncodingFuncs.quantizedEncode(array, 2);
+
+        let quantized = result.quantized;
+        let decodeMat = result.decodeMat;
+
+        // IMPORTANT: calculate original geometry bounding info first, before updating packed positions
+        if (mesh.geometry.boundingBox == null) mesh.geometry.computeBoundingBox();
+        if (mesh.geometry.boundingSphere == null) mesh.geometry.computeBoundingSphere();
+
+        mesh.geometry.setAttribute('position', new THREE.BufferAttribute(quantized, 3));
+        mesh.geometry.attributes.position.isPacked = true;
+        mesh.geometry.attributes.position.needsUpdate = true;
+
+        // modify material
+        if (!(mesh.material instanceof PackedPhongMaterial)) {
+            mesh.material = new PackedPhongMaterial().copy(mesh.material);
+        }
+
+        mesh.material.defines.USE_PACKED_POSITION = 0;
+
+        mesh.material.uniforms.quantizeMatPos.value = decodeMat;
+        mesh.material.uniforms.quantizeMatPos.needsUpdate = true;
+
+    },
+
+
+    packUvs: function (mesh) {
+
+        if (!mesh.geometry) {
+            console.error("Mesh must contain geometry property. ");
+        }
+
+        let uvs = mesh.geometry.attributes.uv;
+
+        if (!uvs) {
+            console.error("Geometry must contain uv attribute. ");
+        }
+
+        if (uvs.isPacked) return;
+
+        let range = { min: Infinity, max: -Infinity };
+
+        let array = uvs.array;
+        let count = uvs.count;
+
+        for (let i = 0; i < array.length; i++) {
+            // if (array[i] > 1.0) array[i] = array[i] - Math.floor(array[i]);
+            range.min = Math.min(range.min, array[i]);
+            range.max = Math.max(range.max, array[i]);
+            // console.log(array[i])
+        }
+
+        console.log(range)
+
+        let result;
+
+        if (range.min >= -1.0 && range.max <= 1.0) {
+
+            // use default encoding method
+            result = new Uint16Array(array.length);
+
+            for (let i = 0; i < array.length; i += 2) {
+
+                let encoded = this.EncodingFuncs.defaultEncode(array[i], array[i + 1], 0, 2);
+
+                result[i] = encoded[0];
+                result[i + 1] = encoded[1];
+
+            }
+
+            mesh.geometry.setAttribute('uv', new THREE.BufferAttribute(result, 2, true));
+            mesh.geometry.attributes.uv.isPacked = true;
+            mesh.geometry.attributes.uv.needsUpdate = true;
+
+            if (!(mesh.material instanceof PackedPhongMaterial)) {
+                mesh.material = new PackedPhongMaterial().copy(mesh.material);
+            }
+
+            mesh.material.defines.USE_PACKED_UV = 0;
+
+        } else {
+
+            // use quantized encoding method
+            result = this.EncodingFuncs.quantizedEncodeUV(array, 2);
+
+            mesh.geometry.setAttribute('uv', new THREE.BufferAttribute(result.quantized, 2));
+            mesh.geometry.attributes.uv.isPacked = true;
+            mesh.geometry.attributes.uv.needsUpdate = true;
+
+            if (!(mesh.material instanceof PackedPhongMaterial)) {
+                mesh.material = new PackedPhongMaterial().copy(mesh.material);
+            }
+
+            mesh.material.defines.USE_PACKED_UV = 1;
+
+            mesh.material.uniforms.quantizeMatUV.value = result.decodeMat;
+            mesh.material.uniforms.quantizeMatUV.needsUpdate = true;
+
+        }
+
+
+
+
+    },
+
+
+    EncodingFuncs: {
+
+        defaultEncode: function (x, y, z, bytes) {
+            if (bytes == 1) {
+                let tmpx = Math.round((x + 1) * 0.5 * 255);
+                let tmpy = Math.round((y + 1) * 0.5 * 255);
+                let tmpz = Math.round((z + 1) * 0.5 * 255);
+                return new Uint8Array([tmpx, tmpy, tmpz]);
+            } else if (bytes == 2) {
+                let tmpx = Math.round((x + 1) * 0.5 * 65535);
+                let tmpy = Math.round((y + 1) * 0.5 * 65535);
+                let tmpz = Math.round((z + 1) * 0.5 * 65535);
+                return new Uint16Array([tmpx, tmpy, tmpz]);
+            } else {
+                console.error("number of bytes error! ");
+            }
+        },
+
+        defaultDecode: function (array, bytes) {
+            if (bytes == 1) {
+                return [
+                    ((array[0] / 255) * 2.0) - 1.0,
+                    ((array[1] / 255) * 2.0) - 1.0,
+                    ((array[2] / 255) * 2.0) - 1.0,
+                ]
+            } else if (bytes == 2) {
+                return [
+                    ((array[0] / 65535) * 2.0) - 1.0,
+                    ((array[1] / 65535) * 2.0) - 1.0,
+                    ((array[2] / 65535) * 2.0) - 1.0,
+                ]
+            } else {
+                console.error("number of bytes error! ");
+            }
+
+        },
+
+        // for `Basic` encoding
+        uInt16Encode: function (x, y, z) {
+            let normal0 = parseInt(0.5 * (1.0 + Math.atan2(y, x) / Math.PI) * 65535);
+            let normal1 = parseInt(0.5 * (1.0 + z) * 65535);
+            return new Uint16Array([normal0, normal1]);
+        },
+
+        // for `OCT` encoding
+        octEncodeBest: function (x, y, z) {
+            var oct, dec, best, currentCos, bestCos;
+
+            // Test various combinations of ceil and floor
+            // to minimize rounding errors
+            best = oct = octEncodeVec3(x, y, z, "floor", "floor");
+            dec = octDecodeVec2(oct);
+            currentCos = bestCos = dot(x, y, z, dec);
+
+            oct = octEncodeVec3(x, y, z, "ceil", "floor");
+            dec = octDecodeVec2(oct);
+            currentCos = dot(x, y, z, dec);
+
+            if (currentCos > bestCos) {
+                best = oct;
+                bestCos = currentCos;
+            }
+
+            oct = octEncodeVec3(x, y, z, "floor", "ceil");
+            dec = octDecodeVec2(oct);
+            currentCos = dot(x, y, z, dec);
+
+            if (currentCos > bestCos) {
+                best = oct;
+                bestCos = currentCos;
+            }
+
+            oct = octEncodeVec3(x, y, z, "ceil", "ceil");
+            dec = octDecodeVec2(oct);
+            currentCos = dot(x, y, z, dec);
+
+            if (currentCos > bestCos) {
+                best = oct;
+                bestCos = currentCos;
+            }
+
+            // var angle = Math.acos(bestCos) * 180 / Math.PI;
+            // angle > 1 && console.log(angle)
+
+            return best;
+
+            function octEncodeVec3(x0, y0, z0, xfunc, yfunc) {
+                var x = x0 / (Math.abs(x0) + Math.abs(y0) + Math.abs(z0));
+                var y = y0 / (Math.abs(x0) + Math.abs(y0) + Math.abs(z0));
+
+                if (z < 0) {
+                    var tempx = x;
+                    var tempy = y;
+                    tempx = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
+                    tempy = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
+                    x = tempx;
+                    y = tempy;
+
+                    var diff = 1 - Math.abs(x) - Math.abs(y);
+                    if (diff > 0) {
+                        diff += 0.001
+                        x += x > 0 ? diff / 2 : -diff / 2;
+                        y += y > 0 ? diff / 2 : -diff / 2;
+                    }
+
+                }
+
+                return new Int8Array([
+                    Math[xfunc](x * 127.5 + (x < 0 ? 1 : 0)),
+                    Math[yfunc](y * 127.5 + (y < 0 ? 1 : 0))
+                ]);
+
+            }
+
+            function octDecodeVec2(oct) {
+                var x = oct[0];
+                var y = oct[1];
+                x /= x < 0 ? 127 : 128;
+                y /= y < 0 ? 127 : 128;
+
+                var z = 1 - Math.abs(x) - Math.abs(y);
+
+                if (z < 0) {
+                    var tmpx = x;
+                    x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
+                    y = (1 - Math.abs(tmpx)) * (y >= 0 ? 1 : -1);
+                }
+
+                var length = Math.sqrt(x * x + y * y + z * z);
+
+                return [
+                    x / length,
+                    y / length,
+                    z / length
+                ];
+            }
+
+            function dot(x, y, z, vec3) {
+                return x * vec3[0] + y * vec3[1] + z * vec3[2];
+            }
+        },
+
+        quantizedEncode: function (array, bytes) {
+
+            let quantized, segments;
+
+            if (bytes == 1) {
+                quantized = new Uint8Array(array.length);
+                segments = 255;
+            } else if (bytes == 2) {
+                quantized = new Uint16Array(array.length);
+                segments = 65535;
+            } else {
+                console.error("number of bytes error! ");
+            }
+
+            let decodeMat = new THREE.Matrix4();
+
+            let min = new Float32Array(3);
+            let max = new Float32Array(3);
+
+            min[0] = min[1] = min[2] = Number.MAX_VALUE;
+            max[0] = max[1] = max[2] = -Number.MAX_VALUE;
+
+            for (let i = 0; i < array.length; i += 3) {
+                min[0] = Math.min(min[0], array[i + 0]);
+                min[1] = Math.min(min[1], array[i + 1]);
+                min[2] = Math.min(min[2], array[i + 2]);
+                max[0] = Math.max(max[0], array[i + 0]);
+                max[1] = Math.max(max[1], array[i + 1]);
+                max[2] = Math.max(max[2], array[i + 2]);
+            }
+
+            decodeMat.scale(new THREE.Vector3(
+                (max[0] - min[0]) / segments,
+                (max[1] - min[1]) / segments,
+                (max[2] - min[2]) / segments
+            ));
+
+            decodeMat.elements[12] = min[0];
+            decodeMat.elements[13] = min[1];
+            decodeMat.elements[14] = min[2];
+
+            decodeMat.transpose();
+
+
+            let multiplier = new Float32Array([
+                max[0] !== min[0] ? segments / (max[0] - min[0]) : 0,
+                max[1] !== min[1] ? segments / (max[1] - min[1]) : 0,
+                max[2] !== min[2] ? segments / (max[2] - min[2]) : 0
+            ]);
+
+            for (let i = 0; i < array.length; i += 3) {
+                quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
+                quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
+                quantized[i + 2] = Math.floor((array[i + 2] - min[2]) * multiplier[2]);
+            }
+
+            return {
+                quantized: quantized,
+                decodeMat: decodeMat
+            };
+        }, 
+
+
+        quantizedEncodeUV: function (array, bytes) {
+
+            let quantized, segments;
+
+            if (bytes == 1) {
+                quantized = new Uint8Array(array.length);
+                segments = 255;
+            } else if (bytes == 2) {
+                quantized = new Uint16Array(array.length);
+                segments = 65535;
+            } else {
+                console.error("number of bytes error! ");
+            }
+
+            let decodeMat = new THREE.Matrix3();
+
+            let min = new Float32Array(2);
+            let max = new Float32Array(2);
+
+            min[0] = min[1] = Number.MAX_VALUE;
+            max[0] = max[1] = -Number.MAX_VALUE;
+
+            for (let i = 0; i < array.length; i += 2) {
+                min[0] = Math.min(min[0], array[i + 0]);
+                min[1] = Math.min(min[1], array[i + 1]);
+                max[0] = Math.max(max[0], array[i + 0]);
+                max[1] = Math.max(max[1], array[i + 1]);
+            }
+
+            decodeMat.scale(
+                (max[0] - min[0]) / segments,
+                (max[1] - min[1]) / segments
+            );
+
+            decodeMat.elements[6] = min[0];
+            decodeMat.elements[7] = min[1];
+
+            decodeMat.transpose();
+
+            let multiplier = new Float32Array([
+                max[0] !== min[0] ? segments / (max[0] - min[0]) : 0,
+                max[1] !== min[1] ? segments / (max[1] - min[1]) : 0
+            ]);
+
+            for (let i = 0; i < array.length; i += 2) {
+                quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
+                quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
+            }
+
+            return {
+                quantized: quantized,
+                decodeMat: decodeMat
+            };
+        }
+
+    }
+
+};
+
+
+
+/**
+ * PackedPhongMaterial inherited from THREE.MeshPhongMaterial
+ * 
+ * @param {*} parameters 
+ */
+function PackedPhongMaterial(parameters) {
+    THREE.MeshPhongMaterial.call(this);
+    this.defines = {};
+    this.type = 'PackedPhongMaterial';
+    this.uniforms = THREE.UniformsUtils.merge([
+
+        THREE.ShaderLib.phong.uniforms,
+
+        {
+            quantizeMatPos: { value: null }, 
+            quantizeMatUV: { value: null }
+        }
+
+    ]);
+
+    this.vertexShader = [
+        "#define PHONG",
+
+        "varying vec3 vViewPosition;",
+
+        "#ifndef FLAT_SHADED",
+        "varying vec3 vNormal;",
+        "#endif",
+
+        THREE.ShaderChunk.common,
+        THREE.ShaderChunk.uv_pars_vertex,
+        THREE.ShaderChunk.uv2_pars_vertex,
+        THREE.ShaderChunk.displacementmap_pars_vertex,
+        THREE.ShaderChunk.envmap_pars_vertex,
+        THREE.ShaderChunk.color_pars_vertex,
+        THREE.ShaderChunk.fog_pars_vertex,
+        THREE.ShaderChunk.morphtarget_pars_vertex,
+        THREE.ShaderChunk.skinning_pars_vertex,
+        THREE.ShaderChunk.shadowmap_pars_vertex,
+        THREE.ShaderChunk.logdepthbuf_pars_vertex,
+        THREE.ShaderChunk.clipping_planes_pars_vertex,
+
+        `#ifdef USE_PACKED_NORMAL
+            #if USE_PACKED_NORMAL == 0
+                vec3 decodeNormal(vec3 packedNormal)
+                { 
+                    float x = packedNormal.x * 2.0 - 1.0;
+                    float y = packedNormal.y * 2.0 - 1.0;
+                    vec2 scth = vec2(sin(x * PI), cos(x * PI));
+                    vec2 scphi = vec2(sqrt(1.0 - y * y), y); 
+                    return normalize( vec3(scth.y * scphi.x, scth.x * scphi.x, scphi.y) );
+                } 
+            #endif
+
+            #if USE_PACKED_NORMAL == 1
+                vec3 decodeNormal(vec3 packedNormal)
+                { 
+                    vec3 v = vec3(packedNormal.xy, 1.0 - abs(packedNormal.x) - abs(packedNormal.y));
+                    if (v.z < 0.0) 
+                    {
+                        v.xy = (1.0 - abs(v.yx)) * vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
+                    }
+                    return normalize(v);
+                }
+            #endif
+
+            #if USE_PACKED_NORMAL == 2
+                vec3 decodeNormal(vec3 packedNormal)
+                {
+                    vec3 v = (packedNormal * 2.0) - 1.0;
+                    return normalize(v);
+                }
+            #endif
+        #endif`,
+
+        `#ifdef USE_PACKED_POSITION
+            #if USE_PACKED_POSITION == 0
+                uniform mat4 quantizeMatPos;
+            #endif
+        #endif`,
+
+        `#ifdef USE_PACKED_UV
+            #if USE_PACKED_UV == 1
+                uniform mat3 quantizeMatUV;
+            #endif
+        #endif`,
+
+        `#ifdef USE_PACKED_UV
+            #if USE_PACKED_UV == 0
+                vec2 decodeUV(vec2 packedUV)
+                {
+                    vec2 uv = (packedUV * 2.0) - 1.0;
+                    return uv;
+                }
+            #endif
+
+            #if USE_PACKED_UV == 1
+                vec2 decodeUV(vec2 packedUV)
+                {
+                    vec2 uv = ( vec3(packedUV, 1.0) * quantizeMatUV ).xy;
+                    return uv;
+                }
+            #endif
+        #endif`,
+
+        "void main() {",
+
+        THREE.ShaderChunk.uv_vertex,
+
+        `
+        #ifdef USE_UV
+            #ifdef USE_PACKED_UV
+                vUv = decodeUV(vUv);
+            #endif
+        #endif
+        `,
+
+        THREE.ShaderChunk.uv2_vertex,
+        THREE.ShaderChunk.color_vertex,
+        THREE.ShaderChunk.beginnormal_vertex,
+
+        `#ifdef USE_PACKED_NORMAL
+            objectNormal = decodeNormal(objectNormal);
+        #endif
+
+        #ifdef USE_TANGENT
+            vec3 objectTangent = vec3( tangent.xyz );
+        #endif
+        `,
+
+        THREE.ShaderChunk.morphnormal_vertex,
+        THREE.ShaderChunk.skinbase_vertex,
+        THREE.ShaderChunk.skinnormal_vertex,
+        THREE.ShaderChunk.defaultnormal_vertex,
+
+        "#ifndef FLAT_SHADED",
+        "vNormal = normalize( transformedNormal );",
+        "#endif",
+
+        THREE.ShaderChunk.begin_vertex,
+
+        `#ifdef USE_PACKED_POSITION
+            #if USE_PACKED_POSITION == 0
+                transformed = ( vec4(transformed, 1.0) * quantizeMatPos ).xyz;
+            #endif
+        #endif`,
+
+        THREE.ShaderChunk.morphtarget_vertex,
+        THREE.ShaderChunk.skinning_vertex,
+        THREE.ShaderChunk.displacementmap_vertex,
+        THREE.ShaderChunk.project_vertex,
+        THREE.ShaderChunk.logdepthbuf_vertex,
+        THREE.ShaderChunk.clipping_planes_vertex,
+
+        "vViewPosition = - mvPosition.xyz;",
+
+        THREE.ShaderChunk.worldpos_vertex,
+        THREE.ShaderChunk.envmap_vertex,
+        THREE.ShaderChunk.shadowmap_vertex,
+        THREE.ShaderChunk.fog_vertex,
+
+        "}",
+    ].join("\n");
+
+    this.fragmentShader = [
+        "#define PHONG",
+
+        "uniform vec3 diffuse;",
+        "uniform vec3 emissive;",
+        "uniform vec3 specular;",
+        "uniform float shininess;",
+        "uniform float opacity;",
+
+        THREE.ShaderChunk.common,
+        THREE.ShaderChunk.packing,
+        THREE.ShaderChunk.dithering_pars_fragment,
+        THREE.ShaderChunk.color_pars_fragment,
+        THREE.ShaderChunk.uv_pars_fragment,
+        THREE.ShaderChunk.uv2_pars_fragment,
+        THREE.ShaderChunk.map_pars_fragment,
+        THREE.ShaderChunk.alphamap_pars_fragment,
+        THREE.ShaderChunk.aomap_pars_fragment,
+        THREE.ShaderChunk.lightmap_pars_fragment,
+        THREE.ShaderChunk.emissivemap_pars_fragment,
+        THREE.ShaderChunk.envmap_common_pars_fragment,
+        THREE.ShaderChunk.envmap_pars_fragment,
+        THREE.ShaderChunk.cube_uv_reflection_fragment,
+        THREE.ShaderChunk.fog_pars_fragment,
+        THREE.ShaderChunk.bsdfs,
+        THREE.ShaderChunk.lights_pars_begin,
+        THREE.ShaderChunk.lights_phong_pars_fragment,
+        THREE.ShaderChunk.shadowmap_pars_fragment,
+        THREE.ShaderChunk.bumpmap_pars_fragment,
+        THREE.ShaderChunk.normalmap_pars_fragment,
+        THREE.ShaderChunk.specularmap_pars_fragment,
+        THREE.ShaderChunk.logdepthbuf_pars_fragment,
+        THREE.ShaderChunk.clipping_planes_pars_fragment,
+
+        "void main() {",
+
+        THREE.ShaderChunk.clipping_planes_fragment,
+
+        "vec4 diffuseColor = vec4( diffuse, opacity );",
+        "ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
+        "vec3 totalEmissiveRadiance = emissive;",
+
+        THREE.ShaderChunk.logdepthbuf_fragment,
+        THREE.ShaderChunk.map_fragment,
+        THREE.ShaderChunk.color_fragment,
+        THREE.ShaderChunk.alphamap_fragment,
+        THREE.ShaderChunk.alphatest_fragment,
+        THREE.ShaderChunk.specularmap_fragment,
+        THREE.ShaderChunk.normal_fragment_begin,
+        THREE.ShaderChunk.normal_fragment_maps,
+        THREE.ShaderChunk.emissivemap_fragment,
+
+        // accumulation
+        THREE.ShaderChunk.lights_phong_fragment,
+        THREE.ShaderChunk.lights_fragment_begin,
+        THREE.ShaderChunk.lights_fragment_maps,
+        THREE.ShaderChunk.lights_fragment_end,
+
+        // modulation
+        THREE.ShaderChunk.aomap_fragment,
+
+        "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
+
+        THREE.ShaderChunk.envmap_fragment,
+
+        "gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+        THREE.ShaderChunk.tonemapping_fragment,
+        THREE.ShaderChunk.encodings_fragment,
+        THREE.ShaderChunk.fog_fragment,
+        THREE.ShaderChunk.premultiplied_alpha_fragment,
+        THREE.ShaderChunk.dithering_fragment,
+        "}",
+    ].join("\n");
+
+    this.setValues(parameters);
+}
+
+PackedPhongMaterial.prototype = Object.create(THREE.MeshPhongMaterial.prototype);
+
+export { GeometryCompressionUtils, PackedPhongMaterial };

+ 0 - 582
examples/jsm/utils/GeometryPackingUtils.js

@@ -1,582 +0,0 @@
-/**
- * @author YY
- */
-
-import * as THREE from "../../../build/three.module.js";
-
-var GeometryPackingUtils = {
-
-    packNormals: function (mesh, encodeMethod) {
-
-        if (!mesh.geometry) {
-            console.error("Mesh must contain geometry property. ");
-        }
-
-        let normal = mesh.geometry.attributes.normal;
-
-        if (!normal) {
-            console.error("Geometry must contain normal attribute. ");
-        }
-
-        if (normal.isPacked) return;
-
-        if (normal.itemSize != 3) {
-            console.error("normal.itemSize is not 3, which cannot be packed. ");
-        }
-
-        let array = normal.array;
-        let count = normal.count;
-
-        let result;
-        if (encodeMethod == "BASIC") {
-
-            result = new Uint16Array(count * 2);
-
-            for (let idx = 0; idx < array.length; idx += 3) {
-
-                let encoded;
-
-                encoded = GeometryEncodingUtils.uInt16Encode(array[idx], array[idx + 1], array[idx + 2]);
-
-                result[idx / 3 * 2 + 0] = encoded[0];
-                result[idx / 3 * 2 + 1] = encoded[1];
-
-            }
-
-            mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
-
-        } else if (encodeMethod == "OCT") {
-
-            result = new Int8Array(count * 2);
-
-            for (let idx = 0; idx < array.length; idx += 3) {
-
-                let encoded;
-
-                encoded = GeometryEncodingUtils.octEncodeBest(array[idx], array[idx + 1], array[idx + 2]);
-
-                let x = encoded[0] / (encoded[0] < 0 ? 128 : 127)
-                let y = encoded[1] / (encoded[1] < 0 ? 128 : 127)
-
-                if ( Math.abs(array[idx + 2]) == 0 ){
-                    console.log([ array[idx], array[idx + 1], array[idx + 2] ], encoded, 
-                        x, 
-                        y,
-                        1-Math.abs(x)-Math.abs(y), 
-                        (1-Math.abs(x)-Math.abs(y))*127, 
-                    )
-                }
-
-                result[idx / 3 * 2 + 0] = encoded[0];
-                result[idx / 3 * 2 + 1] = encoded[1];
-
-            }
-
-            mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
-
-        } else if (encodeMethod == "DEFAULT") {
-
-            result = new Uint8Array(count * 3);
-
-            for (let idx = 0; idx < array.length; idx += 3) {
-
-                let encoded;
-
-                encoded = GeometryEncodingUtils.defaultEncode(array[idx], array[idx + 1], array[idx + 2]);
-
-                let decoded = GeometryEncodingUtils.defaultDecode(encoded);
-
-                let angle = Math.acos(Math.min(array[idx] * decoded[0] + array[idx + 1]  * decoded[1] + array[idx + 2] * decoded[2], 1.0)) * 180 / Math.PI;
-
-                Math.abs(array[idx + 2]) < 0.05 && console.log([array[idx], array[idx + 1], array[idx + 2]], encoded, decoded, angle)
-
-
-                result[idx + 0] = encoded[0];
-                result[idx + 1] = encoded[1];
-                result[idx + 2] = encoded[2];
-
-            }
-
-            mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 3, true));
-
-        } else {
-
-            console.error("Unrecognized encoding method, should be `BASIC` or `OCT`. ");
-
-        }
-
-        mesh.geometry.attributes.normal.needsUpdate = true;
-        mesh.geometry.attributes.normal.isPacked = true;
-        mesh.geometry.attributes.normal.packingMethod = encodeMethod;
-
-        // modify material
-        if (!(mesh.material instanceof PackedPhongMaterial)) {
-            mesh.material = new PackedPhongMaterial().copy(mesh.material);
-        }
-
-        if (encodeMethod == "BASIC") {
-            mesh.material.defines.USE_PACKED_NORMAL = 0;
-        }
-        if (encodeMethod == "OCT") {
-            mesh.material.defines.USE_PACKED_NORMAL = 1;
-        }
-        if (encodeMethod == "DEFAULT") {
-            mesh.material.defines.USE_PACKED_NORMAL = 2;
-        }
-
-    },
-
-
-    packPositions: function (mesh) {
-
-        if (!mesh.geometry) {
-            console.error("Mesh must contain geometry property. ");
-        }
-
-        let position = mesh.geometry.attributes.position;
-
-        if (!position) {
-            console.error("Geometry must contain position attribute. ");
-        }
-
-        if (position.isPacked) return;
-
-        if (position.itemSize != 3) {
-            console.error("position.itemSize is not 3, which cannot be packed. ");
-        }
-
-        let array = position.array;
-        let count = position.count;
-
-        let quantized = new Uint16Array(array.length);
-
-        let decodeMat4 = new THREE.Matrix4();
-        let min = new Float32Array(3);
-        let max = new Float32Array(3);
-
-        min[0] = min[1] = min[2] = Number.MAX_VALUE;
-        max[0] = max[1] = max[2] = -Number.MAX_VALUE;
-
-        for (let i = 0; i < array.length; i += 3) {
-            min[0] = Math.min(min[0], array[i + 0]);
-            min[1] = Math.min(min[1], array[i + 1]);
-            min[2] = Math.min(min[2], array[i + 2]);
-            max[0] = Math.max(max[0], array[i + 0]);
-            max[1] = Math.max(max[1], array[i + 1]);
-            max[2] = Math.max(max[2], array[i + 2]);
-        }
-
-        decodeMat4.scale(new THREE.Vector3(
-            (max[0] - min[0]) / 65535,
-            (max[1] - min[1]) / 65535,
-            (max[2] - min[2]) / 65535
-        ));
-
-        decodeMat4.elements[12] = min[0];
-        decodeMat4.elements[13] = min[1];
-        decodeMat4.elements[14] = min[2];
-
-        decodeMat4.transpose();
-
-
-        let multiplier = new Float32Array([
-            max[0] !== min[0] ? 65535 / (max[0] - min[0]) : 0,
-            max[1] !== min[1] ? 65535 / (max[1] - min[1]) : 0,
-            max[2] !== min[2] ? 65535 / (max[2] - min[2]) : 0
-        ]);
-
-        for (let i = 0; i < array.length; i += 3) {
-            quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
-            quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
-            quantized[i + 2] = Math.floor((array[i + 2] - min[2]) * multiplier[2]);
-        }
-
-        // IMPORTANT: calculate original geometry bounding info first, before updating packed positions
-        if (mesh.geometry.boundingBox == null) mesh.geometry.computeBoundingBox();
-        if (mesh.geometry.boundingSphere == null) mesh.geometry.computeBoundingSphere();
-
-        mesh.geometry.setAttribute('position', new THREE.BufferAttribute(quantized, 3));
-        mesh.geometry.attributes.position.isPacked = true;
-        mesh.geometry.attributes.position.needsUpdate = true;
-
-        // modify material
-        if (!(mesh.material instanceof PackedPhongMaterial)) {
-            mesh.material = new PackedPhongMaterial().copy(mesh.material);
-        }
-
-        mesh.material.defines.USE_PACKED_POSITION = 0;
-
-        mesh.material.uniforms.quantizeMat.value = decodeMat4;
-        mesh.material.uniforms.quantizeMat.needsUpdate = true;
-
-    }
-
-};
-
-
-/**
- * 
- * Encoding functions: Default, Basic, OCT
- * 
- */
-var GeometryEncodingUtils = {
-
-    defaultEncode: function (x, y, z) {
-        let tmpx = parseInt((x + 1) * 0.5 * 255);
-        let tmpy = parseInt((y + 1) * 0.5 * 255);
-        let tmpz = parseInt((z + 1) * 0.5 * 255);
-        return new Uint8Array([tmpx, tmpy, tmpz]);
-    },
-
-    defaultDecode: function (array) {
-        return [
-            ((array[0] / 255) * 2.0) - 1.0,
-            ((array[1] / 255) * 2.0) - 1.0,
-            ((array[2] / 255) * 2.0) - 1.0,
-        ]
-    },
-
-    // for `Basic` encoding
-    uInt16Encode: function (x, y, z) {
-        let normal0 = parseInt(0.5 * (1.0 + Math.atan2(y, x) / Math.PI) * 65535);
-        let normal1 = parseInt(0.5 * (1.0 + z) * 65535);
-        return new Uint16Array([normal0, normal1]);
-    },
-
-    // for `OCT` encoding
-    octEncodeBest: function (x, y, z) {
-        var oct, dec, best, currentCos, bestCos;
-
-        // Test various combinations of ceil and floor
-        // to minimize rounding errors
-        best = oct = octEncodeVec3(x, y, z, "floor", "floor");
-        dec = octDecodeVec2(oct);
-        currentCos = bestCos = dot(x, y, z, dec);
-
-        oct = octEncodeVec3(x, y, z, "ceil", "floor");
-        dec = octDecodeVec2(oct);
-        currentCos = dot(x, y, z, dec);
-
-        if (currentCos > bestCos) {
-            best = oct;
-            bestCos = currentCos;
-        }
-
-        oct = octEncodeVec3(x, y, z, "floor", "ceil");
-        dec = octDecodeVec2(oct);
-        currentCos = dot(x, y, z, dec);
-
-        if (currentCos > bestCos) {
-            best = oct;
-            bestCos = currentCos;
-        }
-
-        oct = octEncodeVec3(x, y, z, "ceil", "ceil");
-        dec = octDecodeVec2(oct);
-        currentCos = dot(x, y, z, dec);
-
-        if (currentCos > bestCos) {
-            best = oct;
-            bestCos = currentCos;
-        }
-
-        var angle = Math.acos(bestCos) * 180 / Math.PI;
-
-        // if (Math.abs(z) < 0.05) {
-        //     console.log(angle)
-        //     console.log([x, y, z], octDecodeVec2(octEncodeVec3(x, y, z, "floor", "floor")))
-        //     console.log([x, y, z], octDecodeVec2(octEncodeVec3(x, y, z, "ceil", "floor")))
-        //     console.log([x, y, z], octDecodeVec2(octEncodeVec3(x, y, z, "floor", "ceil")))
-        //     console.log([x, y, z], octDecodeVec2(octEncodeVec3(x, y, z, "ceil", "ceil")))
-        // }
-
-        return best;
-
-        function octEncodeVec3(x, y, z, xfunc, yfunc) {
-            var x = x / (Math.abs(x) + Math.abs(y) + Math.abs(z));
-            var y = y / (Math.abs(x) + Math.abs(y) + Math.abs(z));
-
-            if (z < 0) {
-                var tempx = x;
-                var tempy = y;
-                tempx = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
-                tempy = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
-                x = tempx;
-                y = tempy;
-
-                var diff = 1-Math.abs(x)-Math.abs(y);
-                if (diff > 0){
-                    diff += 0.001
-                    x += x > 0 ? diff / 2 : -diff / 2;
-                    y += y > 0 ? diff / 2 : -diff / 2;
-                }
-
-                console.log("z < 0", `z:${z}`, 1-Math.abs(x)-Math.abs(y))
-            }
-
-            return new Int8Array([
-                Math[xfunc](x * 127.5 + (x < 0 ? 1 : 0)),
-                Math[yfunc](y * 127.5 + (y < 0 ? 1 : 0))
-            ]);
-
-            // return new Int8Array([
-            //     parseInt(x * 127.5 + (x < 0 ? 1 : 0)),
-            //     parseInt(y * 127.5 + (y < 0 ? 1 : 0))
-            // ]);
-        }
-
-        function octDecodeVec2(oct) {
-            var x = oct[0];
-            var y = oct[1];
-            x /= x < 0 ? 127 : 128;
-            y /= y < 0 ? 127 : 128;
-
-            var z = 1 - Math.abs(x) - Math.abs(y);
-
-            if (z < 0) {
-                var tmpx = x;
-                x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
-                y = (1 - Math.abs(tmpx)) * (y >= 0 ? 1 : -1);
-            }
-
-            var length = Math.sqrt(x * x + y * y + z * z);
-
-            return [
-                x / length,
-                y / length,
-                z / length
-            ];
-        }
-
-        function dot(x, y, z, vec3) {
-            return x * vec3[0] + y * vec3[1] + z * vec3[2];
-        }
-    }
-
-};
-
-
-
-/**
- * PackedPhongMaterial inherited from THREE.MeshPhongMaterial
- * 
- * @param {*} parameters 
- */
-function PackedPhongMaterial(parameters) {
-    THREE.MeshPhongMaterial.call(this);
-    this.defines = {};
-    this.type = 'PackedPhongMaterial';
-    this.uniforms = PackedPhongShader.uniforms;
-    this.vertexShader = PackedPhongShader.vertexShader;
-    this.fragmentShader = PackedPhongShader.fragmentShader;
-    this.setValues(parameters);
-}
-
-var PackedPhongShader = {
-
-    uniforms: THREE.UniformsUtils.merge([
-
-        THREE.ShaderLib.phong.uniforms,
-
-        {
-            quantizeMat: { value: null }
-        }
-
-    ]),
-
-    vertexShader: [
-        "#define PHONG",
-
-        "varying vec3 vViewPosition;",
-
-        "#ifndef FLAT_SHADED",
-        "varying vec3 vNormal;",
-        "#endif",
-
-        THREE.ShaderChunk.common,
-        THREE.ShaderChunk.uv_pars_vertex,
-        THREE.ShaderChunk.uv2_pars_vertex,
-        THREE.ShaderChunk.displacementmap_pars_vertex,
-        THREE.ShaderChunk.envmap_pars_vertex,
-        THREE.ShaderChunk.color_pars_vertex,
-        THREE.ShaderChunk.fog_pars_vertex,
-        THREE.ShaderChunk.morphtarget_pars_vertex,
-        THREE.ShaderChunk.skinning_pars_vertex,
-        THREE.ShaderChunk.shadowmap_pars_vertex,
-        THREE.ShaderChunk.logdepthbuf_pars_vertex,
-        THREE.ShaderChunk.clipping_planes_pars_vertex,
-
-        `#ifdef USE_PACKED_NORMAL
-            vec3 basicDecode(vec3 packedNormal)
-            { 
-                float x = packedNormal.x * 2.0 - 1.0;
-                float y = packedNormal.y * 2.0 - 1.0;
-                vec2 scth = vec2(sin(x * PI), cos(x * PI));
-                vec2 scphi = vec2(sqrt(1.0 - y * y), y); 
-                return normalize( vec3(scth.y * scphi.x, scth.x * scphi.x, scphi.y) );
-            } 
-
-            vec3 octDecode(vec3 packedNormal)
-            { 
-                // packedNormal = packedNormal / 127.0;
-
-                vec3 v = vec3(packedNormal.xy, 1.0 - abs(packedNormal.x) - abs(packedNormal.y));
-                if (v.z < 0.0) 
-                {
-                    v.xy = (1.0 - abs(v.yx)) * vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
-                }
-                return normalize(v);
-            }
-
-            vec3 defaultDecode(vec3 packedNormal)
-            {
-                vec3 v = (packedNormal * 2.0) - 1.0;
-                return normalize(v);
-            }
-        #endif`,
-
-        `#ifdef USE_PACKED_POSITION
-            #if USE_PACKED_POSITION == 0
-                uniform mat4 quantizeMat;
-            #endif
-        #endif`,
-
-        "void main() {",
-
-        THREE.ShaderChunk.uv_vertex,
-        THREE.ShaderChunk.uv2_vertex,
-        THREE.ShaderChunk.color_vertex,
-        THREE.ShaderChunk.beginnormal_vertex,
-
-        `#ifdef USE_PACKED_NORMAL
-            #if USE_PACKED_NORMAL == 0
-                objectNormal = basicDecode(objectNormal);
-            #endif
-
-            #if USE_PACKED_NORMAL == 1
-                objectNormal = octDecode(objectNormal);
-            #endif
-
-            #if USE_PACKED_NORMAL == 2
-                objectNormal = defaultDecode(objectNormal);
-            #endif
-        #endif
-
-        #ifdef USE_TANGENT
-            vec3 objectTangent = vec3( tangent.xyz );
-        #endif
-        `,
-
-        THREE.ShaderChunk.morphnormal_vertex,
-        THREE.ShaderChunk.skinbase_vertex,
-        THREE.ShaderChunk.skinnormal_vertex,
-        THREE.ShaderChunk.defaultnormal_vertex,
-
-        "#ifndef FLAT_SHADED",
-        "vNormal = normalize( transformedNormal );",
-        "#endif",
-
-        THREE.ShaderChunk.begin_vertex,
-
-        `#ifdef USE_PACKED_POSITION
-            #if USE_PACKED_POSITION == 0
-                transformed = ( vec4(transformed, 1.0) * quantizeMat ).xyz;
-            #endif
-        #endif`,
-
-        THREE.ShaderChunk.morphtarget_vertex,
-        THREE.ShaderChunk.skinning_vertex,
-        THREE.ShaderChunk.displacementmap_vertex,
-        THREE.ShaderChunk.project_vertex,
-        THREE.ShaderChunk.logdepthbuf_vertex,
-        THREE.ShaderChunk.clipping_planes_vertex,
-
-        "vViewPosition = - mvPosition.xyz;",
-
-        THREE.ShaderChunk.worldpos_vertex,
-        THREE.ShaderChunk.envmap_vertex,
-        THREE.ShaderChunk.shadowmap_vertex,
-        THREE.ShaderChunk.fog_vertex,
-
-        "}",
-    ].join("\n"),
-
-    fragmentShader: [
-        "#define PHONG",
-
-        "uniform vec3 diffuse;",
-        "uniform vec3 emissive;",
-        "uniform vec3 specular;",
-        "uniform float shininess;",
-        "uniform float opacity;",
-
-        THREE.ShaderChunk.common,
-        THREE.ShaderChunk.packing,
-        THREE.ShaderChunk.dithering_pars_fragment,
-        THREE.ShaderChunk.color_pars_fragment,
-        THREE.ShaderChunk.uv_pars_fragment,
-        THREE.ShaderChunk.uv2_pars_fragment,
-        THREE.ShaderChunk.map_pars_fragment,
-        THREE.ShaderChunk.alphamap_pars_fragment,
-        THREE.ShaderChunk.aomap_pars_fragment,
-        THREE.ShaderChunk.lightmap_pars_fragment,
-        THREE.ShaderChunk.emissivemap_pars_fragment,
-        THREE.ShaderChunk.envmap_common_pars_fragment,
-        THREE.ShaderChunk.envmap_pars_fragment,
-        THREE.ShaderChunk.cube_uv_reflection_fragment,
-        THREE.ShaderChunk.fog_pars_fragment,
-        THREE.ShaderChunk.bsdfs,
-        THREE.ShaderChunk.lights_pars_begin,
-        THREE.ShaderChunk.lights_phong_pars_fragment,
-        THREE.ShaderChunk.shadowmap_pars_fragment,
-        THREE.ShaderChunk.bumpmap_pars_fragment,
-        THREE.ShaderChunk.normalmap_pars_fragment,
-        THREE.ShaderChunk.specularmap_pars_fragment,
-        THREE.ShaderChunk.logdepthbuf_pars_fragment,
-        THREE.ShaderChunk.clipping_planes_pars_fragment,
-
-        "void main() {",
-
-        THREE.ShaderChunk.clipping_planes_fragment,
-
-        "vec4 diffuseColor = vec4( diffuse, opacity );",
-        "ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
-        "vec3 totalEmissiveRadiance = emissive;",
-
-        THREE.ShaderChunk.logdepthbuf_fragment,
-        THREE.ShaderChunk.map_fragment,
-        THREE.ShaderChunk.color_fragment,
-        THREE.ShaderChunk.alphamap_fragment,
-        THREE.ShaderChunk.alphatest_fragment,
-        THREE.ShaderChunk.specularmap_fragment,
-        THREE.ShaderChunk.normal_fragment_begin,
-        THREE.ShaderChunk.normal_fragment_maps,
-        THREE.ShaderChunk.emissivemap_fragment,
-
-        // accumulation
-        THREE.ShaderChunk.lights_phong_fragment,
-        THREE.ShaderChunk.lights_fragment_begin,
-        THREE.ShaderChunk.lights_fragment_maps,
-        THREE.ShaderChunk.lights_fragment_end,
-
-        // modulation
-        THREE.ShaderChunk.aomap_fragment,
-
-        "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
-
-        THREE.ShaderChunk.envmap_fragment,
-
-        "gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
-
-        THREE.ShaderChunk.tonemapping_fragment,
-        THREE.ShaderChunk.encodings_fragment,
-        THREE.ShaderChunk.fog_fragment,
-        THREE.ShaderChunk.premultiplied_alpha_fragment,
-        THREE.ShaderChunk.dithering_fragment,
-        "}",
-    ].join("\n")
-};
-
-PackedPhongMaterial.prototype = Object.create(THREE.MeshPhongMaterial.prototype);
-
-export { GeometryPackingUtils, PackedPhongMaterial };

+ 87 - 41
examples/webgl_materials_packed_geometry.html → examples/webgl_geometry_compression.html

@@ -10,7 +10,7 @@
 
 <body>
 	<div id="info">
-		<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Geometry Packing Example<br />
+		<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Geometry Compression Example<br />
 	</div>
 
 	<script type="module">
@@ -20,7 +20,7 @@
 		import Stats from './jsm/libs/stats.module.js';
 
 		import { OrbitControls } from './jsm/controls/OrbitControls.js';
-		import { GeometryPackingUtils, PackedPhongMaterial } from './jsm/utils/GeometryPackingUtils.js';
+		import { GeometryCompressionUtils, PackedPhongMaterial } from './jsm/utils/GeometryCompressionUtils.js';
 		import { GUI } from './jsm/libs/dat.gui.module.js';
 
 		var statsEnabled = true;
@@ -29,23 +29,48 @@
 
 		var camera, scene, renderer, controls;
 
+		var lights = [];
+
 		var data = {
 			flatShading: false,
-			wireframe: false, 
-			basicEncode: false,
-			octEncode: false, 
-			defaultEncode: false, 
-			quantizePosition: false
+			wireframe: true,
+			basicEncodeNormal: false,
+			octEncodeNormal: false,
+			defaultEncodeNormal: false,
+			quantizeEncodePosition: false,
+			defaultEncodeUV: false,
+			lightsRotation: 1
 		};
 
+
 		var radius = 100;
+		var detail = 5;
+		var uvScale = [];
+
+		// materials
+		var lineMaterial = new THREE.LineBasicMaterial({ color: 0xaaaaaa, transparent: true, opacity: 0.5 });
+		var meshMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x111111, flatShading: data.flatShading });
+
+		var texture = new THREE.TextureLoader().load("textures/uv_grid_opengl.jpg");
+		// var texture = new THREE.TextureLoader().load("textures/uv_grid_opengl_8k.jpg");
+		// var texture = new THREE.TextureLoader().load("textures/brick_diffuse.jpg");
+
+		texture.wrapS = THREE.RepeatWrapping;
+		texture.wrapT = THREE.RepeatWrapping;
+
+		// meshMaterial.map = texture;
+
 
 		init();
 		animate();
 
 		function init() {
 
-			// GeometryPackingUtils.changeShaderChunk();
+			//
+
+			// for (let i = 0; i < 1000000; i++) {
+			// 	uvScale.push(Math.random() * 100);
+			// }
 
 			container = document.createElement('div');
 			document.body.appendChild(container);
@@ -68,7 +93,6 @@
 			controls = new OrbitControls(camera, renderer.domElement);
 
 			//
-			var lights = [];
 			lights[0] = new THREE.PointLight(0xffffff, 1, 0);
 			lights[1] = new THREE.PointLight(0xffffff, 1, 0);
 			lights[2] = new THREE.PointLight(0xffffff, 1, 0);
@@ -85,21 +109,7 @@
 			scene.add(new THREE.AxesHelper(radius * 5));
 
 			//
-
-			var lineMaterial = new THREE.LineBasicMaterial({ color: 0x999999, transparent: true, opacity: 0.5 });
-			var meshMaterial = new THREE.MeshPhongMaterial({ color: 0x156289, emissive: 0x072534, flatShading: data.flatShading });
-
-			var texture = new THREE.TextureLoader().load("textures/disturb.jpg");
-
-			texture.wrapS = THREE.RepeatWrapping;
-			texture.wrapT = THREE.RepeatWrapping;
-
-			// meshMaterial.map = texture;
-
-			var ballGeom = new THREE.IcosahedronBufferGeometry( radius, 4 );
-			// var ballGeom = new THREE.CylinderBufferGeometry( 5, 5, 20, 32 );
-			// var ballGeom = new THREE.OctahedronBufferGeometry( 10, 4 );
-			// var ballGeom = new THREE.BoxBufferGeometry( 10, 10, 10 );
+			var ballGeom = newGeometry();
 
 			var ballMesh = new THREE.Mesh(ballGeom, meshMaterial);
 			var ballLineSegments = new THREE.LineSegments(ballGeom, lineMaterial);
@@ -112,17 +122,35 @@
 
 			gui = new GUI();
 
-			function generateGeometry() {
+			function newGeometry() {
+				var geom = new THREE.IcosahedronBufferGeometry(radius, detail);
+				// var geom = new THREE.CylinderBufferGeometry( 5, 5, 20, 32 );
+				// var geom = new THREE.OctahedronBufferGeometry( radius, detail );
+				// var geom = new THREE.BoxBufferGeometry(radius, radius, radius, detail, detail, detail);
+
+				// for (let i = 0; i < geom.attributes.uv.array.length; i += 2) {
+				// 	if (i % 13 == 0){
+				// 		geom.attributes.uv.array[i] = geom.attributes.uv.array[i] * uvScale[i];
+				// 	}
+				// }
 
-				updateGroupGeometry(ballMesh, ballLineSegments, new THREE.IcosahedronBufferGeometry( radius, 4 ), data);
+				return geom;
+			}
+
+			function generateGeometry() {
 
+				updateGroupGeometry(
+					ballMesh,
+					ballLineSegments,
+					newGeometry(),
+					data);
 			}
 
 			function updateMaterial() {
 				ballMesh.material = new THREE.MeshPhongMaterial({ color: 0x156289, emissive: 0x072534, flatShading: data.flatShading });
 			}
 
-			function updateLineSegments(){
+			function updateLineSegments() {
 				ballLineSegments.visible = data.wireframe;
 			}
 
@@ -130,10 +158,12 @@
 			folder.open();
 			folder.add(data, 'flatShading', false).onChange(updateMaterial);
 			folder.add(data, 'wireframe', false).onChange(updateLineSegments);
-			folder.add(data, 'basicEncode', false).onChange(generateGeometry);
-			folder.add(data, 'octEncode', false).onChange(generateGeometry);
-			folder.add(data, 'defaultEncode', false).onChange(generateGeometry);
-			folder.add(data, 'quantizePosition', false).onChange(generateGeometry);
+			folder.add(data, 'basicEncodeNormal', false).onChange(generateGeometry);
+			folder.add(data, 'octEncodeNormal', false).onChange(generateGeometry);
+			folder.add(data, 'defaultEncodeNormal', false).onChange(generateGeometry);
+			folder.add(data, 'quantizeEncodePosition', false).onChange(generateGeometry);
+			folder.add(data, 'defaultEncodeUV', false).onChange(generateGeometry);
+			folder.add(data, 'lightsRotation', 1, 10, 1);
 
 			scene.add(ballMesh);
 
@@ -149,6 +179,9 @@
 			}
 
 			window.addEventListener('resize', onWindowResize, false);
+			window.addEventListener('keydown', (event) => {
+				changeLightsPos();
+			}, false);
 
 		}
 
@@ -163,6 +196,15 @@
 
 		}
 
+		//
+		function changeLightsPos() {
+			lights.forEach(light => {
+				var direction = light.position.clone();
+				direction.applyAxisAngle(new THREE.Vector3(0, 1, 0), data.lightsRotation / 180 * Math.PI);
+				light.position.add(direction.sub(light.position));
+			});
+		}
+
 		//
 
 		function animate() {
@@ -193,27 +235,31 @@
 
 			lineSegments.geometry = new THREE.WireframeGeometry(geometry);
 			mesh.geometry = geometry;
-			mesh.material = new THREE.MeshPhongMaterial({ color: 0x156289, emissive: 0x072534, flatShading: data.flatShading });
+			mesh.material = meshMaterial;
 
 			var method = "";
-			if (data.basicEncode) {
+			if (data.basicEncodeNormal) {
 				method = "BASIC";
-			} else if (data.octEncode) {
+			} else if (data.octEncodeNormal) {
 				method = "OCT";
-			} else if (data.defaultEncode) {
+			} else if (data.defaultEncodeNormal) {
 				method = "DEFAULT";
 			}
 
 			if (method != "") {
-				GeometryPackingUtils.packNormals(mesh, method);
-			} 
+				GeometryCompressionUtils.packNormals(mesh, method);
+			}
+
+			if (data.quantizeEncodePosition) {
+				GeometryCompressionUtils.packPositions(mesh);
+			}
 
-			if (data.quantizePosition){
-				GeometryPackingUtils.packPositions(mesh);
+			if (method == "" && !data.quantizeEncodePosition) {
+				mesh.material = meshMaterial;
 			}
 
-			if (method == "" && !data.quantizePosition) {
-				mesh.material = new THREE.MeshPhongMaterial({ color: 0x156289, emissive: 0x072534, flatShading: data.flatShading });
+			if (data.defaultEncodeUV) {
+				GeometryCompressionUtils.packUvs(mesh);
 			}
 
 		}