UNPKG

odin

Version:

Node.js Canvas/WebGL Javascript Game Framework

670 lines (522 loc) 24.5 kB
if (typeof(define) !== "function") { var define = require("amdefine")(module); } define([ "odin/core/assets/asset", "odin/core/assets/bone", "odin/math/vec2", "odin/math/vec3", "odin/math/vec4", "odin/math/color" ], function(Asset, Bone, Vec2, Vec3, Vec4, Color) { "use strict"; function Mesh(opts) { opts || (opts = {}); Asset.call(this, opts); this.vertices = opts.vertices != undefined ? opts.vertices : []; this.normals = opts.normals != undefined ? opts.normals : []; this.tangents = opts.tangents != undefined ? opts.tangents : []; this.indices = opts.indices != undefined ? opts.indices : []; this.colors = opts.colors != undefined ? opts.colors : []; this.uvs = opts.uvs != undefined ? opts.uvs : []; this.bones = opts.bones != undefined ? opts.bones : []; this.boneIndices = opts.boneIndices != undefined ? opts.boneIndices : []; this.boneWeights = opts.boneWeights != undefined ? opts.boneWeights : []; this.dynamic = opts.dynamic != undefined ? !! opts.dynamic : false; this.useBones = opts.useBones != undefined ? !! opts.useBones : false; this.aabb = new AABB3; if (opts.vertices) this.aabb.fromPoints(this.vertices); this._webgl = {}; this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; this.tangentsNeedUpdate = true; this.indicesNeedUpdate = true; this.colorsNeedUpdate = true; this.uvsNeedUpdate = true; this.boneIndicesNeedUpdate = true; this.boneWeightsNeedUpdate = true; if (opts.json) this.fromJSON(opts.json); } Asset.extend(Mesh); Mesh.prototype.copy = function(other) { Asset.prototype.copy.call(this, other); var vertices = this.vertices, otherVertices = other.vertices, normals = this.normals, otherNormals = other.normals, tangents = this.tangents, otherTangents = other.tangents, indices = this.indices, otherIndices = other.indices, colors = this.colors, otherColors = other.colors, uvs = this.uvs, otherUvs = other.uvs, bones = this.bones, otherBones = other.bones, boneIndices = this.boneIndices, otherBoneIndices = other.boneIndices, boneWeights = this.boneWeights, otherBoneWeights = other.boneWeights, i; vertices.length = otherVertices.length; normals.length = otherNormals.length; tangents.length = otherTangents.length; indices.length = otherIndices.length; colors.length = otherColors.length; uvs.length = otherUvs.length; bones.length = otherBones.length; boneIndices.length = otherBoneIndices.length; boneWeights.length = otherBoneWeights.length; for (i = otherVertices.length; i--;) vertices[i] = (vertices[i] || new Vec3).copy(otherVertices[i]); for (i = otherNormals.length; i--;) normals[i] = (normals[i] || new Vec3).copy(otherNormals[i]); for (i = otherTangents.length; i--;) tangents[i] = (tangents[i] || new Vec4).copy(otherTangents[i]); for (i = otherIndices.length; i--;) indices[i] = otherIndices[i]; for (i = otherColors.length; i--;) colors[i] = (colors[i] || new Color).copy(otherColors[i]); for (i = otherUvs.length; i--;) uvs[i] = (uvs[i] || new Vec2).copy(otherUvs[i]); for (i = otherBones.length; i--;) bones[i] = (bones[i] || new Bone).copy(otherBones[i]); for (i = otherBoneIndices.length; i--;) boneIndices[i] = otherBoneIndices[i]; for (i = otherBoneWeights.length; i--;) boneWeights[i] = otherBoneWeights[i]; this.dynamic = other.dynamic; this.useBones = other.useBones; this.aabb.fromPoints(this.vertices); this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; this.tangentsNeedUpdate = true; this.indicesNeedUpdate = true; this.colorsNeedUpdate = true; this.uvsNeedUpdate = true; this.boneIndicesNeedUpdate = true; this.boneWeightsNeedUpdate = true; return this; }; Mesh.prototype.clear = function() { Asset.prototype.clear.call(this); this.vertices.length = 0; this.normals.length = 0; this.tangents.length = 0; this.indices.length = 0; this.colors.length = 0; this.uvs.length = 0; this.bones.length = 0; this.boneIndices.length = 0; this.boneWeights.length = 0; this.aabb.clear(); this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; this.tangentsNeedUpdate = true; this.indicesNeedUpdate = true; this.colorsNeedUpdate = true; this.uvsNeedUpdate = true; this.boneIndicesNeedUpdate = true; this.boneWeightsNeedUpdate = true; return this; }; var EMPTY_ARRAY = []; Mesh.prototype.parse = function(raw) { Asset.prototype.parse.call(this, raw); var vertices = this.vertices, normals = this.normals, tangents = this.tangents, indices = this.indices, colors = this.colors, uvs = this.uvs, bones = this.bones, boneWeights = this.boneWeights, boneIndices = this.boneIndices, bone, items, item, i, il; vertices.length = normals.length = tangents.length = indices.length = colors.length = uvs.length = 0; bones.length = boneWeights.length = boneIndices.length = 0; items = raw.vertices || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i += 3) vertices.push(new Vec3(items[i], items[i + 1], items[i + 2])); items = raw.normals || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i += 3) normals.push(new Vec3(items[i], items[i + 1], items[i + 2])); items = raw.tangents || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i += 4) tangents.push(new Vec4(items[i], items[i + 1], items[i + 2], items[i + 3])); items = raw.indices || raw.faces || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i += 3) indices.push(items[i], items[i + 1], items[i + 2]); items = raw.colors || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i += 3) colors.push(new Color(items[i], items[i + 1], items[i + 2])); items = raw.uvs || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i += 2) uvs.push(new Vec2(items[i], items[i + 1])); items = raw.bones || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i++) { item = items[i]; bone = new Bone(item.parent, item.name); bone.position.fromArray(item.position); bone.rotation.fromArray(item.rotation); bone.scale.fromArray(item.scale); bone.bindPose.fromArray(item.bindPose); bone.skinned = item.skinned; bone.inheritPoosition = item.inheritPoosition; bone.inheritRotation = item.inheritRotation; bone.inheritScale = item.inheritScale; bones.push(bone); } if (items.length) this.useBones = true; items = raw.boneWeights || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i++) boneWeights.push(items[i]); items = raw.boneIndices || EMPTY_ARRAY; for (i = 0, il = items.length; i < il; i++) boneIndices.push(items[i]); this.aabb.fromPoints(this.vertices); this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; this.tangentsNeedUpdate = true; this.indicesNeedUpdate = true; this.colorsNeedUpdate = true; this.uvsNeedUpdate = true; this.boneIndicesNeedUpdate = true; this.boneWeightsNeedUpdate = true; return this; }; Mesh.prototype.calculateAABB = function() { this.aabb.fromPoints(this.vertices); }; Mesh.prototype.calculateNormals = function() { var u = new Vec3, v = new Vec3, uv = new Vec3, faceNormal = new Vec3; return function() { var vertices = this.vertices, normals = this.normals, indices = this.indices, a, b, c, va, vb, vc, i; for (i = vertices.length; i--;)(normals[i] || (normals[i] = new Vec3)).set(0, 0, 0); for (i = indices.length; i -= 3;) { a = indices[i]; b = indices[i + 1]; c = indices[i + 2]; va = vertices[a]; vb = vertices[b]; vc = vertices[c]; u.vsub(vc, vb); v.vsub(va, vb); uv.vcross(u, v); faceNormal.copy(uv).normalize(); normals[a].add(faceNormal); normals[b].add(faceNormal); normals[c].add(faceNormal); } for (i = indices.length; i -= 3;) { normals[indices[i]].normalize(); normals[indices[i + 1]].normalize(); normals[indices[i + 2]].normalize(); } this.normalsNeedUpdate = true; return this; }; }(); Mesh.prototype.calculateTangents = function() { var tan1 = [], tan2 = [], sdir = new Vec3, tdir = new Vec3, n = new Vec3, t = new Vec3, tmp1 = new Vec3, tmp2 = new Vec3; return function() { var indices = this.indices, vertices = this.vertices, normals = this.normals, tangents = this.tangents, uvs = this.uvs, v1, v2, v3, w1, w2, w3, x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, a, b, c, r, w, i; for (i = vertices.length; i--;) { (tan1[i] || (tan1[i] = new Vec3)).set(0, 0, 0); (tan2[i] || (tan2[i] = new Vec3)).set(0, 0, 0); (tangents[i] || (tangents[i] = new Vec4)).set(0, 0, 0, 1); } for (i = vertices.length; i--;) uvs[i] = uvs[i] || (uvs[i] = new Vec2); for (i = indices.length; i -= 3;) { a = indices[i]; b = indices[i + 1]; c = indices[i + 2]; v1 = vertices[a]; v2 = vertices[b]; v3 = vertices[c]; w1 = uvs[a]; w2 = uvs[b]; w3 = uvs[c]; x1 = v2.x - v1.x; x2 = v3.x - v1.x; y1 = v2.y - v1.y; y2 = v3.y - v1.y; z1 = v2.z - v1.z; z2 = v3.z - v1.z; s1 = w2.x - w1.x; s2 = w3.x - w1.x; t1 = w2.y - w1.y; t2 = w3.y - w1.y; r = s1 * t2 - s2 * t1; r = r !== 0 ? 1 / r : 0; sdir.set( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r ); tdir.set( (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r ); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } for (i = vertices.length; i--;) { t.copy(tan1[i]); n.copy(normals[i]); tmp1.copy(t); tmp1.sub(n.smul(n.dot(t))).normalize(); n.copy(normals[i]); tmp2.vcross(n, t); w = (tmp2.dot(tan2[i]) < 0) ? -1 : 1; tangents[i].set(tmp1.x, tmp1.y, tmp1.z, w); } this.tangentsNeedUpdate = true; return this; }; }(); Mesh.prototype.toJSON = function(json, pack) { json = Asset.prototype.toJSON.call(this, json, pack); var vertices = this.vertices, jsonVertices = json.vertices || (json.vertices = []), normals = this.normals, jsonNormals = json.normals || (json.normals = []), tangents = this.tangents, jsonTangents = json.tangents || (json.tangents = []), indices = this.indices, jsonIndices = json.indices || (json.indices = []), colors = this.colors, jsonColors = json.colors || (json.colors = []), uvs = this.uvs, jsonUvs = json.uvs || (json.uvs = []), bones = this.bones, jsonBones = json.bones || (json.bones = []), boneIndices = this.boneIndices, jsonBoneIndices = json.boneIndices || (json.boneIndices = []), boneWeights = this.boneWeights, jsonBoneWeights = json.boneWeights || (json.boneWeights = []), i; jsonVertices.length = vertices.length; jsonNormals.length = normals.length; jsonTangents.length = tangents.length; jsonIndices.length = indices.length; jsonColors.length = colors.length; jsonUvs.length = uvs.length; jsonBones.length = bones.length; jsonBoneIndices.length = boneIndices.length; jsonBoneWeights.length = boneWeights.length; for (i = vertices.length; i--;) jsonVertices[i] = vertices[i].toJSON(jsonVertices[i]); for (i = normals.length; i--;) jsonNormals[i] = normals[i].toJSON(jsonNormals[i]); for (i = tangents.length; i--;) jsonTangents[i] = tangents[i].toJSON(jsonTangents[i]); for (i = indices.length; i--;) indices[i] = jsonIndices[i]; for (i = colors.length; i--;) jsonColors[i] = colors[i].toJSON(jsonColors[i]); for (i = uvs.length; i--;) jsonUvs[i] = uvs[i].toJSON(jsonUvs[i]); for (i = bones.length; i--;) jsonBones[i] = bones[i].toJSON(jsonBones[i]); for (i = boneIndices.length; i--;) boneIndices[i] = jsonBoneIndices[i]; for (i = boneWeights.length; i--;) boneWeights[i] = jsonBoneWeights[i]; json.dynamic = this.dynamic; json.useBones = this.useBones; return json; }; Mesh.prototype.fromJSON = function(json) { Asset.prototype.fromJSON.call(this, json); var vertices = this.vertices, jsonVertices = json.vertices, normals = this.normals, jsonNormals = json.normals, tangents = this.tangents, jsonTangents = json.tangents, indices = this.indices, jsonIndices = json.indices, colors = this.colors, jsonColors = json.colors, uvs = this.uvs, jsonUvs = json.uvs, bones = this.bones, jsonBones = json.bones, boneIndices = this.boneIndices, jsonBoneIndices = json.boneIndices, boneWeights = this.boneWeights, jsonBoneWeights = json.boneWeights, i; vertices.length = jsonVertices.length; normals.length = jsonNormals.length; tangents.length = jsonTangents.length; indices.length = jsonIndices.length; colors.length = jsonColors.length; uvs.length = jsonUvs.length; bones.length = jsonBones.length; boneIndices.length = jsonBoneIndices.length; boneWeights.length = jsonBoneWeights.length; for (i = jsonVertices.length; i--;) vertices[i] = (vertices[i] || new Vec3).copy(jsonVertices[i]); for (i = jsonNormals.length; i--;) normals[i] = (normals[i] || new Vec3).copy.fromJSON(jsonNormals[i]); for (i = jsonTangents.length; i--;) tangents[i] = (tangents[i] || new Vec4).fromJSON(jsonTangents[i]); for (i = jsonIndices.length; i--;) indices[i] = jsonIndices[i]; for (i = jsonColors.length; i--;) colors[i] = (colors[i] || new Color).fromJSON(jsonColors[i]); for (i = jsonUvs.length; i--;) uvs[i] = (uvs[i] || new Vec2).fromJSON(jsonUvs[i]); for (i = jsonBones.length; i--;) bones[i] = (bones[i] || new Bone).fromJSON(jsonBones[i]); for (i = jsonBoneIndices.length; i--;) boneIndices[i] = jsonBoneIndices[i]; for (i = jsonBoneWeights.length; i--;) boneWeights[i] = jsonBoneWeights[i]; this.dynamic = json.dynamic; this.useBones = json.useBones; this.aabb.fromPoints(this.vertices); this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; this.tangentsNeedUpdate = true; this.indicesNeedUpdate = true; this.colorsNeedUpdate = true; this.uvsNeedUpdate = true; this.boneIndicesNeedUpdate = true; this.boneWeightsNeedUpdate = true; return this; }; var PI = Math.PI, HALF_PI = PI * 0.5, TWO_PI = PI * 2, sin = Math.sin, cos = Math.cos; Mesh.Sphere = function(opts) { opts || (opts = {}); var radius = opts.radius != undefined ? opts.radius : 0.5, segments = (opts.segments != undefined ? floor(max(opts.segments, 3)) : 16) + 1, rings = (opts.rings != undefined ? floor(max(opts.rings, 3)) : 8) + 2, R = 1 / (rings - 1), S = 1 / (segments - 1), r, s, x, y, z, a, b, c, d, mesh = new Mesh(opts), vertices = mesh.vertices, normals = mesh.normals, uvs = mesh.uvs, colors = mesh.colors, indices = mesh.indices; for (r = 0; r < rings; r++) { for (s = 0; s < segments; s++) { z = sin(-HALF_PI + PI * r * R); x = cos(TWO_PI * s * S) * sin(PI * r * R); y = sin(TWO_PI * s * S) * sin(PI * r * R); vertices.push(new Vec3(x, y, z).smul(radius)); normals.push(new Vec3(x, y, z)); uvs.push(new Vec2(s * S, r * R)); colors.push(new Vec3(s * S, r * R, 0)); } } for (r = 0; r < rings - 1; r++) { for (s = 0; s < segments - 1; s++) { a = r * segments + s; b = r * segments + (s + 1); c = (r + 1) * segments + (s + 1); d = (r + 1) * segments + s; indices.push(a, b, c); indices.push(a, c, d); } } mesh.calculateAABB(); mesh.load = false; return mesh; }; Mesh.Cube = function(opts) { opts || (opts = {}); var w = opts.width || 1, h = opts.height || 1, d = opts.depth || 1, hw = w * 0.5, hh = h * 0.5, hd = d * 0.5, ws = (opts.widthSegments || 1), hs = (opts.heightSegments || 1), ds = (opts.depthSegments || 1), mesh = new Mesh(opts); buildPlane(mesh, "z", "y", -1, -1, d, ds, h, hs, hw, ws); buildPlane(mesh, "z", "y", 1, -1, d, ds, h, hs, -hw, ws); buildPlane(mesh, "x", "z", 1, 1, w, ws, d, ds, hh, hs); buildPlane(mesh, "x", "z", 1, -1, w, ws, d, ds, -hh, hs); buildPlane(mesh, "x", "y", 1, -1, w, ws, h, hs, hd, ds); buildPlane(mesh, "x", "y", -1, -1, w, ws, h, hs, -hd, ds); mesh.calculateAABB(); mesh.load = false; return mesh; }; Mesh.Plane = function(opts) { opts || (opts = {}); var w = opts.width || 1, h = opts.height || 1, hw = w * 0.5, hh = h * 0.5, ws = (opts.widthSegments || 1), hs = (opts.heightSegments || 1), mesh = new Mesh(opts); buildPlane(mesh, "x", "y", 1, 1, w, ws, h, hs, 0, 0); mesh.calculateAABB(); mesh.load = false; return mesh; }; function buildPlane(mesh, u, v, udir, vdir, width, ws, height, hs, depth, ds) { var vertices = mesh.vertices, normals = mesh.normals, indices = mesh.indices, uvs = mesh.uvs, gridX = ws, gridY = hs, width_half = width / 2, height_half = height / 2, offset = vertices.length, w, ix, iy; if ((u === "x" && v === "y") || (u === "y" && v === "x")) { w = "z"; } else if ((u === "x" && v === "z") || (u === "z" && v === "x")) { w = "y"; gridY = ds; } else if ((u === "z" && v === "y") || (u === "y" && v === "z")) { w = "x"; gridX = ds; } var gridX1 = gridX + 1, gridY1 = gridY + 1, segment_width = width / gridX, segment_height = height / gridY, normal = new Vec3(); normal[w] = depth > 0 ? 1 : -1; for (iy = 0; iy < gridY1; iy++) { for (ix = 0; ix < gridX1; ix++) { var vector = new Vec3(); vector[u] = (ix * segment_width - width_half) * udir; vector[v] = (iy * segment_height - height_half) * vdir; vector[w] = depth; vertices.push(vector); } } for (iy = 0; iy < gridY; iy++) { for (ix = 0; ix < gridX; ix++) { var a = offset + (ix + gridX1 * iy), b = offset + (ix + gridX1 * (iy + 1)), c = offset + ((ix + 1) + gridX1 * (iy + 1)), d = offset + ((ix + 1) + gridX1 * iy), uva = new Vec2(ix / gridX, 1 - iy / gridY), uvb = new Vec2(ix / gridX, 1 - (iy + 1) / gridY), uvc = new Vec2((ix + 1) / gridX, 1 - iy / gridY), uvd = new Vec2((ix + 1) / gridX, 1 - (iy + 1) / gridY); normals.push(normal.clone(), normal.clone(), normal.clone(), normal.clone()); uvs.push(uva, uvb, uvc, uvd); indices.push( a, b, c, a, c, d ); } } } Mesh.Bone = Bone; return Mesh; } );