You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
self.cant_start, self.cant_end, self.skew_start,
self.skew_end, self.mirror)
def_compute_spline_gradient_coeffs(self):
""" Computes the hermite spline coefficients for this spline from the gradient and delta height properties. :return: the coefficients c, a, and b representing the z^3, z^2, and z terms respectively """b=self.start_grad/100ifself.use_delta_height:
c= (self.end_grad-self.start_grad)/100*self.length-2* (self.delta_height-self.start_grad/100*self.length)
c/=self.length*self.length*self.lengtha=-(self.end_grad-self.start_grad)/100*self.length+3* (self.delta_height-self.start_grad/100*self.length)
a/=self.length*self.lengthelse:
c=0a= (self.end_grad-self.start_grad)/100/(2*self.length)
returnc, a, bdefgenerate_tesselation_points(self, spline_tess_dist, spline_curve_sag):
""" Generates a set of z coordinates along the spline which represents an "ideal" sampling of the spline. :param spline_tess_dist: :param spline_curve_sag: :return: """""" This is a non-trivial problem to solve, and hence the answer is somewhat approximate here. Our spline is essentially a hermite curve along the y axis and an arc along the xz plane. For ease of implementation we consider the sampling of these separately and then merge and weld the samplings together. The Y axis can be described by the following function: (see https://www.desmos.com/calculator/7xfjwmqtpz for my calculator) f_y(z, x) = z^3*c + z^2*a + z*b + x*(z*cant_delta + cant_0) {0 <= z <= length} To approximate the curvature of the spline we take the arctangent of the first derivative (essentially converting the gradient of the function into an angle): f_dy(z, x) = 3z^2*c + 2z*a + b + x*cant_delta {0 <= z <= length} f_curvature(z, x) = abs(atan(f_dy(z, x))) * (1 + 1/r) We want to sample this at regular horizontal slices of the curvature equation (ie the higher the gradient of the curvature equation the more samples to generate), we do this by taking the inverse of the curvature equation and sampling it at regular intervals along the x axis. This is represented by the following expression: f_icurvature(z, x) = (-a +- sqrt(a^2 - 3c(b + x*cant_delta) +- 3c*tan(z)) / 3c {0 <= z <= pi/2} Because of the two +- operations in the above equation, we can actually get up to four points per iteration. """samples= []
n_grad_samples=0ifself.start_grad!=self.end_gradorself.use_delta_height:
c, a, b=self._compute_spline_gradient_coeffs()
ifa!=0orc!=0:
cant_delta= (self.cant_end-self.cant_start) /self.length/100# Maybe we should consider the value of x?x=0_3c=c*3ifself.use_delta_height:
a23cbxc=a*a-_3c*(b+x*cant_delta)
else:
bxc2a= (-b-x*cant_delta)/(2*a)
z=0if (math.pi/2) /spline_curve_sag>10000:
log("[ERROR] Spline tesselation would take too long, please increase the spline curve sag distance!")
# Return a basic tesselation...n_segments=min(max(math.ceil(self.length/spline_tess_dist), 1), 100)
dx=self.length/n_segmentsreturn [i*dxforiinrange(n_segments+1)]
whilez<=math.pi/2:
ifself.use_delta_height:
_3ctanz=_3c*math.tan(z)
ic00, ic01, ic10, ic11=-1, -1, -1, -1ifa23cbxc+_3ctanz>=0:
ic00= (-a+math.sqrt(a23cbxc+_3ctanz))/_3cic01= (-a-math.sqrt(a23cbxc+_3ctanz))/_3cifa23cbxc-_3ctanz>=0:
ic10= (-a+math.sqrt(a23cbxc-_3ctanz))/_3cic11= (-a-math.sqrt(a23cbxc-_3ctanz))/_3cif0<=ic00<=self.length:
samples.append(ic00)
if0<=ic01<=self.length:
samples.append(ic01)
if0<=ic10<=self.length:
samples.append(ic10)
if0<=ic11<=self.length:
samples.append(ic11)
else:
# f_icurvature2(z, x) = (-b -x*c_d)/(2a) +- (tan z)/(2a)t2a=math.tan(z)/(2*a)
ic20=bxc2a+t2aic21=bxc2a-t2aif0<=ic20<=self.length:
samples.append(ic20)
if0<=ic21<=self.length:
samples.append(ic21)
z+=spline_curve_sag*10n_grad_samples=len(samples)
# Now append the samples from the arc on the xz plane (spline radius)radius=abs(self.radius)
dr=float("inf")
ifradius>0:
revs=min(self.length/radius, math.pi*2)
# Arc distance based angle incrementdr_a=spline_tess_dist/radius# Sag distance based angle increment, clamped to 0.06deg < x < 90degdr_b=2*math.acos(1-clamp(spline_curve_sag/radius, 0.001, 0.294))
dr=min(dr_a, dr_b)
n_segments=max(math.ceil(revs/dr), 1)
dr=revs/n_segmentssamples.extend([i*dr*radiusforiinrange(n_segments+1)])
else:
# Now append the samples from the constant tesselation factor# Note that the arc segment already include the constant tesselation factorn_segments=max(math.ceil(self.length/spline_tess_dist), 1)
dx=self.length/n_segmentssamples.extend([i*dxforiinrange(n_segments+1)])
# Now weld the samples together based on a heuristiciflen(samples) >1:
samples.sort()
# This tuple stores the sample position, and it's weight (used for averaging); when a sample is consumed its# weight is set to 0 and the sample it's merged with has its weight incrementedsamples_weighted= [(s, 1) forsinsamples]
last_s=0# log(f"spline-{self.id}\tmerge_dist: grad={(self.length/max(float(n_grad_samples), 0.1)/3):3f} "# f"const={spline_tess_dist:3f} arc={dr*max(radius, 0.01)/2:3f} length={self.length*0.9:3f}")merge_dist=min((self.length/max(float(n_grad_samples), 0.1)/3),
spline_tess_dist,
dr*max(radius, 0.01)/2,
self.length*0.9)
foriinrange(1, len(samples_weighted)-1):
d=samples_weighted[i][0] -samples_weighted[last_s][0]
ifd<merge_dist:
sl=samples_weighted[last_s]
samples_weighted[last_s] = ((sl[0]*sl[1]+samples_weighted[i][0]) / (sl[1]+1), sl[1]+1)
samples_weighted[i] = (0, 0)
else:
last_s=isamples= [s[0] forsinsamples_weightedifs[1] >0]
# Make sure the start and end points are fixedsamples[0] =0samples[-1] =self.lengthreturnsamplesdefevaluate_spline(self, pos_offset, apply_rot=False, world_space=False):
""" Computes a position along a spline given offset coordinates. :param world_space: whether the position should be relative to the tile or relative to the origin of the spline :param apply_rot: whether the spline segment's rotation should also be applied to the spline :param pos_offset: position offset along the spline; y is forward along the spline, x is across the width of the spline :return: (the computed position, the computed rotation) """# Split the split evaluation into separate gradient and radius steps# Gradient# Evaluate: f_z(y, x) = y^3*c + y^2*a + y*b + x*(y*cant_delta + cant_0) {0 <= y <= length}# The rotation is given by:# f_rx(y, x) = atan(3y^2*c + 2y*a + b + x*cant_delta) {0 <= y <= length}ox, oy, oz=pos_offsetcant_delta= (self.cant_end-self.cant_start)/100/self.lengthc, a, b=self._compute_spline_gradient_coeffs()
pz=oy*oy*oy*c+oy*oy*a+oy*brx=math.atan(3*oy*oy*c+2*oy*a+b+ox*(oy*cant_delta+self.cant_start/100))
pz+=oz# Cantry=math.atan(oy*cant_delta+self.cant_start/100)
# TODO: The x coordinate should be clamped to the width of the splinepz+=-ox*(oy*cant_delta+self.cant_start/100)
# Radiusifabs(self.radius) >0:
rz=oy/self.radiusk=ox-self.radiuspx=k*math.cos(rz) +self.radiuspy=-k*math.sin(rz)
rz=-rzelse:
rz=0px=oxpy=oy# World spacepos=Vector((px, py, pz))
rot=Vector((rx, ry, rz))
ifapply_rotorworld_space:
ifbpy.app.version< (2, 80):
pos=pos*mathutils.Matrix.Rotation(math.radians(self.rot), 4, "Z")
else:
pos=pos @ mathutils.Matrix.Rotation(math.radians(self.rot), 4, "Z")
rot.z+=math.radians(-self.rot)
ifworld_space:
pos+=Vector(self.pos).xzyreturnpos, rotdefgenerate_mesh(self, sli_cache, spline_tess_dist, spline_curve_sag, apply_xform=False):
""" Generates a Blender mesh for this spline. :param sli_cache: the dictionary of spline profile definitions :param spline_tess_dist: the distance between tesselation segments :param spline_curve_sag: the maximum amount of curve sag for the tessellated segments :param apply_xform: :return: a reference to the Blender mesh for this spline """sli=sli_cache[self.sli_path]
verts= []
uvs= []
The text was updated successfully, but these errors were encountered:
https://api.github.com/space928/Blender-O3D-IO-Public/blob/734b4df774329c68085f978beb7d29e123b98b88/o3d_io/io_omsi_spline.py#L238
The text was updated successfully, but these errors were encountered: