Move older Python code into python_cage_meshgen

This commit is contained in:
Chris Hodapp
2021-07-21 17:46:18 -04:00
parent a4ddcf4941
commit 31e27846cf
9 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# To-do items, wanted features, bugs:
## Cool
- More complicated: Examples of *merging*. I'm not sure on the theory
behind this.
## Annoying/boring
- https://en.wikipedia.org/wiki/Polygon_triangulation - do this to
fix my wave example!
- http://www.polygontriangulation.com/2018/07/triangulation-algorithm.html
- Clean up examples.ram_horn_branch(). The way I clean it up might
help inform some cleaner designs.
- I really need to standardize some of the behavior of fundamental
operations (with regard to things like sizes they generate). This
is behavior that, if it changes, will change a lot of things that I'm
trying to keep consistent so that my examples still work.
- Winding order. It is consistent through seemingly
everything, except for reflection and close_boundary_simple.
(When there are two parallel boundaries joined with something like
join_boundary_simple, traversing these boundaries in their actual order
to generate triangles - like in close_boundary_simple - will produce
opposite winding order on each. Imagine a transparent clock: seen from the
front, it moves clockwise, but seen from the back, it moves
counter-clockwise.)
- File that bug that I've seen in trimesh/three.js
(see trimesh_fail.ipynb)
- Why do I get the weird zig-zag pattern on the triangles,
despite larger numbers of them? Is it something in how I
twist the frames?
- How can I compute the *torsion* on a quad? I think it
comes down to this: torsion applied across the quad I'm
triangulating leading to neither diagonal being a
particularly good choice. Subdividing the boundary seems
to help, but other triangulation methods (e.g. turning a
quad to 4 triangles by adding the centroid) could be good
too.
- Facets/edges are just oriented the wrong way...
- Picking at random the diagonal on the quad to triangulate with
does seem to turn 'error' just to noise, and in its own way this
is preferable.
- Integrate parallel_transport work and reuse what I can
- /mnt/dev/graphics_misc/isosurfaces_2018_2019 - perhaps include my
spiral isosurface stuff from here
## Abstractions
- This has a lot of functions parametrized over a lot
of functions. Need to work with this somehow. (e.g. should
it subdivide this boundary? should it merge opening/closing
boundaries?)
- Some generators produce boundaries that can be directly merged
and produce sensible geometry. Some generators produce
boundaries that are only usable when they are further
transformed (and would produce degenerate geometry). What sort
of nomenclature captures this?
- How can I capture the idea of a group of parameters which, if
they are all scaled in the correct way (some linearly, others
inversely perhaps), generated geometry that is more or less
identical except that it is higher-resolution?
- Use mixins to extend 3D transformations to things (matrices,
cages, meshes, existing transformations)
- I can transform a Cage. Why not a CageGen?
## ????
- Embed this in Blender?
## Future thoughts
- What if I had a function that could generate a Cage as if
from a parametric formula and smoothly vary its orientation?
My existing tools could easily turn this to a mesh. If I could vary
the detail of the Cage itself (if needed), then I could also
generate a mesh at an arbitrary level of detail simply by sampling at
finer and finer points on the parameter space. (This might also tie
into the Parallel Transport work.)
- What are the limitations of using Cages?
- Current system is very "generative". Could I do basically L-system
if I have rules for how a much is *refined*? What about IFS?
- Do this in Rust once I understand WTF I am doing
## Other thoughts
- Why do I never use the term "extruding" to describe what I'm doing?

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,282 @@
import itertools
import meshutil
import stl.mesh
import numpy
class Cage(object):
"""An ordered list of polygons (or polytopes, technically)."""
def __init__(self, verts, splits):
# Element i of 'self.splits' gives the row index in 'self.verts'
# in which polygon i begins.
self.splits = splits
# NumPy array of shape (N,3)
self.verts = verts
@classmethod
def from_arrays(cls, *arrs):
"""
Pass any number of array-like objects, with each one being a
nested array with 3 elements - e.g. [[0,0,0], [1,1,1], [2,2,2]] -
providing points.
Each array-like object is treated as vertices describing a
polygon/polytope.
"""
n = 0
splits = [0]*len(arrs)
for i,arr in enumerate(arrs):
splits[i] = n
n += len(arr)
verts = numpy.zeros((n,3), dtype=numpy.float64)
# Populate it accordingly:
i0 = 0
for arr in arrs:
i1 = i0 + len(arr)
verts[i0:i1, :] = arr
i0 = i1
return cls(verts, splits)
def polys(self):
"""Return iterable of polygons as (views of) NumPy arrays."""
count = len(self.splits)
for i,n0 in enumerate(self.splits):
if i+1 < count:
n1 = self.splits[i+1]
yield self.verts[n0:n1,:]
else:
yield self.verts[n0:,:]
def subdivide_deprecated(self):
# assume self.verts has shape (4,3).
# Midpoints of every segment:
mids = (self.verts + numpy.roll(self.verts, -1, axis=0)) / 2
# Centroid:
centroid = numpy.mean(self.verts, axis=0)
# Now, every single new boundary has: one vertex of 'bound', an
# adjacent midpoint, a centroid, and the other adjacent midpoint.
arrs = [
[self.verts[0,:], mids[0,:], centroid, mids[3,:]],
[mids[0,:], self.verts[1,:], mids[1,:], centroid],
[centroid, mids[1,:], self.verts[2,:], mids[2,:]],
[mids[3,:], centroid, mids[2,:], self.verts[3,:]],
]
# The above respects winding order and should not add any rotation.
# I'm sure it has a pattern I can factor out, but I've not tried
# yet.
cages = [Cage(numpy.array(a), self.splits) for a in arrs]
trans_verts = numpy.zeros((2*len(self.verts),3), dtype=self.verts.dtype)
for i,(v,m) in enumerate(zip(self.verts, mids)):
trans_verts[2*i] = v
trans_verts[2*i+1] = m
trans_edges = [[7, 0, 1], [1, 2, 3], [3, 4, 5], [5, 6, 7]]
return cages, trans_verts, trans_edges
def subdivide_x_deprecated(self):
mids = (self.verts + numpy.roll(self.verts, -1, axis=0)) / 2
centroid = numpy.mean(self.verts, axis=0)
arrs = [
[self.verts[0,:], mids[0,:], mids[2,:], self.verts[3,:]],
[mids[0,:], self.verts[1,:], self.verts[2,:], mids[2,:]],
]
cages = [Cage(numpy.array(a), self.splits) for a in arrs]
trans_verts = numpy.zeros((2*len(self.verts),3), dtype=self.verts.dtype)
for i,(v,m) in enumerate(zip(self.verts, mids)):
trans_verts[2*i] = v
trans_verts[2*i+1] = m
trans_edges = [[7, 0, 1], [1, 2, 3], [3, 4, 5], [5, 6, 7]]
return cages, trans_verts, trans_edges
def is_fork(self):
return False
def transform(self, xform):
"""Apply a Transform to all vertices, returning a new Cage."""
return Cage(xform.apply_to(self.verts), self.splits)
def classify_overlap(self, cages):
"""Classifies each vertex in a list of cages according to some rules.
(This is mostly used in order to verify that certain rules are
followed when a mesh is undergoing forking/branching.)
Returns:
v -- List of length len(cages). v[i] is a numpy array of shape (N,)
where N is the number of vertices in cages[i] (i.e. rows of
cages[i].verts). Element v[i][j] gives a classification of
X=l[i].verts[j] that will take values below:
0 -- None of the below apply to X.
1 -- X lies on an edge in this Cage (i.e. self).
2 -- X equals another (different) vertex somewhere in 'cages', and
case 1 does not apply.
3 -- X equals a vertex in self.verts.
"""
v = [numpy.zeros((cage.verts.shape[0],), dtype=numpy.uint8)
for cage in cages]
# for cage i of all the cages...
for i, cage in enumerate(cages):
# for vertex j within cage i...
for j, vert in enumerate(cage.verts):
# Check against every vert in our own (self.verts):
for vert2 in self.verts:
if numpy.allclose(vert, vert2):
v[i][j] = 3
break
if v[i][j] > 0:
continue
# Check against every edge of our own polygons:
for poly in self.polys():
for k,_ in enumerate(poly):
# Below is because 'poly' is cyclic (last vertex
# has an edge to the first):
k2 = (k + 1) % len(poly)
# Find distance from 'vert' to each vertex of the edge:
d1 = numpy.linalg.norm(poly[k,:] - vert)
d2 = numpy.linalg.norm(poly[k2,:] - vert)
# Find the edge's length:
d = numpy.linalg.norm(poly[k2,:] - poly[k,:])
# These are equal if and only if the vertex lies along
# that edge:
if numpy.isclose(d, d1 + d2):
v[i][j] = 1
break
if v[i][j] > 0:
break
if v[i][j] > 0:
continue
# Check against every *other* vert in cages:
for i2, cage2 in enumerate(cages):
for j2, vert2 in enumerate(cage.verts):
if i == i2 and j == j2:
# same cage, same vertex - ignore:
continue
if numpy.allclose(vert, vert2):
v[i][j] = 2
break
if v[i][j] > 0:
break
return v
class CageFork(object):
"""A series of generators that all split off in such a way that their
initial polygons collectively cover all of some larger polygon, with
no overlap. The individual generators must produce either Cage, or
more CageFork.
Transition vertices and edges are here to help adapt this CageFork
to an earlier Cage, which may require subdividing its edges.
Vertices (in 'verts') should proceed in the same direction around the
cage, and start at the same vertex. Edges (in 'edges') should have N
elements, one for each of N vertices in the 'starting' Cage (the one
that we must adapt *from*), and edges[i] should itself be a list in
which each element is a (row) index of 'verts'. edges[i] specifies,
in correct order, which vertices in 'verts' should connect to vertex
i of the 'starting' Cage. In its entirety, it also gives the
'transition' Cage (hence, order matters in the inner lists).
As an example, if a starting cage is [0, 0, 0], [1, 0, 0], [1, 1, 0],
[0, 1, 0] and the CageFork simply subdivides into 4 equal-size cages,
then 'verts' might be [[0, 0, 0], [0.5, 0, 0], [1, 0, 0], [1, 0.5, 0],
[1, 1, 0], [0.5, 1, 0], [0, 1, 0], [0, 0.5, 0]] - note that it begins
at the same vertex, subdivides each edge, and (including the cyclic
nature) ends at the same vertex. 'edges' then would be:
[[7, 0, 1], [1, 2, 3], [3, 4, 5], [5, 6, 7]]. Note that every vertex
in the starting cage connects to 3 vertices in 'verts' and overlaps
with the previous and next vertex.
(Sorry. This is explained badly and I know it.)
Parameters:
gens -- explained above
verts -- Numpy array with 'transition' vertices, shape (M,3)
edges -- List of 'transition' edges
"""
def __init__(self, gens, verts, edges):
self.gens = gens
self.verts = verts
self.edges = edges
def is_fork(self):
return True
def transition_from(self, cage):
"""Generate a transitional mesh to adapt the given starting Cage"""
#print("DEBUG: Transition from {} to {}".format(cage.verts, self.verts))
vs = numpy.concatenate([cage.verts, self.verts])
# Indices 0...offset-1 are from cage, rest are from self.verts
offset = cage.verts.shape[0]
# We have one face for total sub-elements in self.edges:
count = sum([len(e) for e in self.edges])
fs = numpy.zeros((count, 3), dtype=int)
face_idx = 0
for j, adjs in enumerate(self.edges):
for k, adj in enumerate(adjs[:-1]):
adj_next = adjs[(k + 1) % len(adjs)]
# Proceed in direction of cage.verts:
fs[face_idx] = [j, offset + adj_next, offset + adj]
face_idx += 1
fs[face_idx] = [j, (j + 1) % len(cage.verts), offset + adjs[-1]]
face_idx += 1
return meshutil.FaceVertexMesh(vs, fs)
class CageGen(object):
"""A generator, finite or infinite, that produces objects of type Cage.
It can also produce CageFork, but only a single one as the final value
of a finite generator."""
def __init__(self, gen):
self.gen = gen
def to_mesh(self, count=None, flip_order=False, loop=False, close_first=False,
close_last=False, join_fn=meshutil.join_boundary_simple):
# Get 'opening' polygons of generator:
cage_first = next(self.gen)
#print("DEBUG: to_mesh(count={}), cage_first={}".format(count, cage_first.verts))
# TODO: Avoid 'next' here so that we can use a list, not solely a
# generator/iterator.
if cage_first.is_fork():
# TODO: Can it be a fork? Does that make sense?
raise Exception("First element in CageGen can't be a fork.")
cage_last = cage_first
meshes = []
# Close off the first polygon if necessary:
if close_first:
for poly in cage_first.polys():
meshes.append(meshutil.close_boundary_simple(poly))
# Generate all polygons from there and connect them:
#print(self.gen)
for i, cage_cur in enumerate(self.gen):
#print("DEBUG: i={}, cage_cur={}, cage_last={}".format(i, cage_cur, cage_last.verts))
#print("{}: {}".format(i, cage_cur))
if count is not None and i >= count:
# We stop recursing here, so close things off if needed:
if close_last:
for poly in cage_last.polys():
meshes.append(meshutil.close_boundary_simple(poly, reverse=True))
# TODO: Fix the winding order hack here.
break
# If it's a fork, then recursively generate all the geometry
# from them, depth-first:
if cage_cur.is_fork():
# First, transition the cage properly:
mesh_trans = cage_cur.transition_from(cage_last)
meshes.append(mesh_trans)
# TODO: Clean up these recursive calls; parameters are ugly.
# Some of them also make no sense in certain combinations
# (e.g. loop with fork)
for gen in cage_cur.gens:
m = gen.to_mesh(count=count - i, flip_order=flip_order, loop=loop,
close_first=False, close_last=close_last,
join_fn=join_fn)
meshes.append(m)
# A fork can be only the final element, so disregard anything
# after one and just quit:
break
if flip_order:
for b0,b1 in zip(cage_cur.polys(), cage_last.polys()):
m = join_fn(b0, b1)
meshes.append(m)
else:
for b0,b1 in zip(cage_cur.polys(), cage_last.polys()):
m = join_fn(b1, b0)
meshes.append(m)
cage_last = cage_cur
if loop:
for b0,b1 in zip(cage_last.polys(), cage_first.polys()):
if flip_order:
m = join_fn(b1, b0)
else:
m = join_fn(b0, b1)
meshes.append(m)
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh

View File

