From 154223d075164817940dc21c8e61555d3f563985 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 15 Dec 2022 17:43:24 +0100 Subject: [PATCH 1/3] motiontrail: Clean up and document MotionTrail class --- direct/src/motiontrail/MotionTrail.py | 715 ++++++++++++++------------ 1 file changed, 388 insertions(+), 327 deletions(-) diff --git a/direct/src/motiontrail/MotionTrail.py b/direct/src/motiontrail/MotionTrail.py index db891548ac..49a65d8703 100644 --- a/direct/src/motiontrail/MotionTrail.py +++ b/direct/src/motiontrail/MotionTrail.py @@ -1,63 +1,92 @@ - - from panda3d.core import * from panda3d.direct import * from direct.task import Task +from direct.task.TaskManagerGlobal import taskMgr from direct.showbase.DirectObject import DirectObject from direct.directnotify.DirectNotifyGlobal import directNotify -def remove_task ( ): - if (MotionTrail.task_added): - total_motion_trails = len (MotionTrail.motion_trail_list) +_want_python_motion_trails = ConfigVariableBool('want-python-motion-trails', False) + + +def remove_task(): + if MotionTrail.task_added: + total_motion_trails = len(MotionTrail.motion_trail_list) if total_motion_trails > 0: print("warning: %d motion trails still exist when motion trail task is removed" % (total_motion_trails)) - MotionTrail.motion_trail_list = [ ] + MotionTrail.motion_trail_list = [] - taskMgr.remove (MotionTrail.motion_trail_task_name) + taskMgr.remove(MotionTrail.motion_trail_task_name) print("MotionTrail task removed") MotionTrail.task_added = False - return + class MotionTrailVertex: def __init__(self, vertex_id, vertex_function, context): self.vertex_id = vertex_id self.vertex_function = vertex_function self.context = context - self.vertex = Vec4 (0.0, 0.0, 0.0, 1.0) + self.vertex = Vec4(0.0, 0.0, 0.0, 1.0) # default - self.start_color = Vec4 (1.0, 1.0, 1.0, 1.0) - self.end_color = Vec4 (0.0, 0.0, 0.0, 1.0) + self.start_color = Vec4(1.0, 1.0, 1.0, 1.0) + self.end_color = Vec4(0.0, 0.0, 0.0, 1.0) self.v = 0.0 + class MotionTrailFrame: - def __init__ (self, current_time, transform): + def __init__(self, current_time, transform): self.time = current_time self.transform = transform -class MotionTrail(NodePath, DirectObject): - notify = directNotify.newCategory ("MotionTrail") +class MotionTrail(NodePath, DirectObject): + """Generates smooth geometry-based motion trails behind a moving object. + + To use this class, first define the shape of the cross-section of the trail + by repeatedly calling `add_vertex()` and `set_vertex_color()`. + When this is done, `update_vertices()` must be called. + + To generate the motion trail, either call `register_motion_trail()` + to have Panda update it automatically, or periodically call the method + `update_motion_trail()` with the current time and the new transform. + + The duration of the sample history is specified by `time_window`. A larger + time window creates longer motion trails (given constant speed). Samples + that are no longer within the time window are automatically discarded. + + The `use_nurbs` option can be used to create smooth interpolated curves + from the samples. This option is useful for animations that lack sampling + to begin with, animations that move very quickly, or low frame rates, or if + `sampling_time` is used to artificially slow down the update frequency. + + By default, the optimized C++ implementation (provided by `.CMotionTrail`) + is used to generate the motion trails. If for some reason you want to use + the pure-Python implementation instead, set `want-python-motion-trails` to + true in Config.prc. + """ + + notify = directNotify.newCategory("MotionTrail") task_added = False - motion_trail_list = [ ] + motion_trail_list = [] motion_trail_task_name = "motion_trail_task" global_enable = True @classmethod - def setGlobalEnable (self, enable): - MotionTrail.global_enable = enable + def setGlobalEnable(cls, enable): + cls.global_enable = enable - def __init__ (self,name,parent_node_path): - - DirectObject.__init__(self) - NodePath.__init__ (self,name) + def __init__(self, name, parent_node_path): + """Creates the motion trail with the given name and parents it to the + given root node. + """ + NodePath.__init__(self, name) # required initialization self.active = True @@ -74,125 +103,138 @@ class MotionTrail(NodePath, DirectObject): self.total_vertices = 0 self.last_update_time = 0.0 self.texture = None - self.vertex_list = [ ] - self.frame_list = [ ] + self.vertex_list = [] + self.frame_list = [] self.parent_node_path = parent_node_path self.previous_matrix = None self.calculate_relative_matrix = False - self.playing = False; + self.playing = False # default options self.continuous_motion_trail = True self.color_scale = 1.0 + + ## How long the time window is for which the trail is computed. Can be + ## increased to obtain a longer trail, decreased for a shorter trail. self.time_window = 1.0 + + ## How often the trail updates, in seconds. The default is 0.0, which + ## has the trail updated every frame for the smoothest result. Higher + ## values will generate a choppier trail. The `use_nurbs` option can + ## compensate partially for this choppiness, however. self.sampling_time = 0.0 + self.square_t = True # self.task_transform = False self.root_node_path = None # node path states - self.reparentTo (parent_node_path) - self.geom_node = GeomNode ("motion_trail") + self.reparentTo(parent_node_path) + + ## A `.GeomNode` object containing the generated geometry. By default + ## parented to the MotionTrail itself, but can be reparented elsewhere + ## if necessary. + self.geom_node = GeomNode("motion_trail") self.geom_node_path = self.attachNewNode(self.geom_node) node_path = self.geom_node_path ### set render states - node_path.setTwoSided (True) + node_path.setTwoSided(True) # set additive blend effects - node_path.setTransparency (True) - node_path.setDepthWrite (False) - node_path.node ( ).setAttrib (ColorBlendAttrib.make (ColorBlendAttrib.MAdd)) + node_path.setTransparency(True) + node_path.setDepthWrite(False) + node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) # do not light - node_path.setLightOff ( ) + node_path.setLightOff() # disable writes to destination alpha, write out rgb colors only - node_path.setAttrib (ColorWriteAttrib.make (ColorWriteAttrib.CRed | ColorWriteAttrib.CGreen | ColorWriteAttrib.CBlue)); + node_path.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.CRed | ColorWriteAttrib.CGreen | ColorWriteAttrib.CBlue)) - if (MotionTrail.task_added == False): -# taskMgr.add (self.motion_trail_task, "motion_trail_task", priority = 50) - taskMgr.add (self.motion_trail_task, MotionTrail.motion_trail_task_name) + if not MotionTrail.task_added: + #taskMgr.add(self.motion_trail_task, "motion_trail_task", priority = 50) + taskMgr.add(self.motion_trail_task, MotionTrail.motion_trail_task_name) - self.acceptOnce ("clientLogout", remove_task) + self.acceptOnce("clientLogout", remove_task) MotionTrail.task_added = True - self.relative_to_render = False + ## Set this to True to use a NURBS curve to generate a smooth trail, + ## even if the underlying animation or movement is janky. self.use_nurbs = False self.resolution_distance = 0.5 - self.cmotion_trail = CMotionTrail ( ) - self.cmotion_trail.setGeomNode (self.geom_node) + self.cmotion_trail = CMotionTrail() + self.cmotion_trail.setGeomNode(self.geom_node) self.modified_vertices = True - if base.config.GetBool('want-python-motion-trails', 0): + if _want_python_motion_trails: self.use_python_version = True else: self.use_python_version = False - return - def delete(self): + """Completely cleans up the motion trail object. + """ self.reset_motion_trail() self.reset_motion_trail_geometry() - self.cmotion_trail.resetVertexList ( ) + self.cmotion_trail.resetVertexList() self.removeNode() - return - def print_matrix (self, matrix): + def print_matrix(self, matrix): separator = ' ' - print(matrix.getCell (0, 0), separator, matrix.getCell (0, 1), separator, matrix.getCell (0, 2), separator, matrix.getCell (0, 3)) - print(matrix.getCell (1, 0), separator, matrix.getCell (1, 1), separator, matrix.getCell (1, 2), separator, matrix.getCell (1, 3)) - print(matrix.getCell (2, 0), separator, matrix.getCell (2, 1), separator, matrix.getCell (2, 2), separator, matrix.getCell (2, 3)) - print(matrix.getCell (3, 0), separator, matrix.getCell (3, 1), separator, matrix.getCell (3, 2), separator, matrix.getCell (3, 3)) + print(matrix.getCell(0, 0), separator, matrix.getCell(0, 1), separator, matrix.getCell(0, 2), separator, matrix.getCell(0, 3)) + print(matrix.getCell(1, 0), separator, matrix.getCell(1, 1), separator, matrix.getCell(1, 2), separator, matrix.getCell(1, 3)) + print(matrix.getCell(2, 0), separator, matrix.getCell(2, 1), separator, matrix.getCell(2, 2), separator, matrix.getCell(2, 3)) + print(matrix.getCell(3, 0), separator, matrix.getCell(3, 1), separator, matrix.getCell(3, 2), separator, matrix.getCell(3, 3)) - def motion_trail_task (self, task): + def motion_trail_task(self, task): current_time = task.time - total_motion_trails = len (MotionTrail.motion_trail_list) + total_motion_trails = len(MotionTrail.motion_trail_list) index = 0 - while (index < total_motion_trails): + while index < total_motion_trails: motion_trail = MotionTrail.motion_trail_list [index] - if (MotionTrail.global_enable): - if (motion_trail.use_python_version): + if MotionTrail.global_enable: + if motion_trail.use_python_version: # Python version - if (motion_trail.active and motion_trail.check_for_update (current_time)): + if motion_trail.active and motion_trail.check_for_update(current_time): transform = None - if (motion_trail.root_node_path != None) and (motion_trail.root_node_path != render): - motion_trail.root_node_path.update ( ) + if motion_trail.root_node_path is not None and motion_trail.root_node_path != render: + motion_trail.root_node_path.update() - if (motion_trail.root_node_path and (motion_trail.relative_to_render == False)): + if motion_trail.root_node_path and not motion_trail.relative_to_render: transform = motion_trail.getMat(motion_trail.root_node_path) else: - transform = Mat4 (motion_trail.getNetTransform ( ).getMat ( )) + transform = Mat4(motion_trail.getNetTransform().getMat()) - if (transform != None): - motion_trail.update_motion_trail (current_time, transform) + if transform is not None: + motion_trail.update_motion_trail(current_time, transform) else: # C++ version - if (motion_trail.active and motion_trail.cmotion_trail.checkForUpdate (current_time)): + if motion_trail.active and motion_trail.cmotion_trail.checkForUpdate(current_time): transform = None - if (motion_trail.root_node_path != None) and (motion_trail.root_node_path != render): - motion_trail.root_node_path.update ( ) + if motion_trail.root_node_path is not None and motion_trail.root_node_path != render: + motion_trail.root_node_path.update() - if (motion_trail.root_node_path and (motion_trail.relative_to_render == False)): + if motion_trail.root_node_path and not motion_trail.relative_to_render: transform = motion_trail.getMat(motion_trail.root_node_path) else: - transform = Mat4 (motion_trail.getNetTransform ( ).getMat ( )) + transform = Mat4(motion_trail.getNetTransform().getMat()) - if (transform != None): - motion_trail.transferVertices ( ) - motion_trail.cmotion_trail.updateMotionTrail (current_time, transform) + if transform is not None: + motion_trail.transferVertices() + motion_trail.cmotion_trail.updateMotionTrail(current_time, transform) else: motion_trail.reset_motion_trail() @@ -202,51 +244,71 @@ class MotionTrail(NodePath, DirectObject): return Task.cont - def add_vertex (self, vertex_id, vertex_function, context): + def add_vertex(self, vertex_id, vertex_function, context): + """This must be called repeatedly to define the polygon that forms the + cross-section of the generated motion trail geometry. The first + argument is a user-defined vertex identifier, the second is a function + that will be called with three parameters that should return the + position of the vertex as a `.Vec4` object, and the third is an + arbitrary context object that is passed as last argument to the + provided function. - motion_trail_vertex = MotionTrailVertex (vertex_id, vertex_function, context) - total_vertices = len (self.vertex_list) + After calling this, you must call `update_vertices()` before the + changes will fully take effect. + """ + motion_trail_vertex = MotionTrailVertex(vertex_id, vertex_function, context) + total_vertices = len(self.vertex_list) - self.vertex_list [total_vertices : total_vertices] = [motion_trail_vertex] + self.vertex_list[total_vertices : total_vertices] = [motion_trail_vertex] - self.total_vertices = len (self.vertex_list) + self.total_vertices = len(self.vertex_list) self.modified_vertices = True return motion_trail_vertex - def set_vertex_color (self, vertex_id, start_color, end_color): - if (vertex_id >= 0 and vertex_id < self.total_vertices): - motion_trail_vertex = self.vertex_list [vertex_id] + def set_vertex_color(self, vertex_id, start_color, end_color): + """Sets the start and end color of the vertex with the given index, + which must have been previously added by `add_vertex()`. The motion + trail will contain a smooth gradient between these colors. By default, + the motion trail fades from white to black (which, with the default + additive blending mode, makes it show up as a purely white motion trail + that fades out towards the end). + """ + if vertex_id >= 0 and vertex_id < self.total_vertices: + motion_trail_vertex = self.vertex_list[vertex_id] motion_trail_vertex.start_color = start_color motion_trail_vertex.end_color = end_color self.modified_vertices = True - return - - def set_texture (self, texture): + def set_texture(self, texture): + """Defines the texture that should be applied to the trail geometry. + This also enables generation of UV coordinates. + """ self.texture = texture - if (texture): - self.geom_node_path.setTexture (texture) + if texture: + self.geom_node_path.setTexture(texture) # texture.setWrapU(Texture.WMClamp) # texture.setWrapV(Texture.WMClamp) else: - self.geom_node_path.clearTexture ( ) + self.geom_node_path.clearTexture() self.modified_vertices = True - return - def update_vertices (self): - - total_vertices = len (self.vertex_list) + def update_vertices(self): + """This must be called after the list of vertices defining the + cross-section shape of the motion trail has been defined by + `add_vertex()` and `set_vertex_color()`. + """ + total_vertices = len(self.vertex_list) self.total_vertices = total_vertices - if (total_vertices >= 2): + if total_vertices >= 2: vertex_index = 0 - while (vertex_index < total_vertices): - motion_trail_vertex = self.vertex_list [vertex_index] - motion_trail_vertex.vertex = motion_trail_vertex.vertex_function (motion_trail_vertex, motion_trail_vertex.vertex_id, motion_trail_vertex.context) + while vertex_index < total_vertices: + motion_trail_vertex = self.vertex_list[vertex_index] + motion_trail_vertex.vertex = motion_trail_vertex.vertex_function(motion_trail_vertex, motion_trail_vertex.vertex_id, motion_trail_vertex.context) vertex_index += 1 # calculate v coordinate @@ -255,8 +317,8 @@ class MotionTrail(NodePath, DirectObject): float_vertex_index = 0.0 float_total_vertices = 0.0 float_total_vertices = total_vertices - 1.0 - while (vertex_index < total_vertices): - motion_trail_vertex = self.vertex_list [vertex_index] + while vertex_index < total_vertices: + motion_trail_vertex = self.vertex_list[vertex_index] motion_trail_vertex.v = float_vertex_index / float_total_vertices vertex_index += 1 float_vertex_index += 1.0 @@ -264,125 +326,130 @@ class MotionTrail(NodePath, DirectObject): # print "motion_trail_vertex.v", motion_trail_vertex.v self.modified_vertices = True - return - def transferVertices (self): + def transferVertices(self): # transfer only on modification - if (self.modified_vertices): - self.cmotion_trail.setParameters (self.sampling_time, self.time_window, self.texture != None, self.calculate_relative_matrix, self.use_nurbs, self.resolution_distance) + if self.modified_vertices: + self.cmotion_trail.setParameters(self.sampling_time, self.time_window, self.texture is not None, self.calculate_relative_matrix, self.use_nurbs, self.resolution_distance) - self.cmotion_trail.resetVertexList ( ) + self.cmotion_trail.resetVertexList() vertex_index = 0 - total_vertices = len (self.vertex_list) - while (vertex_index < total_vertices): - motion_trail_vertex = self.vertex_list [vertex_index] - self.cmotion_trail.addVertex (motion_trail_vertex.vertex, motion_trail_vertex.start_color, motion_trail_vertex.end_color, motion_trail_vertex.v) + total_vertices = len(self.vertex_list) + while vertex_index < total_vertices: + motion_trail_vertex = self.vertex_list[vertex_index] + self.cmotion_trail.addVertex(motion_trail_vertex.vertex, motion_trail_vertex.start_color, motion_trail_vertex.end_color, motion_trail_vertex.v) vertex_index += 1 self.modified_vertices = False - return - - def register_motion_trail (self): + def register_motion_trail(self): + """Adds this motion trail to the list of trails that are updated + automatically every frame. Be careful not to call this twice. + """ MotionTrail.motion_trail_list = MotionTrail.motion_trail_list + [self] - return - def unregister_motion_trail (self): - if (self in MotionTrail.motion_trail_list): - MotionTrail.motion_trail_list.remove (self) - return + def unregister_motion_trail(self): + """Removes this motion trail from the list of trails that are updated + automatically every frame. If it is not on that list, does nothing. + """ + if self in MotionTrail.motion_trail_list: + MotionTrail.motion_trail_list.remove(self) - def begin_geometry (self): + def begin_geometry(self): + self.vertex_index = 0 - self.vertex_index = 0; - - if (self.texture != None): - self.format = GeomVertexFormat.getV3c4t2 ( ) + if self.texture is not None: + self.format = GeomVertexFormat.getV3c4t2() else: - self.format = GeomVertexFormat.getV3c4 ( ) + self.format = GeomVertexFormat.getV3c4() - self.vertex_data = GeomVertexData ("vertices", self.format, Geom.UHStatic) + self.vertex_data = GeomVertexData("vertices", self.format, Geom.UHStatic) - self.vertex_writer = GeomVertexWriter (self.vertex_data, "vertex") - self.color_writer = GeomVertexWriter (self.vertex_data, "color") - if (self.texture != None): - self.texture_writer = GeomVertexWriter (self.vertex_data, "texcoord") + self.vertex_writer = GeomVertexWriter(self.vertex_data, "vertex") + self.color_writer = GeomVertexWriter(self.vertex_data, "color") + if self.texture is not None: + self.texture_writer = GeomVertexWriter(self.vertex_data, "texcoord") - self.triangles = GeomTriangles (Geom.UHStatic) + self.triangles = GeomTriangles(Geom.UHStatic) - def add_geometry_quad (self, v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3): + def add_geometry_quad(self, v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3): - self.vertex_writer.addData3f (v0 [0], v0 [1], v0 [2]) - self.vertex_writer.addData3f (v1 [0], v1 [1], v1 [2]) - self.vertex_writer.addData3f (v2 [0], v2 [1], v2 [2]) - self.vertex_writer.addData3f (v3 [0], v3 [1], v3 [2]) + self.vertex_writer.addData3f(v0 [0], v0 [1], v0 [2]) + self.vertex_writer.addData3f(v1 [0], v1 [1], v1 [2]) + self.vertex_writer.addData3f(v2 [0], v2 [1], v2 [2]) + self.vertex_writer.addData3f(v3 [0], v3 [1], v3 [2]) - self.color_writer.addData4f (c0) - self.color_writer.addData4f (c1) - self.color_writer.addData4f (c2) - self.color_writer.addData4f (c3) + self.color_writer.addData4f(c0) + self.color_writer.addData4f(c1) + self.color_writer.addData4f(c2) + self.color_writer.addData4f(c3) - if (self.texture != None): - self.texture_writer.addData2f (t0) - self.texture_writer.addData2f (t1) - self.texture_writer.addData2f (t2) - self.texture_writer.addData2f (t3) + if self.texture is not None: + self.texture_writer.addData2f(t0) + self.texture_writer.addData2f(t1) + self.texture_writer.addData2f(t2) + self.texture_writer.addData2f(t3) - vertex_index = self.vertex_index; + vertex_index = self.vertex_index - self.triangles.addVertex (vertex_index + 0) - self.triangles.addVertex (vertex_index + 1) - self.triangles.addVertex (vertex_index + 2) - self.triangles.closePrimitive ( ) + self.triangles.addVertex(vertex_index + 0) + self.triangles.addVertex(vertex_index + 1) + self.triangles.addVertex(vertex_index + 2) + self.triangles.closePrimitive() - self.triangles.addVertex (vertex_index + 1) - self.triangles.addVertex (vertex_index + 3) - self.triangles.addVertex (vertex_index + 2) - self.triangles.closePrimitive ( ) + self.triangles.addVertex(vertex_index + 1) + self.triangles.addVertex(vertex_index + 3) + self.triangles.addVertex(vertex_index + 2) + self.triangles.closePrimitive() self.vertex_index += 4 - def end_geometry (self): - self.geometry = Geom (self.vertex_data) - self.geometry.addPrimitive (self.triangles) + def end_geometry(self): + self.geometry = Geom(self.vertex_data) + self.geometry.addPrimitive(self.triangles) - self.geom_node.removeAllGeoms ( ) - self.geom_node.addGeom (self.geometry) - - def check_for_update (self, current_time): + self.geom_node.removeAllGeoms() + self.geom_node.addGeom(self.geometry) + def check_for_update(self, current_time): + """Returns true if the motion trail is overdue for an update based on + the configured `sampling_time` (by default 0.0 to update continuously), + and is not currently paused. + """ state = False - if ((current_time - self.last_update_time) >= self.sampling_time): + if (current_time - self.last_update_time) >= self.sampling_time: state = True - if (self.pause): + if self.pause: state = False update = state and self.enable return state - def update_motion_trail (self, current_time, transform): - - if (len (self.frame_list) >= 1): - if (transform == self.frame_list [0].transform): + def update_motion_trail(self, current_time, transform): + """If the trail is overdue for an update based on the given time in + seconds, updates it, extracting the new object position from the given + transform matrix. + """ + if len(self.frame_list) >= 1: + if transform == self.frame_list[0].transform: # ignore duplicate transform updates return - if (self.check_for_update (current_time)): + if self.check_for_update(current_time): + color_scale = self.color_scale - color_scale = self.color_scale; - - if (self.fade): + if self.fade: elapsed_time = current_time - self.fade_start_time - if (elapsed_time < 0.0): + if elapsed_time < 0.0: print("elapsed_time < 0: %f" % (elapsed_time)) elapsed_time = 0.0 - if (elapsed_time < self.fade_time): + if elapsed_time < self.fade_time: color_scale = (1.0 - (elapsed_time / self.fade_time)) * color_scale else: color_scale = 0.0 @@ -395,119 +462,116 @@ class MotionTrail(NodePath, DirectObject): index = 0 - last_frame_index = len (self.frame_list) - 1 + last_frame_index = len(self.frame_list) - 1 - while (index <= last_frame_index): - motion_trail_frame = self.frame_list [last_frame_index - index] - if (motion_trail_frame.time >= minimum_time): + while index <= last_frame_index: + motion_trail_frame = self.frame_list[last_frame_index - index] + if motion_trail_frame.time >= minimum_time: break index += 1 - if (index > 0): - self.frame_list [last_frame_index - index: last_frame_index + 1] = [ ] + if index > 0: + self.frame_list[last_frame_index - index: last_frame_index + 1] = [] # add new frame to beginning of list - motion_trail_frame = MotionTrailFrame (current_time, transform) + motion_trail_frame = MotionTrailFrame(current_time, transform) self.frame_list = [motion_trail_frame] + self.frame_list # convert frames and vertices to geometry - total_frames = len (self.frame_list) + total_frames = len(self.frame_list) - """ - print "total_frames", total_frames + #print("total_frames", total_frames) + # + #index = 0 + #while index < total_frames: + # motion_trail_frame = self.frame_list[index] + # print("frame time", index, motion_trail_frame.time) + # index += 1 - index = 0; - while (index < total_frames): - motion_trail_frame = self.frame_list [index] - print "frame time", index, motion_trail_frame.time - index += 1 - """ - - if ((total_frames >= 2) and (self.total_vertices >= 2)): - - self.begin_geometry ( ) + if total_frames >= 2 and self.total_vertices >= 2: + self.begin_geometry() total_segments = total_frames - 1 - last_motion_trail_frame = self.frame_list [total_segments] + last_motion_trail_frame = self.frame_list[total_segments] minimum_time = last_motion_trail_frame.time delta_time = current_time - minimum_time - if (self.calculate_relative_matrix): - inverse_matrix = Mat4 (transform) - inverse_matrix.invertInPlace ( ) + if self.calculate_relative_matrix: + inverse_matrix = Mat4(transform) + inverse_matrix.invertInPlace() - if (self.use_nurbs and (total_frames >= 5)): + if self.use_nurbs and total_frames >= 5: total_distance = 0.0 - vector = Vec3 ( ) + vector = Vec3() - nurbs_curve_evaluator_list = [ ] + nurbs_curve_evaluator_list = [] total_vertex_segments = self.total_vertices - 1 - # create a NurbsCurveEvaluator for each vertex (the starting point for the trail) + # create a NurbsCurveEvaluator for each vertex(the starting point for the trail) index = 0 - while (index < self.total_vertices): - nurbs_curve_evaluator = NurbsCurveEvaluator ( ) - nurbs_curve_evaluator.reset (total_segments) + while index < self.total_vertices: + nurbs_curve_evaluator = NurbsCurveEvaluator() + nurbs_curve_evaluator.reset(total_segments) nurbs_curve_evaluator_list = nurbs_curve_evaluator_list + [nurbs_curve_evaluator] index += 1 # add vertices to each NurbsCurveEvaluator segment_index = 0 - while (segment_index < total_segments): - motion_trail_frame_start = self.frame_list [segment_index] - motion_trail_frame_end = self.frame_list [segment_index + 1] + while segment_index < total_segments: + motion_trail_frame_start = self.frame_list[segment_index] + motion_trail_frame_end = self.frame_list[segment_index + 1] - vertex_segement_index = 0 + vertex_segment_index = 0 - if (self.calculate_relative_matrix): - start_transform = Mat4 ( ) - end_transform = Mat4 ( ) + if self.calculate_relative_matrix: + start_transform = Mat4() + end_transform = Mat4() - start_transform.multiply (motion_trail_frame_start.transform, inverse_matrix) - end_transform.multiply (motion_trail_frame_end.transform, inverse_matrix) + start_transform.multiply(motion_trail_frame_start.transform, inverse_matrix) + end_transform.multiply(motion_trail_frame_end.transform, inverse_matrix) else: start_transform = motion_trail_frame_start.transform end_transform = motion_trail_frame_end.transform - motion_trail_vertex_start = self.vertex_list [0] + motion_trail_vertex_start = self.vertex_list[0] - v0 = start_transform.xform (motion_trail_vertex_start.vertex) - v2 = end_transform.xform (motion_trail_vertex_start.vertex) + v0 = start_transform.xform(motion_trail_vertex_start.vertex) + v2 = end_transform.xform(motion_trail_vertex_start.vertex) - nurbs_curve_evaluator = nurbs_curve_evaluator_list [vertex_segement_index] + nurbs_curve_evaluator = nurbs_curve_evaluator_list [vertex_segment_index] - nurbs_curve_evaluator.setVertex (segment_index, v0) + nurbs_curve_evaluator.setVertex(segment_index, v0) - while (vertex_segement_index < total_vertex_segments): + while vertex_segment_index < total_vertex_segments: - motion_trail_vertex_start = self.vertex_list [vertex_segement_index] - motion_trail_vertex_end = self.vertex_list [vertex_segement_index + 1] + motion_trail_vertex_start = self.vertex_list[vertex_segment_index] + motion_trail_vertex_end = self.vertex_list[vertex_segment_index + 1] - v1 = start_transform.xform (motion_trail_vertex_end.vertex) - v3 = end_transform.xform (motion_trail_vertex_end.vertex) + v1 = start_transform.xform(motion_trail_vertex_end.vertex) + v3 = end_transform.xform(motion_trail_vertex_end.vertex) - nurbs_curve_evaluator = nurbs_curve_evaluator_list [vertex_segement_index + 1] + nurbs_curve_evaluator = nurbs_curve_evaluator_list [vertex_segment_index + 1] - nurbs_curve_evaluator.setVertex (segment_index, v1) + nurbs_curve_evaluator.setVertex(segment_index, v1) - if (vertex_segement_index == (total_vertex_segments - 1)): + if vertex_segment_index == (total_vertex_segments - 1): v = v1 - v3 - vector.set (v[0], v[1], v[2]) + vector.set(v[0], v[1], v[2]) distance = vector.length() total_distance += distance - vertex_segement_index += 1 + vertex_segment_index += 1 segment_index += 1 # evaluate NurbsCurveEvaluator for each vertex index = 0 - nurbs_curve_result_list = [ ] - while (index < self.total_vertices): + nurbs_curve_result_list = [] + while index < self.total_vertices: nurbs_curve_evaluator = nurbs_curve_evaluator_list [index] - nurbs_curve_result = nurbs_curve_evaluator.evaluate ( ) + nurbs_curve_result = nurbs_curve_evaluator.evaluate() nurbs_curve_result_list = nurbs_curve_result_list + [nurbs_curve_result] nurbs_start_t = nurbs_curve_result.getStartT() @@ -517,57 +581,55 @@ class MotionTrail(NodePath, DirectObject): # create quads from NurbsCurveResult total_curve_segments = total_distance / self.resolution_distance - if (total_curve_segments < total_segments): - total_curve_segments = total_segments; + if total_curve_segments < total_segments: + total_curve_segments = total_segments - v0 = Vec3 ( ) - v1 = Vec3 ( ) - v2 = Vec3 ( ) - v3 = Vec3 ( ) + v0 = Vec3() + v1 = Vec3() + v2 = Vec3() + v3 = Vec3() - def one_minus_x (x): + def one_minus_x(x): x = 1.0 - x - if (x < 0.0): + if x < 0.0: x = 0.0 return x curve_segment_index = 0.0 - while (curve_segment_index < total_curve_segments): + while curve_segment_index < total_curve_segments: - vertex_segement_index = 0 + vertex_segment_index = 0 - if (True): - st = curve_segment_index / total_curve_segments - et = (curve_segment_index + 1.0) / total_curve_segments - else: - st = curve_segment_index / total_segments - et = (curve_segment_index + 1.0) / total_segments + st = curve_segment_index / total_curve_segments + et = (curve_segment_index + 1.0) / total_curve_segments + #st = curve_segment_index / total_segments + #et = (curve_segment_index + 1.0) / total_segments start_t = st end_t = et - if (self.square_t): + if self.square_t: start_t *= start_t end_t *= end_t - motion_trail_vertex_start = self.vertex_list [0] + motion_trail_vertex_start = self.vertex_list[0] vertex_start_color = motion_trail_vertex_start.end_color + (motion_trail_vertex_start.start_color - motion_trail_vertex_start.end_color) color_start_t = color_scale * start_t color_end_t = color_scale * end_t - c0 = vertex_start_color * one_minus_x (color_start_t) - c2 = vertex_start_color * one_minus_x (color_end_t) + c0 = vertex_start_color * one_minus_x(color_start_t) + c2 = vertex_start_color * one_minus_x(color_end_t) - t0 = Vec2 (one_minus_x (st), motion_trail_vertex_start.v) - t2 = Vec2 (one_minus_x (et), motion_trail_vertex_start.v) + t0 = Vec2(one_minus_x(st), motion_trail_vertex_start.v) + t2 = Vec2(one_minus_x(et), motion_trail_vertex_start.v) - while (vertex_segement_index < total_vertex_segments): + while vertex_segment_index < total_vertex_segments: - motion_trail_vertex_start = self.vertex_list [vertex_segement_index] - motion_trail_vertex_end = self.vertex_list [vertex_segement_index + 1] + motion_trail_vertex_start = self.vertex_list[vertex_segment_index] + motion_trail_vertex_end = self.vertex_list[vertex_segment_index + 1] - start_nurbs_curve_result = nurbs_curve_result_list [vertex_segement_index] - end_nurbs_curve_result = nurbs_curve_result_list [vertex_segement_index + 1] + start_nurbs_curve_result = nurbs_curve_result_list [vertex_segment_index] + end_nurbs_curve_result = nurbs_curve_result_list [vertex_segment_index + 1] start_nurbs_start_t = start_nurbs_curve_result.getStartT() start_nurbs_end_t = start_nurbs_curve_result.getEndT() @@ -577,23 +639,23 @@ class MotionTrail(NodePath, DirectObject): start_delta_t = (start_nurbs_end_t - start_nurbs_start_t) end_delta_t = (end_nurbs_end_t - end_nurbs_start_t) - start_nurbs_curve_result.evalPoint (start_nurbs_start_t + (start_delta_t * st), v0); - end_nurbs_curve_result.evalPoint (end_nurbs_start_t + (end_delta_t * st), v1); + start_nurbs_curve_result.evalPoint(start_nurbs_start_t + (start_delta_t * st), v0) + end_nurbs_curve_result.evalPoint(end_nurbs_start_t + (end_delta_t * st), v1) - start_nurbs_curve_result.evalPoint (start_nurbs_start_t + (start_delta_t * et), v2); - end_nurbs_curve_result.evalPoint (end_nurbs_start_t + (end_delta_t * et), v3); + start_nurbs_curve_result.evalPoint(start_nurbs_start_t + (start_delta_t * et), v2) + end_nurbs_curve_result.evalPoint(end_nurbs_start_t + (end_delta_t * et), v3) # color vertex_end_color = motion_trail_vertex_end.end_color + (motion_trail_vertex_end.start_color - motion_trail_vertex_end.end_color) - c1 = vertex_end_color * one_minus_x (color_start_t) - c3 = vertex_end_color * one_minus_x (color_end_t) + c1 = vertex_end_color * one_minus_x(color_start_t) + c3 = vertex_end_color * one_minus_x(color_end_t) # uv - t1 = Vec2 (one_minus_x (st), motion_trail_vertex_end.v) - t3 = Vec2 (one_minus_x (et), motion_trail_vertex_end.v) + t1 = Vec2(one_minus_x(st), motion_trail_vertex_end.v) + t3 = Vec2(one_minus_x(et), motion_trail_vertex_end.v) - self.add_geometry_quad (v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3) + self.add_geometry_quad(v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3) # reuse calculations c0 = c1 @@ -602,17 +664,15 @@ class MotionTrail(NodePath, DirectObject): t0 = t1 t2 = t3 - vertex_segement_index += 1 + vertex_segment_index += 1 curve_segment_index += 1.0 - else: - segment_index = 0 - while (segment_index < total_segments): - motion_trail_frame_start = self.frame_list [segment_index] - motion_trail_frame_end = self.frame_list [segment_index + 1] + while segment_index < total_segments: + motion_trail_frame_start = self.frame_list[segment_index] + motion_trail_frame_end = self.frame_list[segment_index + 1] start_t = (motion_trail_frame_start.time - minimum_time) / delta_time end_t = (motion_trail_frame_end.time - minimum_time) / delta_time @@ -620,26 +680,26 @@ class MotionTrail(NodePath, DirectObject): st = start_t et = end_t - if (self.square_t): + if self.square_t: start_t *= start_t end_t *= end_t - vertex_segement_index = 0 + vertex_segment_index = 0 total_vertex_segments = self.total_vertices - 1 - if (self.calculate_relative_matrix): - start_transform = Mat4 ( ) - end_transform = Mat4 ( ) - start_transform.multiply (motion_trail_frame_start.transform, inverse_matrix) - end_transform.multiply (motion_trail_frame_end.transform, inverse_matrix) + if self.calculate_relative_matrix: + start_transform = Mat4() + end_transform = Mat4() + start_transform.multiply(motion_trail_frame_start.transform, inverse_matrix) + end_transform.multiply(motion_trail_frame_end.transform, inverse_matrix) else: start_transform = motion_trail_frame_start.transform end_transform = motion_trail_frame_end.transform - motion_trail_vertex_start = self.vertex_list [0] + motion_trail_vertex_start = self.vertex_list[0] - v0 = start_transform.xform (motion_trail_vertex_start.vertex) - v2 = end_transform.xform (motion_trail_vertex_start.vertex) + v0 = start_transform.xform(motion_trail_vertex_start.vertex) + v2 = end_transform.xform(motion_trail_vertex_start.vertex) vertex_start_color = motion_trail_vertex_start.end_color + (motion_trail_vertex_start.start_color - motion_trail_vertex_start.end_color) color_start_t = color_scale * start_t @@ -647,16 +707,16 @@ class MotionTrail(NodePath, DirectObject): c0 = vertex_start_color * color_start_t c2 = vertex_start_color * color_end_t - t0 = Vec2 (st, motion_trail_vertex_start.v) - t2 = Vec2 (et, motion_trail_vertex_start.v) + t0 = Vec2(st, motion_trail_vertex_start.v) + t2 = Vec2(et, motion_trail_vertex_start.v) - while (vertex_segement_index < total_vertex_segments): + while vertex_segment_index < total_vertex_segments: - motion_trail_vertex_start = self.vertex_list [vertex_segement_index] - motion_trail_vertex_end = self.vertex_list [vertex_segement_index + 1] + motion_trail_vertex_start = self.vertex_list[vertex_segment_index] + motion_trail_vertex_end = self.vertex_list[vertex_segment_index + 1] - v1 = start_transform.xform (motion_trail_vertex_end.vertex) - v3 = end_transform.xform (motion_trail_vertex_end.vertex) + v1 = start_transform.xform(motion_trail_vertex_end.vertex) + v3 = end_transform.xform(motion_trail_vertex_end.vertex) # color vertex_end_color = motion_trail_vertex_end.end_color + (motion_trail_vertex_end.start_color - motion_trail_vertex_end.end_color) @@ -665,10 +725,10 @@ class MotionTrail(NodePath, DirectObject): c3 = vertex_end_color * color_end_t # uv - t1 = Vec2 (st, motion_trail_vertex_end.v) - t3 = Vec2 (et, motion_trail_vertex_end.v) + t1 = Vec2(st, motion_trail_vertex_end.v) + t3 = Vec2(et, motion_trail_vertex_end.v) - self.add_geometry_quad (v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3) + self.add_geometry_quad(v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3) # reuse calculations v0 = v1 @@ -680,87 +740,88 @@ class MotionTrail(NodePath, DirectObject): t0 = t1 t2 = t3 - vertex_segement_index += 1 + vertex_segment_index += 1 segment_index += 1 - self.end_geometry ( ) - - return + self.end_geometry() def enable_motion_trail(self, enable): + """Sets whether the motion trail is currently enabled. Every motion + trail starts off as being enabled, passing False to this method prevents + it from being updated. + """ self.enable = enable - return def reset_motion_trail(self): - self.frame_list = [ ] - self.cmotion_trail.reset ( ); - return + """Call this to have the motion trail restart from nothing on the next + update. + """ + self.frame_list = [] + self.cmotion_trail.reset() def reset_motion_trail_geometry(self): - if (self.geom_node != None): - self.geom_node.removeAllGeoms ( ) - return + """Destroys the currently generated motion trail geometry immediately. + However, it will be fully regenerated on the next call to update, see + `reset_motion_trail()` to prevent this. + """ + if self.geom_node is not None: + self.geom_node.removeAllGeoms() - def attach_motion_trail (self): - self.reset_motion_trail ( ) - return + def attach_motion_trail(self): + """Alias of `reset_motion_trail()`. + """ + self.reset_motion_trail() - def begin_motion_trail (self): - if (self.continuous_motion_trail == False): - self.reset_motion_trail ( ) - self.active = True; - self.playing = True; - return + def begin_motion_trail(self): + if not self.continuous_motion_trail: + self.reset_motion_trail() + self.active = True + self.playing = True - def end_motion_trail (self): - if (self.continuous_motion_trail == False): + def end_motion_trail(self): + if not self.continuous_motion_trail: self.active = False - self.reset_motion_trail ( ) - self.reset_motion_trail_geometry ( ) - self.playing = False; - return + self.reset_motion_trail() + self.reset_motion_trail_geometry() + self.playing = False # the following functions are not currently supported in the C++ version - def set_fade (self, time, current_time): - if (self.pause == False): + def set_fade(self, time, current_time): + if not self.pause: self.fade_color_scale = 1.0 - if (time == 0.0): + if time == 0.0: self.fade = False else: self.fade_start_time = current_time self.fade_time = time self.fade = True - return def pause_motion_trail(self, current_time): - if (self.pause == False): + if not self.pause: self.pause_time = current_time self.pause = True - return def resume_motion_trail(self, current_time): - if (self.pause): + if self.pause: delta_time = current_time - self.pause_time frame_index = 0 - total_frames = len (self.frame_list) - while (frame_index < total_frames): - motion_trail_frame = self.frame_list [frame_index] + total_frames = len(self.frame_list) + while frame_index < total_frames: + motion_trail_frame = self.frame_list[frame_index] motion_trail_frame.time += delta_time frame_index += 1 - if (self.fade): + if self.fade: self.fade_start_time += delta_time self.pause = False - return - def toggle_pause_motion_trail (self, current_time): - if (self.pause): - self.resume_motion_trail (current_time) + def toggle_pause_motion_trail(self, current_time): + if self.pause: + self.resume_motion_trail(current_time) else: - self.pause_motion_trail (current_time) - + self.pause_motion_trail(current_time) From 8ab37b88046cc8724ffd21651d001a32c56bd80b Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 15 Dec 2022 17:58:46 +0100 Subject: [PATCH 2/3] motiontrail: Add easier way to call `add_vertex()` Currently, you must pass in a function, which is a little awkward compared to just passing in a position. Now you can just pass in a vertex position, similar to the C++ interface. --- direct/src/motiontrail/MotionTrail.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/direct/src/motiontrail/MotionTrail.py b/direct/src/motiontrail/MotionTrail.py index 49a65d8703..097f61ee09 100644 --- a/direct/src/motiontrail/MotionTrail.py +++ b/direct/src/motiontrail/MotionTrail.py @@ -244,7 +244,7 @@ class MotionTrail(NodePath, DirectObject): return Task.cont - def add_vertex(self, vertex_id, vertex_function, context): + def add_vertex(self, vertex_id, vertex_function=None, context=None): """This must be called repeatedly to define the polygon that forms the cross-section of the generated motion trail geometry. The first argument is a user-defined vertex identifier, the second is a function @@ -255,8 +255,15 @@ class MotionTrail(NodePath, DirectObject): After calling this, you must call `update_vertices()` before the changes will fully take effect. + + As of Panda3D 1.10.13, you may alternatively simply pass in a single + argument containing the vertex position as a `.Vec4` or `.Point3`. """ - motion_trail_vertex = MotionTrailVertex(vertex_id, vertex_function, context) + if vertex_function is None: + motion_trail_vertex = MotionTrailVertex(None, None, context) + motion_trail_vertex.vertex = Vec4(vertex_id) + else: + motion_trail_vertex = MotionTrailVertex(vertex_id, vertex_function, context) total_vertices = len(self.vertex_list) self.vertex_list[total_vertices : total_vertices] = [motion_trail_vertex] @@ -308,7 +315,8 @@ class MotionTrail(NodePath, DirectObject): vertex_index = 0 while vertex_index < total_vertices: motion_trail_vertex = self.vertex_list[vertex_index] - motion_trail_vertex.vertex = motion_trail_vertex.vertex_function(motion_trail_vertex, motion_trail_vertex.vertex_id, motion_trail_vertex.context) + if motion_trail_vertex.vertex_function is not None: + motion_trail_vertex.vertex = motion_trail_vertex.vertex_function(motion_trail_vertex, motion_trail_vertex.vertex_id, motion_trail_vertex.context) vertex_index += 1 # calculate v coordinate From 38528b797b5fa0547de834c7b2cff02b661423e8 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 15 Dec 2022 19:50:29 +0100 Subject: [PATCH 3/3] downloader: Initialize winsock even if nativenet is compiled out Fixes HTTPClient when static linking if nativenet is not used and therefore init_network() is compiled out --- panda/src/downloader/config_downloader.cxx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/panda/src/downloader/config_downloader.cxx b/panda/src/downloader/config_downloader.cxx index cd8f1de723..6543630ed3 100644 --- a/panda/src/downloader/config_downloader.cxx +++ b/panda/src/downloader/config_downloader.cxx @@ -18,6 +18,9 @@ #include "virtualFileMountHTTP.h" #include "pandaSystem.h" +#if defined(_WIN32) && defined(HAVE_OPENSSL) +#include +#endif #if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_PANDA_DOWNLOADER) #error Buildsystem error: BUILDING_PANDA_DOWNLOADER not defined @@ -170,5 +173,11 @@ init_libdownloader() { PandaSystem *ps = PandaSystem::get_global_ptr(); ps->add_system("OpenSSL"); + +#ifdef _WIN32 + // We need to call this before we can open sockets on Windows. + static struct WSAData mydata; + WSAStartup(0x0101, &mydata); +#endif // _WIN32 #endif // HAVE_OPENSSL }