xushiting 4 سال پیش
والد
کامیت
46ba5fba9c
1فایلهای تغییر یافته به همراه797 افزوده شده و 0 حذف شده
  1. 797 0
      js/OBJLoader.js

+ 797 - 0
js/OBJLoader.js

@@ -0,0 +1,797 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+function OBJLoader_plugin() {
+    IV.THREE.OBJLoader = (function () {
+
+        // o object_name | g group_name
+        var object_pattern = /^[og]\s*(.+)?/;
+        // mtllib file_reference
+        var material_library_pattern = /^mtllib /;
+        // usemtl material_name
+        var material_use_pattern = /^usemtl /;
+
+        function ParserState() {
+
+            var state = {
+                objects: [],
+                object: {},
+
+                vertices: [],
+                normals: [],
+                colors: [],
+                uvs: [],
+
+                materialLibraries: [],
+
+                startObject: function (name, fromDeclaration) {
+
+                    // If the current object (initial from reset) is not from a g/o declaration in the parsed
+                    // file. We need to use it for the first parsed g/o to keep things in sync.
+                    if (this.object && this.object.fromDeclaration === false) {
+
+                        this.object.name = name;
+                        this.object.fromDeclaration = (fromDeclaration !== false);
+                        return;
+
+                    }
+
+                    var previousMaterial = (this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined);
+
+                    if (this.object && typeof this.object._finalize === 'function') {
+
+                        this.object._finalize(true);
+
+                    }
+
+                    this.object = {
+                        name: name || '',
+                        fromDeclaration: (fromDeclaration !== false),
+
+                        geometry: {
+                            vertices: [],
+                            normals: [],
+                            colors: [],
+                            uvs: []
+                        },
+                        materials: [],
+                        smooth: true,
+
+                        startMaterial: function (name, libraries) {
+
+                            var previous = this._finalize(false);
+
+                            // New usemtl declaration overwrites an inherited material, except if faces were declared
+                            // after the material, then it must be preserved for proper MultiMaterial continuation.
+                            if (previous && (previous.inherited || previous.groupCount <= 0)) {
+
+                                this.materials.splice(previous.index, 1);
+
+                            }
+
+                            var material = {
+                                index: this.materials.length,
+                                name: name || '',
+                                mtllib: (Array.isArray(libraries) && libraries.length > 0 ? libraries[libraries.length - 1] : ''),
+                                smooth: (previous !== undefined ? previous.smooth : this.smooth),
+                                groupStart: (previous !== undefined ? previous.groupEnd : 0),
+                                groupEnd: -1,
+                                groupCount: -1,
+                                inherited: false,
+
+                                clone: function (index) {
+
+                                    var cloned = {
+                                        index: (typeof index === 'number' ? index : this.index),
+                                        name: this.name,
+                                        mtllib: this.mtllib,
+                                        smooth: this.smooth,
+                                        groupStart: 0,
+                                        groupEnd: -1,
+                                        groupCount: -1,
+                                        inherited: false
+                                    };
+                                    cloned.clone = this.clone.bind(cloned);
+                                    return cloned;
+
+                                }
+                            };
+
+                            this.materials.push(material);
+
+                            return material;
+
+                        },
+
+                        currentMaterial: function () {
+
+                            if (this.materials.length > 0) {
+
+                                return this.materials[this.materials.length - 1];
+
+                            }
+
+                            return undefined;
+
+                        },
+
+                        _finalize: function (end) {
+
+                            var lastMultiMaterial = this.currentMaterial();
+                            if (lastMultiMaterial && lastMultiMaterial.groupEnd === -1) {
+
+                                lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
+                                lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
+                                lastMultiMaterial.inherited = false;
+
+                            }
+
+                            // Ignore objects tail materials if no face declarations followed them before a new o/g started.
+                            if (end && this.materials.length > 1) {
+
+                                for (var mi = this.materials.length - 1; mi >= 0; mi--) {
+
+                                    if (this.materials[mi].groupCount <= 0) {
+
+                                        this.materials.splice(mi, 1);
+
+                                    }
+
+                                }
+
+                            }
+
+                            // Guarantee at least one empty material, this makes the creation later more straight forward.
+                            if (end && this.materials.length === 0) {
+
+                                this.materials.push({
+                                    name: '',
+                                    smooth: this.smooth
+                                });
+
+                            }
+
+                            return lastMultiMaterial;
+
+                        }
+                    };
+
+                    // Inherit previous objects material.
+                    // Spec tells us that a declared material must be set to all objects until a new material is declared.
+                    // If a usemtl declaration is encountered while this new object is being parsed, it will
+                    // overwrite the inherited material. Exception being that there was already face declarations
+                    // to the inherited material, then it will be preserved for proper MultiMaterial continuation.
+
+                    if (previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function') {
+
+                        var declared = previousMaterial.clone(0);
+                        declared.inherited = true;
+                        this.object.materials.push(declared);
+
+                    }
+
+                    this.objects.push(this.object);
+
+                },
+
+                finalize: function () {
+
+                    if (this.object && typeof this.object._finalize === 'function') {
+
+                        this.object._finalize(true);
+
+                    }
+
+                },
+
+                parseVertexIndex: function (value, len) {
+
+                    var index = parseInt(value, 10);
+                    return (index >= 0 ? index - 1 : index + len / 3) * 3;
+
+                },
+
+                parseNormalIndex: function (value, len) {
+
+                    var index = parseInt(value, 10);
+                    return (index >= 0 ? index - 1 : index + len / 3) * 3;
+
+                },
+
+                parseUVIndex: function (value, len) {
+
+                    var index = parseInt(value, 10);
+                    return (index >= 0 ? index - 1 : index + len / 2) * 2;
+
+                },
+
+                addVertex: function (a, b, c) {
+
+                    var src = this.vertices;
+                    var dst = this.object.geometry.vertices;
+
+                    dst.push(src[a + 0], src[a + 1], src[a + 2]);
+                    dst.push(src[b + 0], src[b + 1], src[b + 2]);
+                    dst.push(src[c + 0], src[c + 1], src[c + 2]);
+
+                },
+
+                addVertexPoint: function (a) {
+
+                    var src = this.vertices;
+                    var dst = this.object.geometry.vertices;
+
+                    dst.push(src[a + 0], src[a + 1], src[a + 2]);
+
+                },
+
+                addVertexLine: function (a) {
+
+                    var src = this.vertices;
+                    var dst = this.object.geometry.vertices;
+
+                    dst.push(src[a + 0], src[a + 1], src[a + 2]);
+
+                },
+
+                addNormal: function (a, b, c) {
+
+                    var src = this.normals;
+                    var dst = this.object.geometry.normals;
+
+                    dst.push(src[a + 0], src[a + 1], src[a + 2]);
+                    dst.push(src[b + 0], src[b + 1], src[b + 2]);
+                    dst.push(src[c + 0], src[c + 1], src[c + 2]);
+
+                },
+
+                addColor: function (a, b, c) {
+
+                    var src = this.colors;
+                    var dst = this.object.geometry.colors;
+
+                    dst.push(src[a + 0], src[a + 1], src[a + 2]);
+                    dst.push(src[b + 0], src[b + 1], src[b + 2]);
+                    dst.push(src[c + 0], src[c + 1], src[c + 2]);
+
+                },
+
+                addUV: function (a, b, c) {
+
+                    var src = this.uvs;
+                    var dst = this.object.geometry.uvs;
+
+                    dst.push(src[a + 0], src[a + 1]);
+                    dst.push(src[b + 0], src[b + 1]);
+                    dst.push(src[c + 0], src[c + 1]);
+
+                },
+
+                addUVLine: function (a) {
+
+                    var src = this.uvs;
+                    var dst = this.object.geometry.uvs;
+
+                    dst.push(src[a + 0], src[a + 1]);
+
+                },
+
+                addFace: function (a, b, c, ua, ub, uc, na, nb, nc) {
+
+                    var vLen = this.vertices.length;
+
+                    var ia = this.parseVertexIndex(a, vLen);
+                    var ib = this.parseVertexIndex(b, vLen);
+                    var ic = this.parseVertexIndex(c, vLen);
+
+                    this.addVertex(ia, ib, ic);
+
+                    if (ua !== undefined && ua !== '') {
+
+                        var uvLen = this.uvs.length;
+                        ia = this.parseUVIndex(ua, uvLen);
+                        ib = this.parseUVIndex(ub, uvLen);
+                        ic = this.parseUVIndex(uc, uvLen);
+                        this.addUV(ia, ib, ic);
+
+                    }
+
+                    if (na !== undefined && na !== '') {
+
+                        // Normals are many times the same. If so, skip function call and parseInt.
+                        var nLen = this.normals.length;
+                        ia = this.parseNormalIndex(na, nLen);
+
+                        ib = na === nb ? ia : this.parseNormalIndex(nb, nLen);
+                        ic = na === nc ? ia : this.parseNormalIndex(nc, nLen);
+
+                        this.addNormal(ia, ib, ic);
+
+                    }
+
+                    if (this.colors.length > 0) {
+
+                        this.addColor(ia, ib, ic);
+
+                    }
+
+                },
+
+                addPointGeometry: function (vertices) {
+
+                    this.object.geometry.type = 'Points';
+
+                    var vLen = this.vertices.length;
+
+                    for (var vi = 0, l = vertices.length; vi < l; vi++) {
+
+                        this.addVertexPoint(this.parseVertexIndex(vertices[vi], vLen));
+
+                    }
+
+                },
+
+                addLineGeometry: function (vertices, uvs) {
+
+                    this.object.geometry.type = 'Line';
+
+                    var vLen = this.vertices.length;
+                    var uvLen = this.uvs.length;
+
+                    for (var vi = 0, l = vertices.length; vi < l; vi++) {
+
+                        this.addVertexLine(this.parseVertexIndex(vertices[vi], vLen));
+
+                    }
+
+                    for (var uvi = 0, l = uvs.length; uvi < l; uvi++) {
+
+                        this.addUVLine(this.parseUVIndex(uvs[uvi], uvLen));
+
+                    }
+
+                }
+
+            };
+
+            state.startObject('', false);
+
+            return state;
+
+        }
+
+        //
+
+        function OBJLoader(manager) {
+
+            this.manager = (manager !== undefined) ? manager : IV.THREE.DefaultLoadingManager;
+
+            this.materials = null;
+
+        }
+
+        OBJLoader.prototype = {
+
+            constructor: OBJLoader,
+
+            load: function (url, onLoad, onProgress, onError) {
+
+                var scope = this;
+
+                var loader = new IV.THREE.FileLoader(scope.manager);
+                loader.setPath(this.path);
+                loader.load(url, function (text) {
+
+                    onLoad(scope.parse(text));
+
+                }, onProgress, onError);
+
+            },
+
+            setPath: function (value) {
+
+                this.path = value;
+
+                return this;
+
+            },
+
+            setMaterials: function (materials) {
+
+                this.materials = materials;
+
+                return this;
+
+            },
+
+            parse: function (text) {
+
+                console.time('OBJLoader');
+
+                var state = new ParserState();
+
+                if (text.indexOf('\r\n') !== -1) {
+
+                    // This is faster than String.split with regex that splits on both
+                    text = text.replace(/\r\n/g, '\n');
+
+                }
+
+                if (text.indexOf('\\\n') !== -1) {
+
+                    // join lines separated by a line continuation character (\)
+                    text = text.replace(/\\\n/g, '');
+
+                }
+
+                var lines = text.split('\n');
+                var line = '', lineFirstChar = '';
+                var lineLength = 0;
+                var result = [];
+
+                // Faster to just trim left side of the line. Use if available.
+                var trimLeft = (typeof ''.trimLeft === 'function');
+
+                for (var i = 0, l = lines.length; i < l; i++) {
+
+                    line = lines[i];
+
+                    line = trimLeft ? line.trimLeft() : line.trim();
+
+                    lineLength = line.length;
+
+                    if (lineLength === 0) continue;
+
+                    lineFirstChar = line.charAt(0);
+
+                    // @todo invoke passed in handler if any
+                    if (lineFirstChar === '#') continue;
+
+                    if (lineFirstChar === 'v') {
+
+                        var data = line.split(/\s+/);
+
+                        switch (data[0]) {
+
+                            case 'v':
+                                state.vertices.push(
+                                    parseFloat(data[1]),
+                                    parseFloat(data[2]),
+                                    parseFloat(data[3])
+                                );
+                                if (data.length === 8) {
+
+                                    state.colors.push(
+                                        parseFloat(data[4]),
+                                        parseFloat(data[5]),
+                                        parseFloat(data[6])
+                                    );
+
+                                }
+                                break;
+                            case 'vn':
+                                state.normals.push(
+                                    parseFloat(data[1]),
+                                    parseFloat(data[2]),
+                                    parseFloat(data[3])
+                                );
+                                break;
+                            case 'vt':
+                                state.uvs.push(
+                                    parseFloat(data[1]),
+                                    parseFloat(data[2])
+                                );
+                                break;
+
+                        }
+
+                    } else if (lineFirstChar === 'f') {
+
+                        var lineData = line.substr(1).trim();
+                        var vertexData = lineData.split(/\s+/);
+                        var faceVertices = [];
+
+                        // Parse the face vertex data into an easy to work with format
+
+                        for (var j = 0, jl = vertexData.length; j < jl; j++) {
+
+                            var vertex = vertexData[j];
+
+                            if (vertex.length > 0) {
+
+                                var vertexParts = vertex.split('/');
+                                faceVertices.push(vertexParts);
+
+                            }
+
+                        }
+
+                        // Draw an edge between the first vertex and all subsequent vertices to form an n-gon
+
+                        var v1 = faceVertices[0];
+
+                        for (var j = 1, jl = faceVertices.length - 1; j < jl; j++) {
+
+                            var v2 = faceVertices[j];
+                            var v3 = faceVertices[j + 1];
+
+                            state.addFace(
+                                v1[0], v2[0], v3[0],
+                                v1[1], v2[1], v3[1],
+                                v1[2], v2[2], v3[2]
+                            );
+
+                        }
+
+                    } else if (lineFirstChar === 'l') {
+
+                        var lineParts = line.substring(1).trim().split(" ");
+                        var lineVertices = [], lineUVs = [];
+
+                        if (line.indexOf("/") === -1) {
+
+                            lineVertices = lineParts;
+
+                        } else {
+
+                            for (var li = 0, llen = lineParts.length; li < llen; li++) {
+
+                                var parts = lineParts[li].split("/");
+
+                                if (parts[0] !== "") lineVertices.push(parts[0]);
+                                if (parts[1] !== "") lineUVs.push(parts[1]);
+
+                            }
+
+                        }
+                        state.addLineGeometry(lineVertices, lineUVs);
+
+                    } else if (lineFirstChar === 'p') {
+
+                        var lineData = line.substr(1).trim();
+                        var pointData = lineData.split(" ");
+
+                        state.addPointGeometry(pointData);
+
+                    } else if ((result = object_pattern.exec(line)) !== null) {
+
+                        // o object_name
+                        // or
+                        // g group_name
+
+                        // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
+                        // var name = result[ 0 ].substr( 1 ).trim();
+                        var name = (" " + result[0].substr(1).trim()).substr(1);
+
+                        state.startObject(name);
+
+                    } else if (material_use_pattern.test(line)) {
+
+                        // material
+
+                        state.object.startMaterial(line.substring(7).trim(), state.materialLibraries);
+
+                    } else if (material_library_pattern.test(line)) {
+
+                        // mtl file
+
+                        state.materialLibraries.push(line.substring(7).trim());
+
+                    } else if (lineFirstChar === 's') {
+
+                        result = line.split(' ');
+
+                        // smooth shading
+
+                        // @todo Handle files that have varying smooth values for a set of faces inside one geometry,
+                        // but does not define a usemtl for each face set.
+                        // This should be detected and a dummy material created (later MultiMaterial and geometry groups).
+                        // This requires some care to not create extra material on each smooth value for "normal" obj files.
+                        // where explicit usemtl defines geometry groups.
+                        // Example asset: examples/models/obj/cerberus/Cerberus.obj
+
+                        /*
+                         * http://paulbourke.net/dataformats/obj/
+                         * or
+                         * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
+                         *
+                         * From chapter "Grouping" Syntax explanation "s group_number":
+                         * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
+                         * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
+                         * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
+                         * than 0."
+                         */
+                        if (result.length > 1) {
+
+                            var value = result[1].trim().toLowerCase();
+                            state.object.smooth = (value !== '0' && value !== 'off');
+
+                        } else {
+
+                            // ZBrush can produce "s" lines #11707
+                            state.object.smooth = true;
+
+                        }
+                        var material = state.object.currentMaterial();
+                        if (material) material.smooth = state.object.smooth;
+
+                    } else {
+
+                        // Handle null terminated files without exception
+                        if (line === '\0') continue;
+
+                        throw new Error('IV.THREE.OBJLoader: Unexpected line: "' + line + '"');
+
+                    }
+
+                }
+
+                state.finalize();
+
+                var container = new IV.THREE.Group();
+                container.materialLibraries = [].concat(state.materialLibraries);
+
+                for (var i = 0, l = state.objects.length; i < l; i++) {
+
+                    var object = state.objects[i];
+                    var geometry = object.geometry;
+                    var materials = object.materials;
+                    var isLine = (geometry.type === 'Line');
+                    var isPoints = (geometry.type === 'Points');
+                    var hasVertexColors = false;
+
+                    // Skip o/g line declarations that did not follow with any faces
+                    if (geometry.vertices.length === 0) continue;
+
+                    var buffergeometry = new IV.THREE.BufferGeometry();
+
+                    buffergeometry.addAttribute('position', new IV.THREE.Float32BufferAttribute(geometry.vertices, 3));
+
+                    if (geometry.normals.length > 0) {
+
+                        buffergeometry.addAttribute('normal', new IV.THREE.Float32BufferAttribute(geometry.normals, 3));
+
+                    } else {
+
+                        buffergeometry.computeVertexNormals();
+
+                    }
+
+                    if (geometry.colors.length > 0) {
+
+                        hasVertexColors = true;
+                        buffergeometry.addAttribute('color', new IV.THREE.Float32BufferAttribute(geometry.colors, 3));
+
+                    }
+
+                    if (geometry.uvs.length > 0) {
+
+                        buffergeometry.addAttribute('uv', new IV.THREE.Float32BufferAttribute(geometry.uvs, 2));
+
+                    }
+
+                    // Create materials
+
+                    var createdMaterials = [];
+
+                    for (var mi = 0, miLen = materials.length; mi < miLen; mi++) {
+
+                        var sourceMaterial = materials[mi];
+                        var material = undefined;
+
+                        if (this.materials !== null) {
+
+                            material = this.materials.create(sourceMaterial.name);
+
+                            // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+                            if (isLine && material && !(material instanceof IV.THREE.LineBasicMaterial)) {
+
+                                var materialLine = new IV.THREE.LineBasicMaterial();
+                                IV.THREE.Material.prototype.copy.call(materialLine, material);
+                                materialLine.color.copy(material.color);
+                                materialLine.lights = false;
+                                material = materialLine;
+
+                            } else if (isPoints && material && !(material instanceof IV.THREE.PointsMaterial)) {
+
+                                var materialPoints = new IV.THREE.PointsMaterial({size: 10, sizeAttenuation: false});
+                                IV.THREE.Material.prototype.copy.call(materialPoints, material);
+                                materialPoints.color.copy(material.color);
+                                materialPoints.map = material.map;
+                                materialPoints.lights = false;
+                                material = materialPoints;
+
+                            }
+
+                        }
+
+                        if (!material) {
+
+                            if (isLine) {
+
+                                material = new IV.THREE.LineBasicMaterial();
+
+                            } else if (isPoints) {
+
+                                material = new IV.THREE.PointsMaterial({size: 1, sizeAttenuation: false});
+
+                            } else {
+
+                                material = new IV.THREE.MeshPhongMaterial();
+
+                            }
+
+                            material.name = sourceMaterial.name;
+
+                        }
+
+                        material.flatShading = sourceMaterial.smooth ? false : true;
+                        material.vertexColors = hasVertexColors ? IV.THREE.VertexColors : IV.THREE.NoColors;
+
+                        createdMaterials.push(material);
+
+                    }
+
+                    // Create mesh
+
+                    var mesh;
+
+                    if (createdMaterials.length > 1) {
+
+                        for (var mi = 0, miLen = materials.length; mi < miLen; mi++) {
+
+                            var sourceMaterial = materials[mi];
+                            buffergeometry.addGroup(sourceMaterial.groupStart, sourceMaterial.groupCount, mi);
+
+                        }
+
+                        if (isLine) {
+
+                            mesh = new IV.THREE.LineSegments(buffergeometry, createdMaterials);
+
+                        } else if (isPoints) {
+
+                            mesh = new IV.THREE.Points(buffergeometry, createdMaterials);
+
+                        } else {
+
+                            mesh = new IV.THREE.Mesh(buffergeometry, createdMaterials);
+
+                        }
+
+                    } else {
+
+                        if (isLine) {
+
+                            mesh = new IV.THREE.LineSegments(buffergeometry, createdMaterials[0]);
+
+                        } else if (isPoints) {
+
+                            mesh = new IV.THREE.Points(buffergeometry, createdMaterials[0]);
+
+                        } else {
+
+                            mesh = new IV.THREE.Mesh(buffergeometry, createdMaterials[0]);
+
+                        }
+
+                    }
+
+                    mesh.name = object.name;
+
+                    container.add(mesh);
+
+                }
+
+                console.timeEnd('OBJLoader');
+
+                return container;
+
+            }
+
+        };
+
+        return OBJLoader;
+
+    })();
+}