@@ -0,0 +1,401 @@
#!/usr/bin/env python3
import itertools
import math
import numpy
import stl.mesh
import trimesh
import meshutil
import meshgen
import cage
# I should be moving some of these things out into more of a
# standard library than an 'examples' script
# The first "working" example I had of the recursive 3D geometry
# that actually kept the manifold throughout:
def ram_horn():
b0 = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0]
xf0_to_1 = meshutil.Transform().translate(0,0,1)
b1 = xf0_to_1.apply_to(b0)
meshes = []
meshes.append(meshutil.join_boundary_simple(b0, b1))
meshes.append(meshutil.close_boundary_simple(b0))
for i in range(4):
# Opening boundary:
b = b1
xf = meshutil.Transform() \
.translate(0,0,-1) \
.scale(0.5) \
.translate(0.25,0.25,1) \
.rotate([0,0,1], i*numpy.pi/2)
for layer in range(128):
b_sub0 = xf.apply_to(b)
incr = meshutil.Transform() \
.scale(0.9) \
.rotate([-1,0,1], 0.3) \
.translate(0,0,0.8)
b_sub1 = incr.compose(xf).apply_to(b)
m = meshutil.join_boundary_simple(b_sub0, b_sub1)
meshes.append(m)
xf = incr.compose(xf)
# Close final boundary:
meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
# ::-1 is to reverse the boundary's order to fix winding order.
# Not sure of the "right" way to fix winding order here.
# The boundary vertices go in an identical order... it's just
# that clockwise/counter-clockwise flip.
# I keep confusing the 'incremental' transform with the
# transform to get b_open in the first place
# I don't need to subdivide *geometry*.
# I need to subdivide *space* and then put geometry in it.
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh
# Rewriting the above in terms of generators & iterated transforms
def ram_horn_gen(b, xf):
while True:
b1 = xf.apply_to(b)
yield [b1]
incr = meshutil.Transform() \
.scale(0.9) \
.rotate([-1,0,1], 0.3) \
.translate(0,0,0.8)
xf = incr.compose(xf)
def ram_horn2():
b0 = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0]
xf0_to_1 = meshutil.Transform().translate(0,0,1)
b1 = xf0_to_1.apply_to(b0)
meshes = []
meshes.append(meshutil.join_boundary_simple(b0, b1))
meshes.append(meshutil.close_boundary_simple(b0))
for i in range(4):
# Opening boundary:
xf = meshutil.Transform() \
.translate(0,0,-1) \
.scale(0.5) \
.translate(0.25,0.25,1) \
.rotate([0,0,1], i*numpy.pi/2)
gen = ram_horn_gen(b1, xf)
mesh = meshgen.gen2mesh(gen, count=128, close_last=True)
meshes.append(mesh)
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh
# Rewriting the above rewrite in terms of Cage
def ram_horn3():
center = meshutil.Transform().translate(-0.5, -0.5, 0)
cage0 = cage.Cage.from_arrays([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
]).transform(center)
xf0_to_1 = meshutil.Transform().translate(0, 0, 1)
cage1 = cage0.transform(xf0_to_1)
opening_boundary = lambda i: meshutil.Transform() \
.translate(0,0,-1) \
.scale(0.5) \
.translate(0.25,0.25,1) \
.rotate([0,0,1], i*numpy.pi/2)
incr = meshutil.Transform() \
.scale(0.9) \
.rotate([-1,0,1], 0.3) \
.translate(0,0,0.8)
def recur(xf):
while True:
cage2 = cage1.transform(xf)
yield cage2
xf = incr.compose(xf)
# TODO: I think there is a way to express 'recur' in the form of
# itertools.accumulate, and it might be clearer. This function is
# just iteratively re-composing 'incr' into a seed transformation,
# and applying this transformation (at every stage) to the same
# mesh.
gens = [cage.CageGen(recur(opening_boundary(i))) for i in range(4)]
cg = cage.CageGen(itertools.chain([cage0, cage1, cage.CageFork(gens)]))
# TODO: if this is just a list it seems silly to require itertools
mesh = cg.to_mesh(count=128, close_first=True, close_last=True)
return mesh
def ram_horn_branch():
center = meshutil.Transform().translate(-0.5, -0.5, 0)
cage0 = cage.Cage.from_arrays([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
]).transform(center)
incr = meshutil.Transform() \
.scale(0.9) \
.rotate([-1,0,1], 0.3) \
.translate(0,0,0.8)
def recur(xf, cage1, count):
for i in range(count):
if i > 0:
c = cage1.transform(xf)
yield c
xf0 = xf
xf = incr.compose(xf)
def xf_sub(i):
# (dx,dy) should be normalized, but I reused from something else
dx = 1 if i == 0 or i == 1 else -1
dy = 1 if i == 0 or i == 3 else -1
return meshutil.Transform().translate(0, 0, 0.5).rotate([-dy,dx,0], -numpy.pi/6)
subdiv, trans_vs, trans_es = cage1.subdivide_deprecated()
gens = [cage.CageGen(itertools.chain(
[cage_sub.transform(xf)],
recur(xf_sub(i).compose(xf), cage_sub, 8)))
for i,cage_sub in
enumerate(subdiv)]
yield cage.CageFork(gens, xf.apply_to(trans_vs), trans_es)
cg = cage.CageGen(itertools.chain(
[cage0],
recur(meshutil.Transform(), cage0, 8),
))
# TODO: if this is just a list it seems silly to require itertools
mesh = cg.to_mesh(count=32, close_first=True, close_last=True)
return mesh
def dream_pendant():
center = meshutil.Transform().translate(-0.5, -0.5, 0)
cage0 = cage.Cage.from_arrays([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
]).transform(center)
incr = meshutil.Transform() \
.scale(0.95, 1.0, 0.95) \
.rotate([0,1,0], 0.2) \
.translate(0,0,0.9)
def recur(xf, cage1, count):
for i in range(count):
if i > 0:
c = cage1.transform(xf)
yield c
xf0 = xf
xf = incr.compose(xf)
def xf_rot(a):
return meshutil.Transform().rotate([0,1,0], a)
subdiv, trans_vs, trans_es = cage1.subdivide_x_deprecated()
gens = [cage.CageGen(itertools.chain(
[cage_sub.transform(xf)],
recur(xf_rot(ang).compose(xf), cage_sub, 5)))
for cage_sub,ang in
zip(subdiv, [-0.2, 0.7])]
yield cage.CageFork(gens, xf.apply_to(trans_vs), trans_es)
cg = cage.CageGen(itertools.chain(
[cage0],
recur(meshutil.Transform(), cage0, 3),
))
# TODO: if this is just a list it seems silly to require itertools
mesh1 = cg.to_mesh(count=32, close_first=False, close_last=True)
mesh2 = mesh1.transform(meshutil.Transform().rotate([0,1,0], math.pi))
return meshutil.FaceVertexMesh.concat_many([mesh1, mesh2])
def branch_test():
b0 = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0]
parts = [meshutil.Transform().scale(0.5).translate(dx, dy, 1)
for dx in (-0.25,+0.25) for dy in (-0.25,+0.25)]
xf = meshutil.Transform().translate(0,0,0.1).scale(0.95)
def gen():
b = b0
for i in range(10):
b = xf.apply_to(b)
yield [b]
return meshgen.gen2mesh(gen(), close_first=True, close_last=True)
# Interlocking twists.
# ang/dz control resolution. dx0 controls radius. count controls
# how many twists. scale controls speed they shrink at.
def twist(ang=0.1, dz=0.2, dx0=2, count=4, scale=0.98):
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0]
meshes = []
for i in range(count):
xf = meshutil.Transform() \
.translate(dx0, 0, 0) \
.rotate([0,0,1], numpy.pi * 2 * i / count)
b0 = xf.apply_to(b)
meshes.append(meshutil.close_boundary_simple(b0))
for layer in range(256):
b_sub0 = xf.apply_to(b)
incr = meshutil.Transform() \
.rotate([0,0,1], ang) \
.translate(0,0,dz) \
.scale(scale)
b_sub1 = xf.compose(incr).apply_to(b)
m = meshutil.join_boundary_simple(b_sub0, b_sub1)
meshes.append(m)
xf = xf.compose(incr)
# Close final boundary:
meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh
def twist_nonlinear(dx0 = 2, dz=0.2, count=3, scale=0.99, layers=100):
# This can be a function rather than a constant:
angs = numpy.power(numpy.linspace(0.4, 2.0, layers), 2.0) / 10.0
ang_fn = lambda i: angs[i]
# (could it also be a function of space rather than which layer?)
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0]
meshes = []
for i in range(count):
xf = meshutil.Transform() \
.translate(dx0, 0, 0) \
.rotate([0,0,1], numpy.pi * 2 * i / count)
b0 = xf.apply_to(b)
meshes.append(meshutil.close_boundary_simple(b0))
for layer in range(layers):
b_sub0 = xf.apply_to(b)
ang = ang_fn(layer)
incr = meshutil.Transform() \
.rotate([0,0,1], ang) \
.translate(0,0,dz) \
.scale(scale)
b_sub1 = xf.compose(incr).apply_to(b)
m = meshutil.join_boundary_simple(b_sub0, b_sub1)
meshes.append(m)
xf = xf.compose(incr)
# Close final boundary:
meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh
def twist_from_gen():
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 0, 1],
[0, 0, 1],
], dtype=numpy.float64) - [0.5, 0, 0.5]
b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
bs = [b]
# since it needs a generator:
gen_inner = itertools.repeat(bs)
gen = meshgen.gen_inc_y(meshgen.gen_twisted_boundary(gen_inner))
mesh = meshgen.gen2mesh(gen, 100, True)
return mesh
# frames = How many step to build this from:
# turn = How many full turns to make in inner twist
# count = How many inner twists to have
def twisty_torus(frames = 200, turns = 4, count = 4, rad = 4):
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 0, 1],
[0, 0, 1],
], dtype=numpy.float64) - [0.5, 0, 0.5]
b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
bs = [b]
# since it needs a generator:
gen_inner = itertools.repeat(bs)
# In order to make this line up properly:
angle = numpy.pi * 2 * turns / frames
gen = meshgen.gen_torus_xy(meshgen.gen_twisted_boundary(gen=gen_inner, count=count, ang=angle), rad=rad, frames=frames)
return meshgen.gen2mesh(gen, 0, flip_order=True, loop=True)
def spiral_nested_2():
# Slow.
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 0, 1],
[0, 0, 1],
], dtype=numpy.float64) - [0.5, 0, 0.5]
b *= 0.3
b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
bs = [b]
# since it needs a generator:
gen1 = itertools.repeat(bs)
gen2 = meshgen.gen_twisted_boundary(gen1, ang=-0.2, dx0=0.5)
gen3 = meshgen.gen_twisted_boundary(gen2, ang=0.05, dx0=1)
gen = meshgen.gen_inc_y(gen3, dy=0.1)
return meshgen.gen2mesh(
gen, count=250, flip_order=True, close_first=True, close_last=True)
def spiral_nested_3():
# Slower.
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 0, 1],
[0, 0, 1],
], dtype=numpy.float64) - [0.5, 0, 0.5]
b *= 0.3
b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
bs = [b]
# since it needs a generator:
gen1 = itertools.repeat(bs)
gen2 = meshgen.gen_twisted_boundary(gen1, ang=-0.2, dx0=0.5)
gen3 = meshgen.gen_twisted_boundary(gen2, ang=0.07, dx0=1)
gen4 = meshgen.gen_twisted_boundary(gen3, ang=-0.03, dx0=3)
gen = meshgen.gen_inc_y(gen4, dy=0.1)
return meshgen.gen2mesh(
gen, count=500, flip_order=True, close_first=True, close_last=True)
def main():
fns = {
ram_horn: "ramhorn.stl",
ram_horn2: "ramhorn2.stl",
# TODO: Fix
#ram_horn3: "ramhorn3.stl",
ram_horn_branch: "ramhorn_branch.stl",
dream_pendant: "dream_pendant.stl",
twist: "twist.stl",
twist_nonlinear: "twist_nonlinear.stl",
twist_from_gen: "twist_from_gen.stl",
twisty_torus: "twisty_torus.stl",
spiral_nested_2: "spiral_nested_2.stl",
spiral_nested_3: "spiral_nested_3.stl",
}
for f in fns:
fname = fns[f]
print("Generate {}...".format(fname))
mesh = f()
nv = mesh.v.shape[0]
nf = mesh.f.shape[0]
print("Saving {} verts & {} faces...".format(nv, nf))
mesh.to_stl_mesh().save(fname)
print("Done.")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,99 @@
import itertools
import meshutil
import stl.mesh
import numpy
import trimesh
# Generate a frame with 'count' boundaries in the XZ plane.
# Each one rotates by 'ang' at each step.
# dx0 is center-point distance from each to the origin.
#
# This doesn't generate usable geometry on its own.
def gen_twisted_boundary(gen=None, count=4, dx0=2, ang=0.1):
if gen is None:
b = numpy.array([
[0, 0, 0],
[1, 0, 0],
[1, 0, 1],
[0, 0, 1],
], dtype=numpy.float64) - [0.5, 0, 0.5]
gen = itertools.repeat([b])
# Generate 'seed' transformations:
xfs = [meshutil.Transform().translate(dx0, 0, 0).rotate([0,1,0], numpy.pi * 2 * i / count)
for i in range(count)]
# (we'll increment the transforms in xfs as we go)
for bs in gen:
xfs_new = []
bs2 = []
for i, xf in enumerate(xfs):
# Generate a boundary from running transform:
bs2 += [xf.apply_to(b) for b in bs]
# Increment transform i:
xf2 = xf.rotate([0,1,0], ang)
xfs_new.append(xf2)
xfs = xfs_new
yield bs2
# This is to see how well it works to compose generators:
def gen_inc_y(gen, dy=0.1):
xf = meshutil.Transform()
for bs in gen:
bs2 = [xf.apply_to(b) for b in bs]
yield bs2
xf = xf.translate(0, dy, 0)
# Wrap a boundary generator around a (sorta) torus that is along XY.
# producing a mesh.
# 'frames' sets resolution, 'rad' sets radius (the boundary's origin
# sweeps through this radius - it's not 'inner' or 'outer' radius).
#
# generator should produce lists of boundaries which are oriented
# roughly in XZ. This will get 'frames' elements from it if
# possible.
def gen_torus_xy(gen, rad=2, frames=100):
ang = numpy.pi*2 / frames
xf = meshutil.Transform().translate(rad, 0, 0)
for i,bs in enumerate(gen):
if i >= frames:
break
bs2 = [xf.apply_to(b) for b in bs]
yield bs2
xf = xf.rotate([0,0,1], ang)
# String together boundaries from a generator.
# If count is nonzero, run only this many iterations.
def gen2mesh(gen, count=0, flip_order=False, loop=False,
close_first = False,
close_last = False,
join_fn=meshutil.join_boundary_simple):
# Get first list of boundaries:
bs_first = next(gen)
bs_last = bs_first
# TODO: Begin and end with close_boundary_simple
meshes = []
if close_first:
for b in bs_first:
meshes.append(meshutil.close_boundary_simple(b))
for i,bs_cur in enumerate(gen):
if count > 0 and i >= count:
break
for j,b in enumerate(bs_cur):
if flip_order:
m = join_fn(b, bs_last[j])
else:
m = join_fn(bs_last[j], b)
meshes.append(m)
bs_last = bs_cur
if loop:
for b0,b1 in zip(bs_last, bs_first):
if flip_order:
m = join_fn(b1, b0)
else:
m = join_fn(b0, b1)
meshes.append(m)
if close_last:
for b in bs_last:
meshes.append(meshutil.close_boundary_simple(b))
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh

View File

@@ -0,0 +1,257 @@
import stl.mesh
import numpy
import quaternion
import random
import quat
# (left/right, bottom/top, back/front)
# I'm using X to the right, Y up, Z inward
# (not that it really matters except for variable names)
lbf = numpy.array([0,0,0])
rbf = numpy.array([1,0,0])
ltf = numpy.array([0,1,0])
rtf = numpy.array([1,1,0])
lbb = numpy.array([0,0,1])
rbb = numpy.array([1,0,1])
ltb = numpy.array([0,1,1])
rtb = numpy.array([1,1,1])
class FaceVertexMesh(object):
def __init__(self, v, f):
# v & f should both be of shape (N,3)
self.v = v
self.f = f
def concat(self, other_mesh):
v2 = numpy.concatenate([self.v, other_mesh.v])
# Note index shift!
f2 = numpy.concatenate([self.f, other_mesh.f + self.v.shape[0]])
m2 = FaceVertexMesh(v2, f2)
return m2
def transform(self, xform):
# Just transform vertices. Indices don't change.
return FaceVertexMesh(xform.apply_to(self.v), self.f)
def to_stl_mesh(self):
data = numpy.zeros(self.f.shape[0], dtype=stl.mesh.Mesh.dtype)
v = data["vectors"]
for i, (iv0, iv1, iv2) in enumerate(self.f):
v[i] = [self.v[iv0], self.v[iv1], self.v[iv2]]
return stl.mesh.Mesh(data)
@classmethod
def Empty(cls):
return FaceVertexMesh(numpy.zeros((0,3)), numpy.zeros((0,3), dtype=int))
@classmethod
def concat_many(cls, meshes):
nv = 0
nf = 0
for m in meshes:
nv += m.v.shape[0]
nf += m.f.shape[0]
v = numpy.zeros((nv,3), dtype=numpy.float64)
f = numpy.zeros((nf,3), dtype=int)
vi = 0
fi = 0
for m in meshes:
vj = vi + m.v.shape[0]
fj = fi + m.f.shape[0]
v[vi:vj,:] = m.v
f[fi:fj,:] = m.f + vi
vi = vj
fi = fj
return FaceVertexMesh(v, f)
class Transform(object):
def __init__(self, mtx=None):
if mtx is None:
self.mtx = numpy.identity(4)
else:
self.mtx = mtx
def _compose(self, mtx2):
# Note pre-multiply. Earlier transforms are done first.
return Transform(mtx2 @ self.mtx)
def compose(self, xform):
return self._compose(xform.mtx)
def scale(self, *a, **kw):
return self._compose(mtx_scale(*a, **kw))
def translate(self, *a, **kw):
return self._compose(mtx_translate(*a, **kw))
def rotate(self, *a, **kw):
return self._compose(mtx_rotate(*a, **kw))
def reflect(self, *a, **kw):
return self._compose(mtx_reflect(*a, **kw))
def identity(self, *a, **kw):
return self._compose(mtx_identity(*a, **kw))
def apply_to(self, vs):
# Homogeneous coords, so append a column of ones. vh is then shape (N,4):
vh = numpy.hstack([vs, numpy.ones((vs.shape[0], 1), dtype=vs.dtype)])
# As we have row vectors, we're doing basically (A*x)^T=(x^T)*(A^T)
# hence transposing the matrix, while vectors are already transposed.
return (vh @ self.mtx.T)[:,0:3]
def mtx_scale(sx, sy=None, sz=None):
if sy is None:
sy = sx
if sz is None:
sz = sx
return numpy.array([
[sx, 0, 0, 0],
[0, sy, 0, 0],
[0, 0, sz, 0],
[0, 0, 0, 1],
])
def mtx_translate(x, y, z):
return numpy.array([
[1, 0, 0, x],
[0, 1, 0, y],
[0, 0, 1, z],
[0, 0, 0, 1],
])
def mtx_rotate(axis, angle):
q = quat.rotation_quaternion(axis, angle)
return quat.quat2mat(q)
def mtx_reflect(axis):
# axis must be norm-1
axis = numpy.array(axis)
axis = axis / numpy.linalg.norm(axis)
a,b,c = axis[0], axis[1], axis[2]
return numpy.array([
[1-2*a*a, -2*a*b, -2*a*c, 0],
[-2*a*b, 1-2*b*b, -2*b*c, 0],
[-2*a*c, -2*b*c, 1-2*c*c, 0],
[0, 0, 0, 1],
])
def mtx_identity():
return numpy.eye(4)
def cube(open_xz=False):
verts = numpy.array([
lbf, rbf, ltf, rtf,
lbb, rbb, ltb, rtb,
], dtype=numpy.float64)
if open_xz:
faces = numpy.zeros((8,3), dtype=int)
else:
faces = numpy.zeros((12,3), dtype=int)
faces[0,:] = [0, 3, 1]
faces[1,:] = [0, 2, 3]
faces[2,:] = [1, 7, 5]
faces[3,:] = [1, 3, 7]
faces[4,:] = [5, 6, 4]
faces[5,:] = [5, 7, 6]
faces[6,:] = [4, 2, 0]
faces[7,:] = [4, 6, 2]
if not open_xz:
faces[8,:] = [2, 7, 3]
faces[9,:] = [2, 6, 7]
faces[10,:] = [0, 1, 5]
faces[11,:] = [0, 5, 4]
# winding order?
return FaceVertexMesh(verts, faces)
def cube_distort(angle, open_xz=False):
q = quat.rotation_quaternion(numpy.array([-1,0,1]), angle)
ltf2 = quat.conjugate_by(ltf, q)[0,:]
rtf2 = quat.conjugate_by(rtf, q)[0,:]
ltb2 = quat.conjugate_by(ltb, q)[0,:]
rtb2 = quat.conjugate_by(rtb, q)[0,:]
# TODO: Just make these functions work right with single vectors
verts = numpy.array([
lbf, rbf, ltf2, rtf2,
lbb, rbb, ltb2, rtb2,
], dtype=numpy.float64)
if open_xz:
faces = numpy.zeros((8,3), dtype=int)
else:
faces = numpy.zeros((12,3), dtype=int)
faces[0,:] = [0, 3, 1]
faces[1,:] = [0, 2, 3]
faces[2,:] = [1, 7, 5]
faces[3,:] = [1, 3, 7]
faces[4,:] = [5, 6, 4]
faces[5,:] = [5, 7, 6]
faces[6,:] = [4, 2, 0]
faces[7,:] = [4, 6, 2]
if not open_xz:
faces[8,:] = [2, 7, 3]
faces[9,:] = [2, 6, 7]
faces[10,:] = [0, 1, 5]
faces[11,:] = [0, 5, 4]
# winding order?
return FaceVertexMesh(verts, faces)
def split_boundary(bound):
# assume bound1 has shape (4,3).
# Midpoints of every segment:
mids = (bound + numpy.roll(bound, 1, axis=0)) / 2
mids_adj = numpy.roll(mids, -1, axis=0)
# Centroid:
centroid = numpy.mean(bound, axis=0)
# Now, every single new boundary has: one vertex of 'bound', an
# adjacent midpoint, a centroid, and the other adjacent midpoint.
bounds = [
numpy.array([bound[i,:], mids[i,:], centroid, mids_adj[i,:]])
for i in range(4)
]
return bounds
def subdivide_boundary(bound):
# assume bound1 has shape (4,3).
# Midpoints of every segment:
mids = (bound + numpy.roll(bound, -1, axis=0)) / 2
b2 = numpy.zeros((bound.shape[0]*2, bound.shape[1]))
for i,row in enumerate(bound):
b2[2*i,:] = bound[i,:]
b2[2*i+1,:] = mids[i,:]
return b2
def join_boundary_simple(bound1, bound2, random_diag=False):
# bound1 & bound2 are both arrays of shape (N,3), representing
# the points of a boundary. This joins the two boundaries by
# simply connecting quads (made of 2 triangles) straight across.
#
# Winding will proceed in the direction of the first boundary.
#
# Returns FaceVertexMesh.
n = bound1.shape[0]
vs = numpy.concatenate([bound1, bound2])
# Indices 0...N-1 are from bound1, N...2*N-1 are from bound2
fs = numpy.zeros((2*n, 3), dtype=int)
for i in range(n):
v0 = i
v1 = (i + 1) % n
if random_diag and random.random() < 0.5:
fs[2*i] = [n + v1, n + v0, v0]
fs[2*i + 1] = [v1, n + v1, v0]
else:
fs[2*i] = [n + v1, n + v0, v1]
fs[2*i + 1] = [v1, n + v0, v0]
return FaceVertexMesh(vs, fs)
def join_boundary_optim(bound1, bound2):
# bound1 and bound2 must stay in order, but we can rotate
# the starting point to whatever we want. Use distance as
# a metric:
errs = [numpy.linalg.norm(bound1 - numpy.roll(bound2, i, axis=0))
for i,_ in enumerate(bound1)]
# What shift gives the lowest distance?
i = numpy.argmin(errs)
return join_boundary_simple(bound1, numpy.roll(bound2, i, axis=0))
def close_boundary_simple(bound, reverse=False):
# This will fail for any non-convex boundary!
centroid = numpy.mean(bound, axis=0)
vs = numpy.concatenate([bound, centroid[numpy.newaxis,:]])
n = bound.shape[0]
# note that n is new the index of the centroid
fs = numpy.zeros((n+1, 3), dtype=int)
if reverse:
for i in range(n):
fs[i] = [(i+1) % n, n, i]
else:
for i in range(n):
fs[i] = [i, n, (i+1) % n]
return FaceVertexMesh(vs, fs)

View File

@@ -0,0 +1,33 @@
import numpy
import quaternion
def conjugate_by(vec, quat):
"""Turn 'vec' to a quaternion, conjugate it by 'quat', and return it."""
q2 = quat * vec2quat(vec) * quat.conjugate()
return quaternion.as_float_array(q2)[:,1:]
def rotation_quaternion(axis, angle):
"""Returns a quaternion for rotating by some axis and angle.
Inputs:
axis -- numpy array of shape (3,), with axis to rotate around
angle -- angle in radians by which to rotate
"""
qc = numpy.cos(angle / 2)
qs = numpy.sin(angle / 2)
qv = qs * numpy.array(axis)
return numpy.quaternion(qc, qv[0], qv[1], qv[2])
def vec2quat(vs):
qs = numpy.zeros(vs.shape[0], dtype=numpy.quaternion)
quaternion.as_float_array(qs)[:,1:4] = vs
return qs
def quat2mat(q):
s = 1
return numpy.array([
[1-2*s*(q.y**2+q.z**2), 2*s*(q.x*q.y-q.z*q.w), 2*s*(q.x*q.z+q.y*q.w), 0],
[2*s*(q.x*q.y+q.z*q.w), 1-2*s*(q.x**2+q.z**2), 2*s*(q.y*q.z-q.x*q.w), 0],
[2*s*(q.x*q.z-q.y*q.w), 2*s*(q.y*q.z+q.x*q.w), 1-2*s*(q.x**2+q.y**2), 0],
[0, 0, 0, 1],
])

View File

@@ -0,0 +1,263 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"face_normals didn't match triangles, ignoring!\n",
"/home/hodapp/.local/lib/python3.6/site-packages/IPython/core/display.py:694: UserWarning: Consider using IPython.display.IFrame instead\n",
" warnings.warn(\"Consider using IPython.display.IFrame instead\")\n"
]
},
{
"data": {
"text/html": [
"<iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang=&quot;en&quot;>\n",
" <head>\n",
" <title>trimesh: threejs viewer</title>\n",
" <meta charset=&quot;utf-8&quot;>\n",
" <meta name=&quot;viewport&quot; content=&quot;width=device-width, \n",
"\t\t user-scalable=no, \n",
"\t\t minimum-scale=1.0, \n",
"\t\t maximum-scale=1.0&quot;>\n",
" <style>\n",
" body {\n",
" margin: 0px;\n",
" overflow: hidden;\n",
" }\n",
" </style>\n",
" </head>\n",
" <body>\n",
" <div id=&quot;container&quot;></div>\n",
" <script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.min.js&quot;></script>\n",
" <script>THREE.TrackballControls=function(object,domElement){var _this=this;var STATE={NONE:-1,ROTATE:0,ZOOM:1,PAN:2,TOUCH_ROTATE:3,TOUCH_ZOOM_PAN:4};this.object=object;this.domElement=(domElement!==undefined)?domElement:document;this.enabled=true;this.screen={left:0,top:0,width:0,height:0};this.rotateSpeed=1.0;this.zoomSpeed=1.2;this.panSpeed=0.3;this.noRotate=false;this.noZoom=false;this.noPan=false;this.staticMoving=false;this.dynamicDampingFactor=0.2;this.minDistance=0;this.maxDistance=Infinity;this.keys=[65 ,83 ,68 ];this.target=new THREE.Vector3();var EPS=0.000001;var lastPosition=new THREE.Vector3();var _state=STATE.NONE,_prevState=STATE.NONE,_eye=new THREE.Vector3(),_movePrev=new THREE.Vector2(),_moveCurr=new THREE.Vector2(),_lastAxis=new THREE.Vector3(),_lastAngle=0,_zoomStart=new THREE.Vector2(),_zoomEnd=new THREE.Vector2(),_touchZoomDistanceStart=0,_touchZoomDistanceEnd=0,_panStart=new THREE.Vector2(),_panEnd=new THREE.Vector2();this.target0=this.target.clone();this.position0=this.object.position.clone();this.up0=this.object.up.clone();var changeEvent={type:'change'};var startEvent={type:'start'};var endEvent={type:'end'};this.handleResize=function(){if(this.domElement===document){this.screen.left=0;this.screen.top=0;this.screen.width=window.innerWidth;this.screen.height=window.innerHeight;}else{var box=this.domElement.getBoundingClientRect();var d=this.domElement.ownerDocument.documentElement;this.screen.left=box.left+window.pageXOffset-d.clientLeft;this.screen.top=box.top+window.pageYOffset-d.clientTop;this.screen.width=box.width;this.screen.height=box.height;}};var getMouseOnScreen=(function(){var vector=new THREE.Vector2();return function getMouseOnScreen(pageX,pageY){vector.set((pageX-_this.screen.left)/_this.screen.width,(pageY-_this.screen.top)/_this.screen.height);return vector;};}());var getMouseOnCircle=(function(){var vector=new THREE.Vector2();return function getMouseOnCircle(pageX,pageY){vector.set(((pageX-_this.screen.width*0.5-_this.screen.left)/(_this.screen.width*0.5)),((_this.screen.height+2*(_this.screen.top-pageY))/_this.screen.width));return vector;};}());this.rotateCamera=(function(){var axis=new THREE.Vector3(),quaternion=new THREE.Quaternion(),eyeDirection=new THREE.Vector3(),objectUpDirection=new THREE.Vector3(),objectSidewaysDirection=new THREE.Vector3(),moveDirection=new THREE.Vector3(),angle;return function rotateCamera(){moveDirection.set(_moveCurr.x-_movePrev.x,_moveCurr.y-_movePrev.y,0);angle=moveDirection.length();if(angle){_eye.copy(_this.object.position).sub(_this.target);eyeDirection.copy(_eye).normalize();objectUpDirection.copy(_this.object.up).normalize();objectSidewaysDirection.crossVectors(objectUpDirection,eyeDirection).normalize();objectUpDirection.setLength(_moveCurr.y-_movePrev.y);objectSidewaysDirection.setLength(_moveCurr.x-_movePrev.x);moveDirection.copy(objectUpDirection.add(objectSidewaysDirection));axis.crossVectors(moveDirection,_eye).normalize();angle*=_this.rotateSpeed;quaternion.setFromAxisAngle(axis,angle);_eye.applyQuaternion(quaternion);_this.object.up.applyQuaternion(quaternion);_lastAxis.copy(axis);_lastAngle=angle;}else if(!_this.staticMoving&&_lastAngle){_lastAngle*=Math.sqrt(1.0-_this.dynamicDampingFactor);_eye.copy(_this.object.position).sub(_this.target);quaternion.setFromAxisAngle(_lastAxis,_lastAngle);_eye.applyQuaternion(quaternion);_this.object.up.applyQuaternion(quaternion);}\n",
"_movePrev.copy(_moveCurr);};}());this.zoomCamera=function(){var factor;if(_state===STATE.TOUCH_ZOOM_PAN){factor=_touchZoomDistanceStart/_touchZoomDistanceEnd;_touchZoomDistanceStart=_touchZoomDistanceEnd;_eye.multiplyScalar(factor);}else{factor=1.0+(_zoomEnd.y-_zoomStart.y)*_this.zoomSpeed;if(factor!==1.0&&factor>0.0){_eye.multiplyScalar(factor);}\n",
"if(_this.staticMoving){_zoomStart.copy(_zoomEnd);}else{_zoomStart.y+=(_zoomEnd.y-_zoomStart.y)*this.dynamicDampingFactor;}}};this.panCamera=(function(){var mouseChange=new THREE.Vector2(),objectUp=new THREE.Vector3(),pan=new THREE.Vector3();return function panCamera(){mouseChange.copy(_panEnd).sub(_panStart);if(mouseChange.lengthSq()){mouseChange.multiplyScalar(_eye.length()*_this.panSpeed);pan.copy(_eye).cross(_this.object.up).setLength(mouseChange.x);pan.add(objectUp.copy(_this.object.up).setLength(mouseChange.y));_this.object.position.add(pan);_this.target.add(pan);if(_this.staticMoving){_panStart.copy(_panEnd);}else{_panStart.add(mouseChange.subVectors(_panEnd,_panStart).multiplyScalar(_this.dynamicDampingFactor));}}};}());this.checkDistances=function(){if(!_this.noZoom||!_this.noPan){if(_eye.lengthSq()>_this.maxDistance*_this.maxDistance){_this.object.position.addVectors(_this.target,_eye.setLength(_this.maxDistance));_zoomStart.copy(_zoomEnd);}\n",
"if(_eye.lengthSq()<_this.minDistance*_this.minDistance){_this.object.position.addVectors(_this.target,_eye.setLength(_this.minDistance));_zoomStart.copy(_zoomEnd);}}};this.update=function(){_eye.subVectors(_this.object.position,_this.target);if(!_this.noRotate){_this.rotateCamera();}\n",
"if(!_this.noZoom){_this.zoomCamera();}\n",
"if(!_this.noPan){_this.panCamera();}\n",
"_this.object.position.addVectors(_this.target,_eye);_this.checkDistances();_this.object.lookAt(_this.target);if(lastPosition.distanceToSquared(_this.object.position)>EPS){_this.dispatchEvent(changeEvent);lastPosition.copy(_this.object.position);}};this.reset=function(){_state=STATE.NONE;_prevState=STATE.NONE;_this.target.copy(_this.target0);_this.object.position.copy(_this.position0);_this.object.up.copy(_this.up0);_eye.subVectors(_this.object.position,_this.target);_this.object.lookAt(_this.target);_this.dispatchEvent(changeEvent);lastPosition.copy(_this.object.position);};function keydown(event){if(_this.enabled===false)return;window.removeEventListener('keydown',keydown);_prevState=_state;if(_state!==STATE.NONE){return;}else if(event.keyCode===_this.keys[STATE.ROTATE]&&!_this.noRotate){_state=STATE.ROTATE;}else if(event.keyCode===_this.keys[STATE.ZOOM]&&!_this.noZoom){_state=STATE.ZOOM;}else if(event.keyCode===_this.keys[STATE.PAN]&&!_this.noPan){_state=STATE.PAN;}}\n",
"function keyup(event){if(_this.enabled===false)return;_state=_prevState;window.addEventListener('keydown',keydown,false);}\n",
"function mousedown(event){if(_this.enabled===false)return;event.preventDefault();event.stopPropagation();if(_state===STATE.NONE){_state=event.button;}\n",
"if(_state===STATE.ROTATE&&!_this.noRotate){_moveCurr.copy(getMouseOnCircle(event.pageX,event.pageY));_movePrev.copy(_moveCurr);}else if(_state===STATE.ZOOM&&!_this.noZoom){_zoomStart.copy(getMouseOnScreen(event.pageX,event.pageY));_zoomEnd.copy(_zoomStart);}else if(_state===STATE.PAN&&!_this.noPan){_panStart.copy(getMouseOnScreen(event.pageX,event.pageY));_panEnd.copy(_panStart);}\n",
"document.addEventListener('mousemove',mousemove,false);document.addEventListener('mouseup',mouseup,false);_this.dispatchEvent(startEvent);}\n",
"function mousemove(event){if(_this.enabled===false)return;event.preventDefault();event.stopPropagation();if(_state===STATE.ROTATE&&!_this.noRotate){_movePrev.copy(_moveCurr);_moveCurr.copy(getMouseOnCircle(event.pageX,event.pageY));}else if(_state===STATE.ZOOM&&!_this.noZoom){_zoomEnd.copy(getMouseOnScreen(event.pageX,event.pageY));}else if(_state===STATE.PAN&&!_this.noPan){_panEnd.copy(getMouseOnScreen(event.pageX,event.pageY));}}\n",
"function mouseup(event){if(_this.enabled===false)return;event.preventDefault();event.stopPropagation();_state=STATE.NONE;document.removeEventListener('mousemove',mousemove);document.removeEventListener('mouseup',mouseup);_this.dispatchEvent(endEvent);}\n",
"function mousewheel(event){if(_this.enabled===false)return;if(_this.noZoom===true)return;event.preventDefault();event.stopPropagation();switch(event.deltaMode){case 2:_zoomStart.y-=event.deltaY*0.025;break;case 1:_zoomStart.y-=event.deltaY*0.01;break;default:_zoomStart.y-=event.deltaY*0.00025;break;}\n",
"_this.dispatchEvent(startEvent);_this.dispatchEvent(endEvent);}\n",
"function touchstart(event){if(_this.enabled===false)return;event.preventDefault();switch(event.touches.length){case 1:_state=STATE.TOUCH_ROTATE;_moveCurr.copy(getMouseOnCircle(event.touches[0].pageX,event.touches[0].pageY));_movePrev.copy(_moveCurr);break;default:_state=STATE.TOUCH_ZOOM_PAN;var dx=event.touches[0].pageX-event.touches[1].pageX;var dy=event.touches[0].pageY-event.touches[1].pageY;_touchZoomDistanceEnd=_touchZoomDistanceStart=Math.sqrt(dx*dx+dy*dy);var x=(event.touches[0].pageX+event.touches[1].pageX)/2;var y=(event.touches[0].pageY+event.touches[1].pageY)/2;_panStart.copy(getMouseOnScreen(x,y));_panEnd.copy(_panStart);break;}\n",
"_this.dispatchEvent(startEvent);}\n",
"function touchmove(event){if(_this.enabled===false)return;event.preventDefault();event.stopPropagation();switch(event.touches.length){case 1:_movePrev.copy(_moveCurr);_moveCurr.copy(getMouseOnCircle(event.touches[0].pageX,event.touches[0].pageY));break;default:var dx=event.touches[0].pageX-event.touches[1].pageX;var dy=event.touches[0].pageY-event.touches[1].pageY;_touchZoomDistanceEnd=Math.sqrt(dx*dx+dy*dy);var x=(event.touches[0].pageX+event.touches[1].pageX)/2;var y=(event.touches[0].pageY+event.touches[1].pageY)/2;_panEnd.copy(getMouseOnScreen(x,y));break;}}\n",
"function touchend(event){if(_this.enabled===false)return;switch(event.touches.length){case 0:_state=STATE.NONE;break;case 1:_state=STATE.TOUCH_ROTATE;_moveCurr.copy(getMouseOnCircle(event.touches[0].pageX,event.touches[0].pageY));_movePrev.copy(_moveCurr);break;}\n",
"_this.dispatchEvent(endEvent);}\n",
"function contextmenu(event){if(_this.enabled===false)return;event.preventDefault();}\n",
"this.dispose=function(){this.domElement.removeEventListener('contextmenu',contextmenu,false);this.domElement.removeEventListener('mousedown',mousedown,false);this.domElement.removeEventListener('wheel',mousewheel,false);this.domElement.removeEventListener('touchstart',touchstart,false);this.domElement.removeEventListener('touchend',touchend,false);this.domElement.removeEventListener('touchmove',touchmove,false);document.removeEventListener('mousemove',mousemove,false);document.removeEventListener('mouseup',mouseup,false);window.removeEventListener('keydown',keydown,false);window.removeEventListener('keyup',keyup,false);};this.domElement.addEventListener('contextmenu',contextmenu,false);this.domElement.addEventListener('mousedown',mousedown,false);this.domElement.addEventListener('wheel',mousewheel,false);this.domElement.addEventListener('touchstart',touchstart,false);this.domElement.addEventListener('touchend',touchend,false);this.domElement.addEventListener('touchmove',touchmove,false);window.addEventListener('keydown',keydown,false);window.addEventListener('keyup',keyup,false);this.handleResize();this.update();};THREE.TrackballControls.prototype=Object.create(THREE.EventDispatcher.prototype);THREE.TrackballControls.prototype.constructor=THREE.TrackballControls;THREE.GLTFLoader=(function(){function GLTFLoader(manager){this.manager=(manager!==undefined)?manager:THREE.DefaultLoadingManager;this.dracoLoader=null;this.ddsLoader=null;}\n",
"GLTFLoader.prototype={constructor:GLTFLoader,crossOrigin:'anonymous',load:function(url,onLoad,onProgress,onError){var scope=this;var resourcePath;if(this.resourcePath!==undefined){resourcePath=this.resourcePath;}else if(this.path!==undefined){resourcePath=this.path;}else{resourcePath=THREE.LoaderUtils.extractUrlBase(url);}\n",
"scope.manager.itemStart(url);var _onError=function(e){if(onError){onError(e);}else{console.error(e);}\n",
"scope.manager.itemError(url);scope.manager.itemEnd(url);};var loader=new THREE.FileLoader(scope.manager);loader.setPath(this.path);loader.setResponseType('arraybuffer');if(scope.crossOrigin==='use-credentials'){loader.setWithCredentials(true);}\n",
"loader.load(url,function(data){try{scope.parse(data,resourcePath,function(gltf){onLoad(gltf);scope.manager.itemEnd(url);},_onError);}catch(e){_onError(e);}},onProgress,_onError);},setCrossOrigin:function(value){this.crossOrigin=value;return this;},setPath:function(value){this.path=value;return this;},setResourcePath:function(value){this.resourcePath=value;return this;},setDRACOLoader:function(dracoLoader){this.dracoLoader=dracoLoader;return this;},setDDSLoader:function(ddsLoader){this.ddsLoader=ddsLoader;return this;},parse:function(data,path,onLoad,onError){var content;var extensions={};if(typeof data==='string'){content=data;}else{var magic=THREE.LoaderUtils.decodeText(new Uint8Array(data,0,4));if(magic===BINARY_EXTENSION_HEADER_MAGIC){try{extensions[EXTENSIONS.KHR_BINARY_GLTF]=new GLTFBinaryExtension(data);}catch(error){if(onError)onError(error);return;}\n",
"content=extensions[EXTENSIONS.KHR_BINARY_GLTF].content;}else{content=THREE.LoaderUtils.decodeText(new Uint8Array(data));}}\n",
"var json=JSON.parse(content);if(json.asset===undefined||json.asset.version[0]<2){if(onError)onError(new Error('THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported. Use LegacyGLTFLoader instead.'));return;}\n",
"if(json.extensionsUsed){for(var i=0;i<json.extensionsUsed.length;++i){var extensionName=json.extensionsUsed[i];var extensionsRequired=json.extensionsRequired||[];switch(extensionName){case EXTENSIONS.KHR_LIGHTS_PUNCTUAL:extensions[extensionName]=new GLTFLightsExtension(json);break;case EXTENSIONS.KHR_MATERIALS_UNLIT:extensions[extensionName]=new GLTFMaterialsUnlitExtension();break;case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:extensions[extensionName]=new GLTFMaterialsPbrSpecularGlossinessExtension();break;case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:extensions[extensionName]=new GLTFDracoMeshCompressionExtension(json,this.dracoLoader);break;case EXTENSIONS.MSFT_TEXTURE_DDS:extensions[EXTENSIONS.MSFT_TEXTURE_DDS]=new GLTFTextureDDSExtension(this.ddsLoader);break;case EXTENSIONS.KHR_TEXTURE_TRANSFORM:extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM]=new GLTFTextureTransformExtension();break;default:if(extensionsRequired.indexOf(extensionName)>=0){console.warn('THREE.GLTFLoader: Unknown extension &quot;'+extensionName+'&quot;.');}}}}\n",
"var parser=new GLTFParser(json,extensions,{path:path||this.resourcePath||'',crossOrigin:this.crossOrigin,manager:this.manager});parser.parse(onLoad,onError);}};function GLTFRegistry(){var objects={};return{get:function(key){return objects[key];},add:function(key,object){objects[key]=object;},remove:function(key){delete objects[key];},removeAll:function(){objects={};}};}\n",
"var EXTENSIONS={KHR_BINARY_GLTF:'KHR_binary_glTF',KHR_DRACO_MESH_COMPRESSION:'KHR_draco_mesh_compression',KHR_LIGHTS_PUNCTUAL:'KHR_lights_punctual',KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:'KHR_materials_pbrSpecularGlossiness',KHR_MATERIALS_UNLIT:'KHR_materials_unlit',KHR_TEXTURE_TRANSFORM:'KHR_texture_transform',MSFT_TEXTURE_DDS:'MSFT_texture_dds'};function GLTFTextureDDSExtension(ddsLoader){if(!ddsLoader){throw new Error('THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader');}\n",
"this.name=EXTENSIONS.MSFT_TEXTURE_DDS;this.ddsLoader=ddsLoader;}\n",
"function GLTFLightsExtension(json){this.name=EXTENSIONS.KHR_LIGHTS_PUNCTUAL;var extension=(json.extensions&&json.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL])||{};this.lightDefs=extension.lights||[];}\n",
"GLTFLightsExtension.prototype.loadLight=function(lightIndex){var lightDef=this.lightDefs[lightIndex];var lightNode;var color=new THREE.Color(0xffffff);if(lightDef.color!==undefined)color.fromArray(lightDef.color);var range=lightDef.range!==undefined?lightDef.range:0;switch(lightDef.type){case'directional':lightNode=new THREE.DirectionalLight(color);lightNode.target.position.set(0,0,-1);lightNode.add(lightNode.target);break;case'point':lightNode=new THREE.PointLight(color);lightNode.distance=range;break;case'spot':lightNode=new THREE.SpotLight(color);lightNode.distance=range;lightDef.spot=lightDef.spot||{};lightDef.spot.innerConeAngle=lightDef.spot.innerConeAngle!==undefined?lightDef.spot.innerConeAngle:0;lightDef.spot.outerConeAngle=lightDef.spot.outerConeAngle!==undefined?lightDef.spot.outerConeAngle:Math.PI/4.0;lightNode.angle=lightDef.spot.outerConeAngle;lightNode.penumbra=1.0-lightDef.spot.innerConeAngle/lightDef.spot.outerConeAngle;lightNode.target.position.set(0,0,-1);lightNode.add(lightNode.target);break;default:throw new Error('THREE.GLTFLoader: Unexpected light type, &quot;'+lightDef.type+'&quot;.');}\n",
"lightNode.position.set(0,0,0);lightNode.decay=2;if(lightDef.intensity!==undefined)lightNode.intensity=lightDef.intensity;lightNode.name=lightDef.name||('light_'+lightIndex);return Promise.resolve(lightNode);};function GLTFMaterialsUnlitExtension(){this.name=EXTENSIONS.KHR_MATERIALS_UNLIT;}\n",
"GLTFMaterialsUnlitExtension.prototype.getMaterialType=function(){return THREE.MeshBasicMaterial;};GLTFMaterialsUnlitExtension.prototype.extendParams=function(materialParams,materialDef,parser){var pending=[];materialParams.color=new THREE.Color(1.0,1.0,1.0);materialParams.opacity=1.0;var metallicRoughness=materialDef.pbrMetallicRoughness;if(metallicRoughness){if(Array.isArray(metallicRoughness.baseColorFactor)){var array=metallicRoughness.baseColorFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3];}\n",
"if(metallicRoughness.baseColorTexture!==undefined){pending.push(parser.assignTexture(materialParams,'map',metallicRoughness.baseColorTexture));}}\n",
"return Promise.all(pending);};var BINARY_EXTENSION_HEADER_MAGIC='glTF';var BINARY_EXTENSION_HEADER_LENGTH=12;var BINARY_EXTENSION_CHUNK_TYPES={JSON:0x4E4F534A,BIN:0x004E4942};function GLTFBinaryExtension(data){this.name=EXTENSIONS.KHR_BINARY_GLTF;this.content=null;this.body=null;var headerView=new DataView(data,0,BINARY_EXTENSION_HEADER_LENGTH);this.header={magic:THREE.LoaderUtils.decodeText(new Uint8Array(data.slice(0,4))),version:headerView.getUint32(4,true),length:headerView.getUint32(8,true)};if(this.header.magic!==BINARY_EXTENSION_HEADER_MAGIC){throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.');}else if(this.header.version<2.0){throw new Error('THREE.GLTFLoader: Legacy binary file detected. Use LegacyGLTFLoader instead.');}\n",
"var chunkView=new DataView(data,BINARY_EXTENSION_HEADER_LENGTH);var chunkIndex=0;while(chunkIndex<chunkView.byteLength){var chunkLength=chunkView.getUint32(chunkIndex,true);chunkIndex+=4;var chunkType=chunkView.getUint32(chunkIndex,true);chunkIndex+=4;if(chunkType===BINARY_EXTENSION_CHUNK_TYPES.JSON){var contentArray=new Uint8Array(data,BINARY_EXTENSION_HEADER_LENGTH+chunkIndex,chunkLength);this.content=THREE.LoaderUtils.decodeText(contentArray);}else if(chunkType===BINARY_EXTENSION_CHUNK_TYPES.BIN){var byteOffset=BINARY_EXTENSION_HEADER_LENGTH+chunkIndex;this.body=data.slice(byteOffset,byteOffset+chunkLength);}\n",
"chunkIndex+=chunkLength;}\n",
"if(this.content===null){throw new Error('THREE.GLTFLoader: JSON content not found.');}}\n",
"function GLTFDracoMeshCompressionExtension(json,dracoLoader){if(!dracoLoader){throw new Error('THREE.GLTFLoader: No DRACOLoader instance provided.');}\n",
"this.name=EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;this.json=json;this.dracoLoader=dracoLoader;}\n",
"GLTFDracoMeshCompressionExtension.prototype.decodePrimitive=function(primitive,parser){var json=this.json;var dracoLoader=this.dracoLoader;var bufferViewIndex=primitive.extensions[this.name].bufferView;var gltfAttributeMap=primitive.extensions[this.name].attributes;var threeAttributeMap={};var attributeNormalizedMap={};var attributeTypeMap={};for(var attributeName in gltfAttributeMap){var threeAttributeName=ATTRIBUTES[attributeName]||attributeName.toLowerCase();threeAttributeMap[threeAttributeName]=gltfAttributeMap[attributeName];}\n",
"for(attributeName in primitive.attributes){var threeAttributeName=ATTRIBUTES[attributeName]||attributeName.toLowerCase();if(gltfAttributeMap[attributeName]!==undefined){var accessorDef=json.accessors[primitive.attributes[attributeName]];var componentType=WEBGL_COMPONENT_TYPES[accessorDef.componentType];attributeTypeMap[threeAttributeName]=componentType;attributeNormalizedMap[threeAttributeName]=accessorDef.normalized===true;}}\n",
"return parser.getDependency('bufferView',bufferViewIndex).then(function(bufferView){return new Promise(function(resolve){dracoLoader.decodeDracoFile(bufferView,function(geometry){for(var attributeName in geometry.attributes){var attribute=geometry.attributes[attributeName];var normalized=attributeNormalizedMap[attributeName];if(normalized!==undefined)attribute.normalized=normalized;}\n",
"resolve(geometry);},threeAttributeMap,attributeTypeMap);});});};function GLTFTextureTransformExtension(){this.name=EXTENSIONS.KHR_TEXTURE_TRANSFORM;}\n",
"GLTFTextureTransformExtension.prototype.extendTexture=function(texture,transform){texture=texture.clone();if(transform.offset!==undefined){texture.offset.fromArray(transform.offset);}\n",
"if(transform.rotation!==undefined){texture.rotation=transform.rotation;}\n",
"if(transform.scale!==undefined){texture.repeat.fromArray(transform.scale);}\n",
"if(transform.texCoord!==undefined){console.warn('THREE.GLTFLoader: Custom UV sets in &quot;'+this.name+'&quot; extension not yet supported.');}\n",
"texture.needsUpdate=true;return texture;};function GLTFMaterialsPbrSpecularGlossinessExtension(){return{name:EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,specularGlossinessParams:['color','map','lightMap','lightMapIntensity','aoMap','aoMapIntensity','emissive','emissiveIntensity','emissiveMap','bumpMap','bumpScale','normalMap','displacementMap','displacementScale','displacementBias','specularMap','specular','glossinessMap','glossiness','alphaMap','envMap','envMapIntensity','refractionRatio',],getMaterialType:function(){return THREE.ShaderMaterial;},extendParams:function(materialParams,materialDef,parser){var pbrSpecularGlossiness=materialDef.extensions[this.name];var shader=THREE.ShaderLib['standard'];var uniforms=THREE.UniformsUtils.clone(shader.uniforms);var specularMapParsFragmentChunk=['#ifdef USE_SPECULARMAP','\tuniform sampler2D specularMap;','#endif'].join('\\n');var glossinessMapParsFragmentChunk=['#ifdef USE_GLOSSINESSMAP','\tuniform sampler2D glossinessMap;','#endif'].join('\\n');var specularMapFragmentChunk=['vec3 specularFactor = specular;','#ifdef USE_SPECULARMAP','\tvec4 texelSpecular = texture2D( specularMap, vUv );','\ttexelSpecular = sRGBToLinear( texelSpecular );','\t// reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture','\tspecularFactor *= texelSpecular.rgb;','#endif'].join('\\n');var glossinessMapFragmentChunk=['float glossinessFactor = glossiness;','#ifdef USE_GLOSSINESSMAP','\tvec4 texelGlossiness = texture2D( glossinessMap, vUv );','\t// reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture','\tglossinessFactor *= texelGlossiness.a;','#endif'].join('\\n');var lightPhysicalFragmentChunk=['PhysicalMaterial material;','material.diffuseColor = diffuseColor.rgb;','material.specularRoughness = clamp( 1.0 - glossinessFactor, 0.04, 1.0 );','material.specularColor = specularFactor.rgb;',].join('\\n');var fragmentShader=shader.fragmentShader.replace('uniform float roughness;','uniform vec3 specular;').replace('uniform float metalness;','uniform float glossiness;').replace('#include <roughnessmap_pars_fragment>',specularMapParsFragmentChunk).replace('#include <metalnessmap_pars_fragment>',glossinessMapParsFragmentChunk).replace('#include <roughnessmap_fragment>',specularMapFragmentChunk).replace('#include <metalnessmap_fragment>',glossinessMapFragmentChunk).replace('#include <lights_physical_fragment>',lightPhysicalFragmentChunk);delete uniforms.roughness;delete uniforms.metalness;delete uniforms.roughnessMap;delete uniforms.metalnessMap;uniforms.specular={value:new THREE.Color().setHex(0x111111)};uniforms.glossiness={value:0.5};uniforms.specularMap={value:null};uniforms.glossinessMap={value:null};materialParams.vertexShader=shader.vertexShader;materialParams.fragmentShader=fragmentShader;materialParams.uniforms=uniforms;materialParams.defines={'STANDARD':''};materialParams.color=new THREE.Color(1.0,1.0,1.0);materialParams.opacity=1.0;var pending=[];if(Array.isArray(pbrSpecularGlossiness.diffuseFactor)){var array=pbrSpecularGlossiness.diffuseFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3];}\n",
"if(pbrSpecularGlossiness.diffuseTexture!==undefined){pending.push(parser.assignTexture(materialParams,'map',pbrSpecularGlossiness.diffuseTexture));}\n",
"materialParams.emissive=new THREE.Color(0.0,0.0,0.0);materialParams.glossiness=pbrSpecularGlossiness.glossinessFactor!==undefined?pbrSpecularGlossiness.glossinessFactor:1.0;materialParams.specular=new THREE.Color(1.0,1.0,1.0);if(Array.isArray(pbrSpecularGlossiness.specularFactor)){materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor);}\n",
"if(pbrSpecularGlossiness.specularGlossinessTexture!==undefined){var specGlossMapDef=pbrSpecularGlossiness.specularGlossinessTexture;pending.push(parser.assignTexture(materialParams,'glossinessMap',specGlossMapDef));pending.push(parser.assignTexture(materialParams,'specularMap',specGlossMapDef));}\n",
"return Promise.all(pending);},createMaterial:function(params){var material=new THREE.ShaderMaterial({defines:params.defines,vertexShader:params.vertexShader,fragmentShader:params.fragmentShader,uniforms:params.uniforms,fog:true,lights:true,opacity:params.opacity,transparent:params.transparent});material.isGLTFSpecularGlossinessMaterial=true;material.color=params.color;material.map=params.map===undefined?null:params.map;material.lightMap=null;material.lightMapIntensity=1.0;material.aoMap=params.aoMap===undefined?null:params.aoMap;material.aoMapIntensity=1.0;material.emissive=params.emissive;material.emissiveIntensity=1.0;material.emissiveMap=params.emissiveMap===undefined?null:params.emissiveMap;material.bumpMap=params.bumpMap===undefined?null:params.bumpMap;material.bumpScale=1;material.normalMap=params.normalMap===undefined?null:params.normalMap;if(params.normalScale)material.normalScale=params.normalScale;material.displacementMap=null;material.displacementScale=1;material.displacementBias=0;material.specularMap=params.specularMap===undefined?null:params.specularMap;material.specular=params.specular;material.glossinessMap=params.glossinessMap===undefined?null:params.glossinessMap;material.glossiness=params.glossiness;material.alphaMap=null;material.envMap=params.envMap===undefined?null:params.envMap;material.envMapIntensity=1.0;material.refractionRatio=0.98;material.extensions.derivatives=true;return material;},cloneMaterial:function(source){var target=source.clone();target.isGLTFSpecularGlossinessMaterial=true;var params=this.specularGlossinessParams;for(var i=0,il=params.length;i<il;i++){var value=source[params[i]];target[params[i]]=(value&&value.isColor)?value.clone():value;}\n",
"return target;},refreshUniforms:function(renderer,scene,camera,geometry,material){if(material.isGLTFSpecularGlossinessMaterial!==true){return;}\n",
"var uniforms=material.uniforms;var defines=material.defines;uniforms.opacity.value=material.opacity;uniforms.diffuse.value.copy(material.color);uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity);uniforms.map.value=material.map;uniforms.specularMap.value=material.specularMap;uniforms.alphaMap.value=material.alphaMap;uniforms.lightMap.value=material.lightMap;uniforms.lightMapIntensity.value=material.lightMapIntensity;uniforms.aoMap.value=material.aoMap;uniforms.aoMapIntensity.value=material.aoMapIntensity;var uvScaleMap;if(material.map){uvScaleMap=material.map;}else if(material.specularMap){uvScaleMap=material.specularMap;}else if(material.displacementMap){uvScaleMap=material.displacementMap;}else if(material.normalMap){uvScaleMap=material.normalMap;}else if(material.bumpMap){uvScaleMap=material.bumpMap;}else if(material.glossinessMap){uvScaleMap=material.glossinessMap;}else if(material.alphaMap){uvScaleMap=material.alphaMap;}else if(material.emissiveMap){uvScaleMap=material.emissiveMap;}\n",
"if(uvScaleMap!==undefined){if(uvScaleMap.isWebGLRenderTarget){uvScaleMap=uvScaleMap.texture;}\n",
"if(uvScaleMap.matrixAutoUpdate===true){uvScaleMap.updateMatrix();}\n",
"uniforms.uvTransform.value.copy(uvScaleMap.matrix);}\n",
"if(material.envMap){uniforms.envMap.value=material.envMap;uniforms.envMapIntensity.value=material.envMapIntensity;uniforms.flipEnvMap.value=material.envMap.isCubeTexture?-1:1;uniforms.reflectivity.value=material.reflectivity;uniforms.refractionRatio.value=material.refractionRatio;uniforms.maxMipLevel.value=renderer.properties.get(material.envMap).__maxMipLevel;}\n",
"uniforms.specular.value.copy(material.specular);uniforms.glossiness.value=material.glossiness;uniforms.glossinessMap.value=material.glossinessMap;uniforms.emissiveMap.value=material.emissiveMap;uniforms.bumpMap.value=material.bumpMap;uniforms.normalMap.value=material.normalMap;uniforms.displacementMap.value=material.displacementMap;uniforms.displacementScale.value=material.displacementScale;uniforms.displacementBias.value=material.displacementBias;if(uniforms.glossinessMap.value!==null&&defines.USE_GLOSSINESSMAP===undefined){defines.USE_GLOSSINESSMAP='';defines.USE_ROUGHNESSMAP='';}\n",
"if(uniforms.glossinessMap.value===null&&defines.USE_GLOSSINESSMAP!==undefined){delete defines.USE_GLOSSINESSMAP;delete defines.USE_ROUGHNESSMAP;}}};}\n",
"function GLTFCubicSplineInterpolant(parameterPositions,sampleValues,sampleSize,resultBuffer){THREE.Interpolant.call(this,parameterPositions,sampleValues,sampleSize,resultBuffer);}\n",
"GLTFCubicSplineInterpolant.prototype=Object.create(THREE.Interpolant.prototype);GLTFCubicSplineInterpolant.prototype.constructor=GLTFCubicSplineInterpolant;GLTFCubicSplineInterpolant.prototype.copySampleValue_=function(index){var result=this.resultBuffer,values=this.sampleValues,valueSize=this.valueSize,offset=index*valueSize*3+valueSize;for(var i=0;i!==valueSize;i++){result[i]=values[offset+i];}\n",
"return result;};GLTFCubicSplineInterpolant.prototype.beforeStart_=GLTFCubicSplineInterpolant.prototype.copySampleValue_;GLTFCubicSplineInterpolant.prototype.afterEnd_=GLTFCubicSplineInterpolant.prototype.copySampleValue_;GLTFCubicSplineInterpolant.prototype.interpolate_=function(i1,t0,t,t1){var result=this.resultBuffer;var values=this.sampleValues;var stride=this.valueSize;var stride2=stride*2;var stride3=stride*3;var td=t1-t0;var p=(t-t0)/td;var pp=p*p;var ppp=pp*p;var offset1=i1*stride3;var offset0=offset1-stride3;var s2=-2*ppp+3*pp;var s3=ppp-pp;var s0=1-s2;var s1=s3-pp+p;for(var i=0;i!==stride;i++){var p0=values[offset0+i+stride];var m0=values[offset0+i+stride2]*td;var p1=values[offset1+i+stride];var m1=values[offset1+i]*td;result[i]=s0*p0+s1*m0+s2*p1+s3*m1;}\n",
"return result;};var WEBGL_CONSTANTS={FLOAT:5126,FLOAT_MAT3:35675,FLOAT_MAT4:35676,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,LINEAR:9729,REPEAT:10497,SAMPLER_2D:35678,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,UNSIGNED_BYTE:5121,UNSIGNED_SHORT:5123};var WEBGL_COMPONENT_TYPES={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array};var WEBGL_FILTERS={9728:THREE.NearestFilter,9729:THREE.LinearFilter,9984:THREE.NearestMipMapNearestFilter,9985:THREE.LinearMipMapNearestFilter,9986:THREE.NearestMipMapLinearFilter,9987:THREE.LinearMipMapLinearFilter};var WEBGL_WRAPPINGS={33071:THREE.ClampToEdgeWrapping,33648:THREE.MirroredRepeatWrapping,10497:THREE.RepeatWrapping};var WEBGL_TYPE_SIZES={'SCALAR':1,'VEC2':2,'VEC3':3,'VEC4':4,'MAT2':4,'MAT3':9,'MAT4':16};var ATTRIBUTES={POSITION:'position',NORMAL:'normal',TANGENT:'tangent',TEXCOORD_0:'uv',TEXCOORD_1:'uv2',COLOR_0:'color',WEIGHTS_0:'skinWeight',JOINTS_0:'skinIndex',};var PATH_PROPERTIES={scale:'scale',translation:'position',rotation:'quaternion',weights:'morphTargetInfluences'};var INTERPOLATION={CUBICSPLINE:undefined,LINEAR:THREE.InterpolateLinear,STEP:THREE.InterpolateDiscrete};var ALPHA_MODES={OPAQUE:'OPAQUE',MASK:'MASK',BLEND:'BLEND'};var MIME_TYPE_FORMATS={'image/png':THREE.RGBAFormat,'image/jpeg':THREE.RGBFormat};function resolveURL(url,path){if(typeof url!=='string'||url==='')return'';if(/^(https?:)?\\/\\//i.test(url))return url;if(/^data:.*,.*$/i.test(url))return url;if(/^blob:.*$/i.test(url))return url;return path+url;}\n",
"var defaultMaterial;function createDefaultMaterial(){defaultMaterial=defaultMaterial||new THREE.MeshStandardMaterial({color:0xFFFFFF,emissive:0x000000,metalness:1,roughness:1,transparent:false,depthTest:true,side:THREE.FrontSide});return defaultMaterial;}\n",
"function addUnknownExtensionsToUserData(knownExtensions,object,objectDef){for(var name in objectDef.extensions){if(knownExtensions[name]===undefined){object.userData.gltfExtensions=object.userData.gltfExtensions||{};object.userData.gltfExtensions[name]=objectDef.extensions[name];}}}\n",
"function assignExtrasToUserData(object,gltfDef){if(gltfDef.extras!==undefined){if(typeof gltfDef.extras==='object'){Object.assign(object.userData,gltfDef.extras);}else{console.warn('THREE.GLTFLoader: Ignoring primitive type .extras, '+gltfDef.extras);}}}\n",
"function addMorphTargets(geometry,targets,parser){var hasMorphPosition=false;var hasMorphNormal=false;for(var i=0,il=targets.length;i<il;i++){var target=targets[i];if(target.POSITION!==undefined)hasMorphPosition=true;if(target.NORMAL!==undefined)hasMorphNormal=true;if(hasMorphPosition&&hasMorphNormal)break;}\n",
"if(!hasMorphPosition&&!hasMorphNormal)return Promise.resolve(geometry);var pendingPositionAccessors=[];var pendingNormalAccessors=[];for(var i=0,il=targets.length;i<il;i++){var target=targets[i];if(hasMorphPosition){var pendingAccessor=target.POSITION!==undefined?parser.getDependency('accessor',target.POSITION):geometry.attributes.position;pendingPositionAccessors.push(pendingAccessor);}\n",
"if(hasMorphNormal){var pendingAccessor=target.NORMAL!==undefined?parser.getDependency('accessor',target.NORMAL):geometry.attributes.normal;pendingNormalAccessors.push(pendingAccessor);}}\n",
"return Promise.all([Promise.all(pendingPositionAccessors),Promise.all(pendingNormalAccessors)]).then(function(accessors){var morphPositions=accessors[0];var morphNormals=accessors[1];for(var i=0,il=morphPositions.length;i<il;i++){if(geometry.attributes.position===morphPositions[i])continue;morphPositions[i]=cloneBufferAttribute(morphPositions[i]);}\n",
"for(var i=0,il=morphNormals.length;i<il;i++){if(geometry.attributes.normal===morphNormals[i])continue;morphNormals[i]=cloneBufferAttribute(morphNormals[i]);}\n",
"for(var i=0,il=targets.length;i<il;i++){var target=targets[i];var attributeName='morphTarget'+i;if(hasMorphPosition){if(target.POSITION!==undefined){var positionAttribute=morphPositions[i];positionAttribute.name=attributeName;var position=geometry.attributes.position;for(var j=0,jl=positionAttribute.count;j<jl;j++){positionAttribute.setXYZ(j,positionAttribute.getX(j)+position.getX(j),positionAttribute.getY(j)+position.getY(j),positionAttribute.getZ(j)+position.getZ(j));}}}\n",
"if(hasMorphNormal){if(target.NORMAL!==undefined){var normalAttribute=morphNormals[i];normalAttribute.name=attributeName;var normal=geometry.attributes.normal;for(var j=0,jl=normalAttribute.count;j<jl;j++){normalAttribute.setXYZ(j,normalAttribute.getX(j)+normal.getX(j),normalAttribute.getY(j)+normal.getY(j),normalAttribute.getZ(j)+normal.getZ(j));}}}}\n",
"if(hasMorphPosition)geometry.morphAttributes.position=morphPositions;if(hasMorphNormal)geometry.morphAttributes.normal=morphNormals;return geometry;});}\n",
"function updateMorphTargets(mesh,meshDef){mesh.updateMorphTargets();if(meshDef.weights!==undefined){for(var i=0,il=meshDef.weights.length;i<il;i++){mesh.morphTargetInfluences[i]=meshDef.weights[i];}}\n",
"if(meshDef.extras&&Array.isArray(meshDef.extras.targetNames)){var targetNames=meshDef.extras.targetNames;if(mesh.morphTargetInfluences.length===targetNames.length){mesh.morphTargetDictionary={};for(var i=0,il=targetNames.length;i<il;i++){mesh.morphTargetDictionary[targetNames[i]]=i;}}else{console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.');}}}\n",
"function createPrimitiveKey(primitiveDef){var dracoExtension=primitiveDef.extensions&&primitiveDef.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION];var geometryKey;if(dracoExtension){geometryKey='draco:'+dracoExtension.bufferView\n",
"+':'+dracoExtension.indices\n",
"+':'+createAttributesKey(dracoExtension.attributes);}else{geometryKey=primitiveDef.indices+':'+createAttributesKey(primitiveDef.attributes)+':'+primitiveDef.mode;}\n",
"return geometryKey;}\n",
"function createAttributesKey(attributes){var attributesKey='';var keys=Object.keys(attributes).sort();for(var i=0,il=keys.length;i<il;i++){attributesKey+=keys[i]+':'+attributes[keys[i]]+';';}\n",
"return attributesKey;}\n",
"function cloneBufferAttribute(attribute){if(attribute.isInterleavedBufferAttribute){var count=attribute.count;var itemSize=attribute.itemSize;var array=attribute.array.slice(0,count*itemSize);for(var i=0,j=0;i<count;++i){array[j++]=attribute.getX(i);if(itemSize>=2)array[j++]=attribute.getY(i);if(itemSize>=3)array[j++]=attribute.getZ(i);if(itemSize>=4)array[j++]=attribute.getW(i);}\n",
"return new THREE.BufferAttribute(array,itemSize,attribute.normalized);}\n",
"return attribute.clone();}\n",
"function GLTFParser(json,extensions,options){this.json=json||{};this.extensions=extensions||{};this.options=options||{};this.cache=new GLTFRegistry();this.primitiveCache={};this.textureLoader=new THREE.TextureLoader(this.options.manager);this.textureLoader.setCrossOrigin(this.options.crossOrigin);this.fileLoader=new THREE.FileLoader(this.options.manager);this.fileLoader.setResponseType('arraybuffer');if(this.options.crossOrigin==='use-credentials'){this.fileLoader.setWithCredentials(true);}}\n",
"GLTFParser.prototype.parse=function(onLoad,onError){var parser=this;var json=this.json;var extensions=this.extensions;this.cache.removeAll();this.markDefs();Promise.all([this.getDependencies('scene'),this.getDependencies('animation'),this.getDependencies('camera'),]).then(function(dependencies){var result={scene:dependencies[0][json.scene||0],scenes:dependencies[0],animations:dependencies[1],cameras:dependencies[2],asset:json.asset,parser:parser,userData:{}};addUnknownExtensionsToUserData(extensions,result,json);assignExtrasToUserData(result,json);onLoad(result);}).catch(onError);};GLTFParser.prototype.markDefs=function(){var nodeDefs=this.json.nodes||[];var skinDefs=this.json.skins||[];var meshDefs=this.json.meshes||[];var meshReferences={};var meshUses={};for(var skinIndex=0,skinLength=skinDefs.length;skinIndex<skinLength;skinIndex++){var joints=skinDefs[skinIndex].joints;for(var i=0,il=joints.length;i<il;i++){nodeDefs[joints[i]].isBone=true;}}\n",
"for(var nodeIndex=0,nodeLength=nodeDefs.length;nodeIndex<nodeLength;nodeIndex++){var nodeDef=nodeDefs[nodeIndex];if(nodeDef.mesh!==undefined){if(meshReferences[nodeDef.mesh]===undefined){meshReferences[nodeDef.mesh]=meshUses[nodeDef.mesh]=0;}\n",
"meshReferences[nodeDef.mesh]++;if(nodeDef.skin!==undefined){meshDefs[nodeDef.mesh].isSkinnedMesh=true;}}}\n",
"this.json.meshReferences=meshReferences;this.json.meshUses=meshUses;};GLTFParser.prototype.getDependency=function(type,index){var cacheKey=type+':'+index;var dependency=this.cache.get(cacheKey);if(!dependency){switch(type){case'scene':dependency=this.loadScene(index);break;case'node':dependency=this.loadNode(index);break;case'mesh':dependency=this.loadMesh(index);break;case'accessor':dependency=this.loadAccessor(index);break;case'bufferView':dependency=this.loadBufferView(index);break;case'buffer':dependency=this.loadBuffer(index);break;case'material':dependency=this.loadMaterial(index);break;case'texture':dependency=this.loadTexture(index);break;case'skin':dependency=this.loadSkin(index);break;case'animation':dependency=this.loadAnimation(index);break;case'camera':dependency=this.loadCamera(index);break;case'light':dependency=this.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].loadLight(index);break;default:throw new Error('Unknown type: '+type);}\n",
"this.cache.add(cacheKey,dependency);}\n",
"return dependency;};GLTFParser.prototype.getDependencies=function(type){var dependencies=this.cache.get(type);if(!dependencies){var parser=this;var defs=this.json[type+(type==='mesh'?'es':'s')]||[];dependencies=Promise.all(defs.map(function(def,index){return parser.getDependency(type,index);}));this.cache.add(type,dependencies);}\n",
"return dependencies;};GLTFParser.prototype.loadBuffer=function(bufferIndex){var bufferDef=this.json.buffers[bufferIndex];var loader=this.fileLoader;if(bufferDef.type&&bufferDef.type!=='arraybuffer'){throw new Error('THREE.GLTFLoader: '+bufferDef.type+' buffer type is not supported.');}\n",
"if(bufferDef.uri===undefined&&bufferIndex===0){return Promise.resolve(this.extensions[EXTENSIONS.KHR_BINARY_GLTF].body);}\n",
"var options=this.options;return new Promise(function(resolve,reject){loader.load(resolveURL(bufferDef.uri,options.path),resolve,undefined,function(){reject(new Error('THREE.GLTFLoader: Failed to load buffer &quot;'+bufferDef.uri+'&quot;.'));});});};GLTFParser.prototype.loadBufferView=function(bufferViewIndex){var bufferViewDef=this.json.bufferViews[bufferViewIndex];return this.getDependency('buffer',bufferViewDef.buffer).then(function(buffer){var byteLength=bufferViewDef.byteLength||0;var byteOffset=bufferViewDef.byteOffset||0;return buffer.slice(byteOffset,byteOffset+byteLength);});};GLTFParser.prototype.loadAccessor=function(accessorIndex){var parser=this;var json=this.json;var accessorDef=this.json.accessors[accessorIndex];if(accessorDef.bufferView===undefined&&accessorDef.sparse===undefined){return Promise.resolve(null);}\n",
"var pendingBufferViews=[];if(accessorDef.bufferView!==undefined){pendingBufferViews.push(this.getDependency('bufferView',accessorDef.bufferView));}else{pendingBufferViews.push(null);}\n",
"if(accessorDef.sparse!==undefined){pendingBufferViews.push(this.getDependency('bufferView',accessorDef.sparse.indices.bufferView));pendingBufferViews.push(this.getDependency('bufferView',accessorDef.sparse.values.bufferView));}\n",
"return Promise.all(pendingBufferViews).then(function(bufferViews){var bufferView=bufferViews[0];var itemSize=WEBGL_TYPE_SIZES[accessorDef.type];var TypedArray=WEBGL_COMPONENT_TYPES[accessorDef.componentType];var elementBytes=TypedArray.BYTES_PER_ELEMENT;var itemBytes=elementBytes*itemSize;var byteOffset=accessorDef.byteOffset||0;var byteStride=accessorDef.bufferView!==undefined?json.bufferViews[accessorDef.bufferView].byteStride:undefined;var normalized=accessorDef.normalized===true;var array,bufferAttribute;if(byteStride&&byteStride!==itemBytes){var ibCacheKey='InterleavedBuffer:'+accessorDef.bufferView+':'+accessorDef.componentType;var ib=parser.cache.get(ibCacheKey);if(!ib){array=new TypedArray(bufferView);ib=new THREE.InterleavedBuffer(array,byteStride/elementBytes);parser.cache.add(ibCacheKey,ib);}\n",
"bufferAttribute=new THREE.InterleavedBufferAttribute(ib,itemSize,byteOffset/elementBytes,normalized);}else{if(bufferView===null){array=new TypedArray(accessorDef.count*itemSize);}else{array=new TypedArray(bufferView,byteOffset,accessorDef.count*itemSize);}\n",
"bufferAttribute=new THREE.BufferAttribute(array,itemSize,normalized);}\n",
"if(accessorDef.sparse!==undefined){var itemSizeIndices=WEBGL_TYPE_SIZES.SCALAR;var TypedArrayIndices=WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType];var byteOffsetIndices=accessorDef.sparse.indices.byteOffset||0;var byteOffsetValues=accessorDef.sparse.values.byteOffset||0;var sparseIndices=new TypedArrayIndices(bufferViews[1],byteOffsetIndices,accessorDef.sparse.count*itemSizeIndices);var sparseValues=new TypedArray(bufferViews[2],byteOffsetValues,accessorDef.sparse.count*itemSize);if(bufferView!==null){bufferAttribute.setArray(bufferAttribute.array.slice());}\n",
"for(var i=0,il=sparseIndices.length;i<il;i++){var index=sparseIndices[i];bufferAttribute.setX(index,sparseValues[i*itemSize]);if(itemSize>=2)bufferAttribute.setY(index,sparseValues[i*itemSize+1]);if(itemSize>=3)bufferAttribute.setZ(index,sparseValues[i*itemSize+2]);if(itemSize>=4)bufferAttribute.setW(index,sparseValues[i*itemSize+3]);if(itemSize>=5)throw new Error('THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.');}}\n",
"return bufferAttribute;});};GLTFParser.prototype.loadTexture=function(textureIndex){var parser=this;var json=this.json;var options=this.options;var textureLoader=this.textureLoader;var URL=window.URL||window.webkitURL;var textureDef=json.textures[textureIndex];var textureExtensions=textureDef.extensions||{};var source;if(textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS]){source=json.images[textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS].source];}else{source=json.images[textureDef.source];}\n",
"var sourceURI=source.uri;var isObjectURL=false;if(source.bufferView!==undefined){sourceURI=parser.getDependency('bufferView',source.bufferView).then(function(bufferView){isObjectURL=true;var blob=new Blob([bufferView],{type:source.mimeType});sourceURI=URL.createObjectURL(blob);return sourceURI;});}\n",
"return Promise.resolve(sourceURI).then(function(sourceURI){var loader=THREE.Loader.Handlers.get(sourceURI);if(!loader){loader=textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS]?parser.extensions[EXTENSIONS.MSFT_TEXTURE_DDS].ddsLoader:textureLoader;}\n",
"return new Promise(function(resolve,reject){loader.load(resolveURL(sourceURI,options.path),resolve,undefined,reject);});}).then(function(texture){if(isObjectURL===true){URL.revokeObjectURL(sourceURI);}\n",
"texture.flipY=false;if(textureDef.name!==undefined)texture.name=textureDef.name;if(source.mimeType in MIME_TYPE_FORMATS){texture.format=MIME_TYPE_FORMATS[source.mimeType];}\n",
"var samplers=json.samplers||{};var sampler=samplers[textureDef.sampler]||{};texture.magFilter=WEBGL_FILTERS[sampler.magFilter]||THREE.LinearFilter;texture.minFilter=WEBGL_FILTERS[sampler.minFilter]||THREE.LinearMipMapLinearFilter;texture.wrapS=WEBGL_WRAPPINGS[sampler.wrapS]||THREE.RepeatWrapping;texture.wrapT=WEBGL_WRAPPINGS[sampler.wrapT]||THREE.RepeatWrapping;return texture;});};GLTFParser.prototype.assignTexture=function(materialParams,mapName,mapDef){var parser=this;return this.getDependency('texture',mapDef.index).then(function(texture){if(!texture.isCompressedTexture){switch(mapName){case'aoMap':case'emissiveMap':case'metalnessMap':case'normalMap':case'roughnessMap':texture.format=THREE.RGBFormat;break;}}\n",
"if(parser.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM]){var transform=mapDef.extensions!==undefined?mapDef.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM]:undefined;if(transform){texture=parser.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM].extendTexture(texture,transform);}}\n",
"materialParams[mapName]=texture;});};GLTFParser.prototype.assignFinalMaterial=function(mesh){var geometry=mesh.geometry;var material=mesh.material;var extensions=this.extensions;var useVertexTangents=geometry.attributes.tangent!==undefined;var useVertexColors=geometry.attributes.color!==undefined;var useFlatShading=geometry.attributes.normal===undefined;var useSkinning=mesh.isSkinnedMesh===true;var useMorphTargets=Object.keys(geometry.morphAttributes).length>0;var useMorphNormals=useMorphTargets&&geometry.morphAttributes.normal!==undefined;if(mesh.isPoints){var cacheKey='PointsMaterial:'+material.uuid;var pointsMaterial=this.cache.get(cacheKey);if(!pointsMaterial){pointsMaterial=new THREE.PointsMaterial();THREE.Material.prototype.copy.call(pointsMaterial,material);pointsMaterial.color.copy(material.color);pointsMaterial.map=material.map;pointsMaterial.lights=false;this.cache.add(cacheKey,pointsMaterial);}\n",
"material=pointsMaterial;}else if(mesh.isLine){var cacheKey='LineBasicMaterial:'+material.uuid;var lineMaterial=this.cache.get(cacheKey);if(!lineMaterial){lineMaterial=new THREE.LineBasicMaterial();THREE.Material.prototype.copy.call(lineMaterial,material);lineMaterial.color.copy(material.color);lineMaterial.lights=false;this.cache.add(cacheKey,lineMaterial);}\n",
"material=lineMaterial;}\n",
"if(useVertexTangents||useVertexColors||useFlatShading||useSkinning||useMorphTargets){var cacheKey='ClonedMaterial:'+material.uuid+':';if(material.isGLTFSpecularGlossinessMaterial)cacheKey+='specular-glossiness:';if(useSkinning)cacheKey+='skinning:';if(useVertexTangents)cacheKey+='vertex-tangents:';if(useVertexColors)cacheKey+='vertex-colors:';if(useFlatShading)cacheKey+='flat-shading:';if(useMorphTargets)cacheKey+='morph-targets:';if(useMorphNormals)cacheKey+='morph-normals:';var cachedMaterial=this.cache.get(cacheKey);if(!cachedMaterial){cachedMaterial=material.isGLTFSpecularGlossinessMaterial?extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].cloneMaterial(material):material.clone();if(useSkinning)cachedMaterial.skinning=true;if(useVertexTangents)cachedMaterial.vertexTangents=true;if(useVertexColors)cachedMaterial.vertexColors=THREE.VertexColors;if(useFlatShading)cachedMaterial.flatShading=true;if(useMorphTargets)cachedMaterial.morphTargets=true;if(useMorphNormals)cachedMaterial.morphNormals=true;this.cache.add(cacheKey,cachedMaterial);}\n",
"material=cachedMaterial;}\n",
"if(material.aoMap&&geometry.attributes.uv2===undefined&&geometry.attributes.uv!==undefined){console.log('THREE.GLTFLoader: Duplicating UVs to support aoMap.');geometry.addAttribute('uv2',new THREE.BufferAttribute(geometry.attributes.uv.array,2));}\n",
"if(material.isGLTFSpecularGlossinessMaterial){mesh.onBeforeRender=extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].refreshUniforms;}\n",
"mesh.material=material;};GLTFParser.prototype.loadMaterial=function(materialIndex){var parser=this;var json=this.json;var extensions=this.extensions;var materialDef=json.materials[materialIndex];var materialType;var materialParams={};var materialExtensions=materialDef.extensions||{};var pending=[];if(materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]){var sgExtension=extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS];materialType=sgExtension.getMaterialType();pending.push(sgExtension.extendParams(materialParams,materialDef,parser));}else if(materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT]){var kmuExtension=extensions[EXTENSIONS.KHR_MATERIALS_UNLIT];materialType=kmuExtension.getMaterialType();pending.push(kmuExtension.extendParams(materialParams,materialDef,parser));}else{materialType=THREE.MeshStandardMaterial;var metallicRoughness=materialDef.pbrMetallicRoughness||{};materialParams.color=new THREE.Color(1.0,1.0,1.0);materialParams.opacity=1.0;if(Array.isArray(metallicRoughness.baseColorFactor)){var array=metallicRoughness.baseColorFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3];}\n",
"if(metallicRoughness.baseColorTexture!==undefined){pending.push(parser.assignTexture(materialParams,'map',metallicRoughness.baseColorTexture));}\n",
"materialParams.metalness=metallicRoughness.metallicFactor!==undefined?metallicRoughness.metallicFactor:1.0;materialParams.roughness=metallicRoughness.roughnessFactor!==undefined?metallicRoughness.roughnessFactor:1.0;if(metallicRoughness.metallicRoughnessTexture!==undefined){pending.push(parser.assignTexture(materialParams,'metalnessMap',metallicRoughness.metallicRoughnessTexture));pending.push(parser.assignTexture(materialParams,'roughnessMap',metallicRoughness.metallicRoughnessTexture));}}\n",
"if(materialDef.doubleSided===true){materialParams.side=THREE.DoubleSide;}\n",
"var alphaMode=materialDef.alphaMode||ALPHA_MODES.OPAQUE;if(alphaMode===ALPHA_MODES.BLEND){materialParams.transparent=true;}else{materialParams.transparent=false;if(alphaMode===ALPHA_MODES.MASK){materialParams.alphaTest=materialDef.alphaCutoff!==undefined?materialDef.alphaCutoff:0.5;}}\n",
"if(materialDef.normalTexture!==undefined&&materialType!==THREE.MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,'normalMap',materialDef.normalTexture));materialParams.normalScale=new THREE.Vector2(1,1);if(materialDef.normalTexture.scale!==undefined){materialParams.normalScale.set(materialDef.normalTexture.scale,materialDef.normalTexture.scale);}}\n",
"if(materialDef.occlusionTexture!==undefined&&materialType!==THREE.MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,'aoMap',materialDef.occlusionTexture));if(materialDef.occlusionTexture.strength!==undefined){materialParams.aoMapIntensity=materialDef.occlusionTexture.strength;}}\n",
"if(materialDef.emissiveFactor!==undefined&&materialType!==THREE.MeshBasicMaterial){materialParams.emissive=new THREE.Color().fromArray(materialDef.emissiveFactor);}\n",
"if(materialDef.emissiveTexture!==undefined&&materialType!==THREE.MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,'emissiveMap',materialDef.emissiveTexture));}\n",
"return Promise.all(pending).then(function(){var material;if(materialType===THREE.ShaderMaterial){material=extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams);}else{material=new materialType(materialParams);}\n",
"if(materialDef.name!==undefined)material.name=materialDef.name;if(material.map)material.map.encoding=THREE.sRGBEncoding;if(material.emissiveMap)material.emissiveMap.encoding=THREE.sRGBEncoding;if(material.specularMap)material.specularMap.encoding=THREE.sRGBEncoding;assignExtrasToUserData(material,materialDef);if(materialDef.extensions)addUnknownExtensionsToUserData(extensions,material,materialDef);return material;});};function addPrimitiveAttributes(geometry,primitiveDef,parser){var attributes=primitiveDef.attributes;var pending=[];function assignAttributeAccessor(accessorIndex,attributeName){return parser.getDependency('accessor',accessorIndex).then(function(accessor){geometry.addAttribute(attributeName,accessor);});}\n",
"for(var gltfAttributeName in attributes){var threeAttributeName=ATTRIBUTES[gltfAttributeName]||gltfAttributeName.toLowerCase();if(threeAttributeName in geometry.attributes)continue;pending.push(assignAttributeAccessor(attributes[gltfAttributeName],threeAttributeName));}\n",
"if(primitiveDef.indices!==undefined&&!geometry.index){var accessor=parser.getDependency('accessor',primitiveDef.indices).then(function(accessor){geometry.setIndex(accessor);});pending.push(accessor);}\n",
"assignExtrasToUserData(geometry,primitiveDef);return Promise.all(pending).then(function(){return primitiveDef.targets!==undefined?addMorphTargets(geometry,primitiveDef.targets,parser):geometry;});}\n",
"GLTFParser.prototype.loadGeometries=function(primitives){var parser=this;var extensions=this.extensions;var cache=this.primitiveCache;function createDracoPrimitive(primitive){return extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION].decodePrimitive(primitive,parser).then(function(geometry){return addPrimitiveAttributes(geometry,primitive,parser);});}\n",
"var pending=[];for(var i=0,il=primitives.length;i<il;i++){var primitive=primitives[i];var cacheKey=createPrimitiveKey(primitive);var cached=cache[cacheKey];if(cached){pending.push(cached.promise);}else{var geometryPromise;if(primitive.extensions&&primitive.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]){geometryPromise=createDracoPrimitive(primitive);}else{geometryPromise=addPrimitiveAttributes(new THREE.BufferGeometry(),primitive,parser);}\n",
"cache[cacheKey]={primitive:primitive,promise:geometryPromise};pending.push(geometryPromise);}}\n",
"return Promise.all(pending);};GLTFParser.prototype.loadMesh=function(meshIndex){var parser=this;var json=this.json;var meshDef=json.meshes[meshIndex];var primitives=meshDef.primitives;var pending=[];for(var i=0,il=primitives.length;i<il;i++){var material=primitives[i].material===undefined?createDefaultMaterial():this.getDependency('material',primitives[i].material);pending.push(material);}\n",
"return Promise.all(pending).then(function(originalMaterials){return parser.loadGeometries(primitives).then(function(geometries){var meshes=[];for(var i=0,il=geometries.length;i<il;i++){var geometry=geometries[i];var primitive=primitives[i];var mesh;var material=originalMaterials[i];if(primitive.mode===WEBGL_CONSTANTS.TRIANGLES||primitive.mode===WEBGL_CONSTANTS.TRIANGLE_STRIP||primitive.mode===WEBGL_CONSTANTS.TRIANGLE_FAN||primitive.mode===undefined){mesh=meshDef.isSkinnedMesh===true?new THREE.SkinnedMesh(geometry,material):new THREE.Mesh(geometry,material);if(mesh.isSkinnedMesh===true&&!mesh.geometry.attributes.skinWeight.normalized){mesh.normalizeSkinWeights();}\n",
"if(primitive.mode===WEBGL_CONSTANTS.TRIANGLE_STRIP){mesh.drawMode=THREE.TriangleStripDrawMode;}else if(primitive.mode===WEBGL_CONSTANTS.TRIANGLE_FAN){mesh.drawMode=THREE.TriangleFanDrawMode;}}else if(primitive.mode===WEBGL_CONSTANTS.LINES){mesh=new THREE.LineSegments(geometry,material);}else if(primitive.mode===WEBGL_CONSTANTS.LINE_STRIP){mesh=new THREE.Line(geometry,material);}else if(primitive.mode===WEBGL_CONSTANTS.LINE_LOOP){mesh=new THREE.LineLoop(geometry,material);}else if(primitive.mode===WEBGL_CONSTANTS.POINTS){mesh=new THREE.Points(geometry,material);}else{throw new Error('THREE.GLTFLoader: Primitive mode unsupported: '+primitive.mode);}\n",
"if(Object.keys(mesh.geometry.morphAttributes).length>0){updateMorphTargets(mesh,meshDef);}\n",
"mesh.name=meshDef.name||('mesh_'+meshIndex);if(geometries.length>1)mesh.name+='_'+i;assignExtrasToUserData(mesh,meshDef);parser.assignFinalMaterial(mesh);meshes.push(mesh);}\n",
"if(meshes.length===1){return meshes[0];}\n",
"var group=new THREE.Group();for(var i=0,il=meshes.length;i<il;i++){group.add(meshes[i]);}\n",
"return group;});});};GLTFParser.prototype.loadCamera=function(cameraIndex){var camera;var cameraDef=this.json.cameras[cameraIndex];var params=cameraDef[cameraDef.type];if(!params){console.warn('THREE.GLTFLoader: Missing camera parameters.');return;}\n",
"if(cameraDef.type==='perspective'){camera=new THREE.PerspectiveCamera(THREE.Math.radToDeg(params.yfov),params.aspectRatio||1,params.znear||1,params.zfar||2e6);}else if(cameraDef.type==='orthographic'){camera=new THREE.OrthographicCamera(params.xmag/-2,params.xmag/2,params.ymag/2,params.ymag/-2,params.znear,params.zfar);}\n",
"if(cameraDef.name!==undefined)camera.name=cameraDef.name;assignExtrasToUserData(camera,cameraDef);return Promise.resolve(camera);};GLTFParser.prototype.loadSkin=function(skinIndex){var skinDef=this.json.skins[skinIndex];var skinEntry={joints:skinDef.joints};if(skinDef.inverseBindMatrices===undefined){return Promise.resolve(skinEntry);}\n",
"return this.getDependency('accessor',skinDef.inverseBindMatrices).then(function(accessor){skinEntry.inverseBindMatrices=accessor;return skinEntry;});};GLTFParser.prototype.loadAnimation=function(animationIndex){var json=this.json;var animationDef=json.animations[animationIndex];var pendingNodes=[];var pendingInputAccessors=[];var pendingOutputAccessors=[];var pendingSamplers=[];var pendingTargets=[];for(var i=0,il=animationDef.channels.length;i<il;i++){var channel=animationDef.channels[i];var sampler=animationDef.samplers[channel.sampler];var target=channel.target;var name=target.node!==undefined?target.node:target.id;var input=animationDef.parameters!==undefined?animationDef.parameters[sampler.input]:sampler.input;var output=animationDef.parameters!==undefined?animationDef.parameters[sampler.output]:sampler.output;pendingNodes.push(this.getDependency('node',name));pendingInputAccessors.push(this.getDependency('accessor',input));pendingOutputAccessors.push(this.getDependency('accessor',output));pendingSamplers.push(sampler);pendingTargets.push(target);}\n",
"return Promise.all([Promise.all(pendingNodes),Promise.all(pendingInputAccessors),Promise.all(pendingOutputAccessors),Promise.all(pendingSamplers),Promise.all(pendingTargets)]).then(function(dependencies){var nodes=dependencies[0];var inputAccessors=dependencies[1];var outputAccessors=dependencies[2];var samplers=dependencies[3];var targets=dependencies[4];var tracks=[];for(var i=0,il=nodes.length;i<il;i++){var node=nodes[i];var inputAccessor=inputAccessors[i];var outputAccessor=outputAccessors[i];var sampler=samplers[i];var target=targets[i];if(node===undefined)continue;node.updateMatrix();node.matrixAutoUpdate=true;var TypedKeyframeTrack;switch(PATH_PROPERTIES[target.path]){case PATH_PROPERTIES.weights:TypedKeyframeTrack=THREE.NumberKeyframeTrack;break;case PATH_PROPERTIES.rotation:TypedKeyframeTrack=THREE.QuaternionKeyframeTrack;break;case PATH_PROPERTIES.position:case PATH_PROPERTIES.scale:default:TypedKeyframeTrack=THREE.VectorKeyframeTrack;break;}\n",
"var targetName=node.name?node.name:node.uuid;var interpolation=sampler.interpolation!==undefined?INTERPOLATION[sampler.interpolation]:THREE.InterpolateLinear;var targetNames=[];if(PATH_PROPERTIES[target.path]===PATH_PROPERTIES.weights){node.traverse(function(object){if(object.isMesh===true&&object.morphTargetInfluences){targetNames.push(object.name?object.name:object.uuid);}});}else{targetNames.push(targetName);}\n",
"var outputArray=outputAccessor.array;if(outputAccessor.normalized){var scale;if(outputArray.constructor===Int8Array){scale=1/127;}else if(outputArray.constructor===Uint8Array){scale=1/255;}else if(outputArray.constructor==Int16Array){scale=1/32767;}else if(outputArray.constructor===Uint16Array){scale=1/65535;}else{throw new Error('THREE.GLTFLoader: Unsupported output accessor component type.');}\n",
"var scaled=new Float32Array(outputArray.length);for(var j=0,jl=outputArray.length;j<jl;j++){scaled[j]=outputArray[j]*scale;}\n",
"outputArray=scaled;}\n",
"for(var j=0,jl=targetNames.length;j<jl;j++){var track=new TypedKeyframeTrack(targetNames[j]+'.'+PATH_PROPERTIES[target.path],inputAccessor.array,outputArray,interpolation);if(sampler.interpolation==='CUBICSPLINE'){track.createInterpolant=function InterpolantFactoryMethodGLTFCubicSpline(result){return new GLTFCubicSplineInterpolant(this.times,this.values,this.getValueSize()/3,result);};track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline=true;}\n",
"tracks.push(track);}}\n",
"var name=animationDef.name!==undefined?animationDef.name:'animation_'+animationIndex;return new THREE.AnimationClip(name,undefined,tracks);});};GLTFParser.prototype.loadNode=function(nodeIndex){var json=this.json;var extensions=this.extensions;var parser=this;var meshReferences=json.meshReferences;var meshUses=json.meshUses;var nodeDef=json.nodes[nodeIndex];return(function(){if(nodeDef.isBone===true){return Promise.resolve(new THREE.Bone());}else if(nodeDef.mesh!==undefined){return parser.getDependency('mesh',nodeDef.mesh).then(function(mesh){var node;if(meshReferences[nodeDef.mesh]>1){var instanceNum=meshUses[nodeDef.mesh]++;node=mesh.clone();node.name+='_instance_'+instanceNum;node.onBeforeRender=mesh.onBeforeRender;for(var i=0,il=node.children.length;i<il;i++){node.children[i].name+='_instance_'+instanceNum;node.children[i].onBeforeRender=mesh.children[i].onBeforeRender;}}else{node=mesh;}\n",
"if(nodeDef.weights!==undefined){node.traverse(function(o){if(!o.isMesh)return;for(var i=0,il=nodeDef.weights.length;i<il;i++){o.morphTargetInfluences[i]=nodeDef.weights[i];}});}\n",
"return node;});}else if(nodeDef.camera!==undefined){return parser.getDependency('camera',nodeDef.camera);}else if(nodeDef.extensions&&nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL]&&nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light!==undefined){return parser.getDependency('light',nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light);}else{return Promise.resolve(new THREE.Object3D());}}()).then(function(node){if(nodeDef.name!==undefined){node.userData.name=nodeDef.name;node.name=THREE.PropertyBinding.sanitizeNodeName(nodeDef.name);}\n",
"assignExtrasToUserData(node,nodeDef);if(nodeDef.extensions)addUnknownExtensionsToUserData(extensions,node,nodeDef);if(nodeDef.matrix!==undefined){var matrix=new THREE.Matrix4();matrix.fromArray(nodeDef.matrix);node.applyMatrix(matrix);}else{if(nodeDef.translation!==undefined){node.position.fromArray(nodeDef.translation);}\n",
"if(nodeDef.rotation!==undefined){node.quaternion.fromArray(nodeDef.rotation);}\n",
"if(nodeDef.scale!==undefined){node.scale.fromArray(nodeDef.scale);}}\n",
"return node;});};GLTFParser.prototype.loadScene=function(){function buildNodeHierachy(nodeId,parentObject,json,parser){var nodeDef=json.nodes[nodeId];return parser.getDependency('node',nodeId).then(function(node){if(nodeDef.skin===undefined)return node;var skinEntry;return parser.getDependency('skin',nodeDef.skin).then(function(skin){skinEntry=skin;var pendingJoints=[];for(var i=0,il=skinEntry.joints.length;i<il;i++){pendingJoints.push(parser.getDependency('node',skinEntry.joints[i]));}\n",
"return Promise.all(pendingJoints);}).then(function(jointNodes){var meshes=node.isGroup===true?node.children:[node];for(var i=0,il=meshes.length;i<il;i++){var mesh=meshes[i];var bones=[];var boneInverses=[];for(var j=0,jl=jointNodes.length;j<jl;j++){var jointNode=jointNodes[j];if(jointNode){bones.push(jointNode);var mat=new THREE.Matrix4();if(skinEntry.inverseBindMatrices!==undefined){mat.fromArray(skinEntry.inverseBindMatrices.array,j*16);}\n",
"boneInverses.push(mat);}else{console.warn('THREE.GLTFLoader: Joint &quot;%s&quot; could not be found.',skinEntry.joints[j]);}}\n",
"mesh.bind(new THREE.Skeleton(bones,boneInverses),mesh.matrixWorld);}\n",
"return node;});}).then(function(node){parentObject.add(node);var pending=[];if(nodeDef.children){var children=nodeDef.children;for(var i=0,il=children.length;i<il;i++){var child=children[i];pending.push(buildNodeHierachy(child,node,json,parser));}}\n",
"return Promise.all(pending);});}\n",
"return function loadScene(sceneIndex){var json=this.json;var extensions=this.extensions;var sceneDef=this.json.scenes[sceneIndex];var parser=this;var scene=new THREE.Scene();if(sceneDef.name!==undefined)scene.name=sceneDef.name;assignExtrasToUserData(scene,sceneDef);if(sceneDef.extensions)addUnknownExtensionsToUserData(extensions,scene,sceneDef);var nodeIds=sceneDef.nodes||[];var pending=[];for(var i=0,il=nodeIds.length;i<il;i++){pending.push(buildNodeHierachy(nodeIds[i],scene,json,parser));}\n",
"return Promise.all(pending).then(function(){return scene;});};}();return GLTFLoader;})();var camera,controls,scene,renderer,tracklight;function autoFit(obj,camera,controls){const boundingBox=new THREE.Box3().setFromObject(obj);const boundingSphere=new THREE.Sphere();boundingBox.getBoundingSphere((target=boundingSphere));const scale=1.0;const angularSize=camera.fov*Math.PI/180*scale;const distanceToCamera=boundingSphere.radius/Math.tan(angularSize);const len=Math.sqrt(Math.pow(distanceToCamera,2)+\n",
"Math.pow(distanceToCamera,2)+\n",
"Math.pow(distanceToCamera,2));camera.position.set(len,len,len);controls.update();camera.lookAt(boundingSphere.center);controls.target.set(boundingSphere.center.x,boundingSphere.center.y,boundingSphere.center.z);camera.updateProjectionMatrix();}\n",
"function centerControls(obj,camera,controls){const boundingBox=new THREE.Box3().setFromObject(obj);const boundingSphere=new THREE.Sphere();boundingBox.getBoundingSphere((target=boundingSphere));controls.update();controls.target.set(boundingSphere.center.x,boundingSphere.center.y,boundingSphere.center.z);}\n",
"function init(){scene=new THREE.Scene();scene.background=new THREE.Color(0xffffff);tracklight=new THREE.DirectionalLight(0xffffff,1.75);scene.add(tracklight);base64_data=&quot;Z2xURgIAAAAkBgAAmAMAAEpTT057InNjZW5lIjogMCwgInNjZW5lcyI6IFt7Im5vZGVzIjogWzBdfV0sICJhc3NldCI6IHsidmVyc2lvbiI6ICIyLjAiLCAiZ2VuZXJhdG9yIjogImh0dHBzOi8vZ2l0aHViLmNvbS9taWtlZGgvdHJpbWVzaCJ9LCAiYWNjZXNzb3JzIjogW3siYnVmZmVyVmlldyI6IDAsICJjb21wb25lbnRUeXBlIjogNTEyNSwgImNvdW50IjogOTYsICJtYXgiOiBbMTldLCAibWluIjogWzBdLCAidHlwZSI6ICJTQ0FMQVIifSwgeyJidWZmZXJWaWV3IjogMSwgImNvbXBvbmVudFR5cGUiOiA1MTI2LCAiY291bnQiOiAyMCwgInR5cGUiOiAiVkVDMyIsICJieXRlT2Zmc2V0IjogMCwgIm1heCI6IFswLjUsIDEuMDk2MDg2NTAyMDc1MTk1MywgMC41XSwgIm1pbiI6IFstMC41LCAtMC41LCAtMC41XX1dLCAibWVzaGVzIjogW3sibmFtZSI6ICJ0cmltZXNoX2ZhaWwuc3RsIiwgInByaW1pdGl2ZXMiOiBbeyJhdHRyaWJ1dGVzIjogeyJQT1NJVElPTiI6IDF9LCAiaW5kaWNlcyI6IDAsICJtb2RlIjogNH1dfV0sICJjYW1lcmFzIjogW3sibmFtZSI6ICJjYW1lcmFfVU9TUVZaIiwgInR5cGUiOiAicGVyc3BlY3RpdmUiLCAicGVyc3BlY3RpdmUiOiB7ImFzcGVjdFJhdGlvIjogMS4zMzMzMzMzMzMzMzMzMzMzLCAieWZvdiI6IDAuNzg1Mzk4MTYzMzk3NDQ4MywgInpuZWFyIjogMC4wMX19XSwgIm5vZGVzIjogW3sibmFtZSI6ICJ3b3JsZCIsICJjaGlsZHJlbiI6IFsxXX0sIHsibmFtZSI6ICJ0cmltZXNoX2ZhaWwuc3RsX1RUVVJPUklSWFJFUCIsICJtZXNoIjogMH1dLCAiYnVmZmVycyI6IFt7ImJ5dGVMZW5ndGgiOiA2MjR9XSwgImJ1ZmZlclZpZXdzIjogW3siYnVmZmVyIjogMCwgImJ5dGVPZmZzZXQiOiAwLCAiYnl0ZUxlbmd0aCI6IDM4NH0sIHsiYnVmZmVyIjogMCwgImJ5dGVPZmZzZXQiOiAzODQsICJieXRlTGVuZ3RoIjogMjQwfV19ICAgIHACAABCSU4ABwAAAA4AAAALAAAABwAAAAkAAAAOAAAACwAAAA8AAAAMAAAACwAAAA4AAAAPAAAADAAAAAoAAAAIAAAADAAAAA8AAAAKAAAACAAAAAkAAAAHAAAACAAAAAoAAAAJAAAAAAAAABMAAAANAAAAAAAAAAUAAAATAAAADQAAAAMAAAAPAAAADQAAABMAAAADAAAADwAAABEAAAACAAAADwAAAAMAAAARAAAAAgAAAAUAAAAAAAAAAgAAABEAAAAFAAAABQAAAAMAAAATAAAABQAAABEAAAADAAAAAAAAAA0AAAAPAAAAAAAAAA8AAAACAAAAAAAAABAAAAABAAAAAAAAAAYAAAAQAAAAAQAAAAQAAAAOAAAAAQAAABAAAAAEAAAADgAAABIAAAANAAAADgAAAAQAAAASAAAADQAAAAYAAAAAAAAADQAAABIAAAAGAAAABgAAAAQAAAAQAAAABgAAABIAAAAEAAAAAAAAAAEAAAAOAAAAAAAAAA4AAAANAAAAAAAAAAAAAD8AAAAAAAAAJQAAAD8AAAC/AAAAAAAAAD8AAAA/NYbkPpBMjD81huQ+NYbkPpBMjD81huS+dnVMvXIKfz92dUy9dnVMvXIKfz92dUw9AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAL8AAAC/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAAAAAAAPwAAAD8AAAC/AAAAPwAAAD8AAAA/uLBWveXohT+dzeW+uLBWveXohT+dzeU+nc3lPuXohT+4sFY9nc3lPuXohT+4sFa9&quot;;;renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);loader=new THREE.GLTFLoader();loader.load(&quot;data:text/plain;base64,&quot;+base64_data,function(gltf){scene.add(gltf.scene);camera=gltf.cameras[0];controls=new THREE.TrackballControls(camera);controls.rotateSpeed=1.0;controls.zoomSpeed=1.2;controls.panSpeed=0.8;controls.noZoom=false;controls.noPan=false;controls.staticMoving=true;controls.dynamicDampingFactor=0.3;controls.keys=[65,83,68];controls.addEventListener(&quot;change&quot;,render);centerControls(scene,camera,controls);render();window.addEventListener(&quot;resize&quot;,onWindowResize,false);animate();});}\n",
"function onWindowResize(){camera.aspect=window.innerWidth/window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth,window.innerHeight);controls.handleResize();render();}\n",
"function animate(){requestAnimationFrame(animate);controls.update();}\n",
"function render(){tracklight.position.copy(camera.position);renderer.render(scene,camera);}\n",
"init();</script></body>\n",
"</html>\" width=\"100%\" height=\"500px\" style=\"border:none;\"></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import trimesh\n",
"m = trimesh.load_mesh(\"trimesh_fail.stl\")\n",
"m.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

Binary file not shown.