Poodletooth-iLand/panda/direct/motiontrail/MotionTrail.py
Master Jumblespeed d882959bfa switch to remote
2015-05-18 22:11:33 -04:00

764 lines
29 KiB
Python
Executable file

from pandac.PandaModules import *
from direct.task import Task
from direct.showbase.DirectObject import DirectObject
def remove_task ( ):
if (MotionTrail.task_added):
total_motion_trails = len (MotionTrail.motion_trail_list)
if (total_motion_trails > 0):
print "warning:", total_motion_trails, "motion trails still exist when motion trail task is removed"
MotionTrail.motion_trail_list = [ ]
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)
# 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.v = 0.0
class MotionTrailFrame:
def __init__ (self, current_time, transform):
self.time = current_time
self.transform = transform
class MotionTrail(NodePath, DirectObject):
notify = directNotify.newCategory ("MotionTrail")
task_added = False
motion_trail_list = [ ]
motion_trail_task_name = "motion_trail_task"
global_enable = True
@classmethod
def setGlobalEnable (self, enable):
MotionTrail.global_enable = enable
def __init__ (self,name,parent_node_path):
DirectObject.__init__(self)
NodePath.__init__ (self,name)
# required initialization
self.active = True
self.enable = True
self.pause = False
self.pause_time = 0.0
self.fade = False
self.fade_end = False
self.fade_start_time = 0.0
self.fade_color_scale = 1.0
self.total_vertices = 0
self.last_update_time = 0.0
self.texture = None
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;
# default options
self.continuous_motion_trail = True
self.color_scale = 1.0
self.time_window = 1.0
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.geom_node_path = self.attachNewNode(self.geom_node)
node_path = self.geom_node_path
### set render states
node_path.setTwoSided (True)
# set additive blend effects
node_path.setTransparency (True)
node_path.setDepthWrite (False)
node_path.node ( ).setAttrib (ColorBlendAttrib.make (ColorBlendAttrib.MAdd))
# do not light
node_path.setLightOff ( )
# disable writes to destination alpha, write out rgb colors only
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)
self.acceptOnce ("clientLogout", remove_task)
MotionTrail.task_added = True
self.relative_to_render = False
self.use_nurbs = False
self.resolution_distance = 0.5
self.cmotion_trail = CMotionTrail ( )
self.cmotion_trail.setGeomNode (self.geom_node)
self.modified_vertices = True
if base.config.GetBool('want-python-motion-trails', 0):
self.use_python_version = True
else:
self.use_python_version = False
return
def delete(self):
self.reset_motion_trail()
self.reset_motion_trail_geometry()
self.cmotion_trail.resetVertexList ( )
self.removeNode()
return
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)
def motion_trail_task (self, task):
current_time = task.time
total_motion_trails = len (MotionTrail.motion_trail_list)
index = 0
while (index < total_motion_trails):
motion_trail = MotionTrail.motion_trail_list [index]
if (MotionTrail.global_enable):
if (motion_trail.use_python_version):
# Python version
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 and (motion_trail.relative_to_render == False)):
transform = motion_trail.getMat(motion_trail.root_node_path)
else:
transform = Mat4 (motion_trail.getNetTransform ( ).getMat ( ))
if (transform != None):
motion_trail.update_motion_trail (current_time, transform)
else:
# C++ version
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 and (motion_trail.relative_to_render == False)):
transform = motion_trail.getMat(motion_trail.root_node_path)
else:
transform = Mat4 (motion_trail.getNetTransform ( ).getMat ( ))
if (transform != None):
motion_trail.transferVertices ( )
motion_trail.cmotion_trail.updateMotionTrail (current_time, transform)
else:
motion_trail.reset_motion_trail()
motion_trail.reset_motion_trail_geometry()
index += 1
return Task.cont
def add_vertex (self, vertex_id, vertex_function, context):
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.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]
motion_trail_vertex.start_color = start_color
motion_trail_vertex.end_color = end_color
self.modified_vertices = True
return
def set_texture (self, texture):
self.texture = texture
if (texture):
self.geom_node_path.setTexture (texture)
# texture.setWrapU(Texture.WMClamp)
# texture.setWrapV(Texture.WMClamp)
else:
self.geom_node_path.clearTexture ( )
self.modified_vertices = True
return
def update_vertices (self):
total_vertices = len (self.vertex_list)
self.total_vertices = total_vertices
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)
vertex_index += 1
# calculate v coordinate
# this is based on the number of vertices only and not on the relative positions of the vertices
vertex_index = 0
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]
motion_trail_vertex.v = float_vertex_index / float_total_vertices
vertex_index += 1
float_vertex_index += 1.0
# print "motion_trail_vertex.v", motion_trail_vertex.v
self.modified_vertices = True
return
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)
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)
vertex_index += 1
self.modified_vertices = False
return
def register_motion_trail (self):
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 begin_geometry (self):
self.vertex_index = 0;
if (self.texture != None):
self.format = GeomVertexFormat.getV3c4t2 ( )
else:
self.format = GeomVertexFormat.getV3c4 ( )
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.triangles = GeomTriangles (Geom.UHStatic)
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.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)
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 + 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)
self.geom_node.removeAllGeoms ( )
self.geom_node.addGeom (self.geometry)
def check_for_update (self, current_time):
state = False
if ((current_time - self.last_update_time) >= self.sampling_time):
state = True
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):
# ignore duplicate transform updates
return
if (self.check_for_update (current_time)):
color_scale = self.color_scale;
if (self.fade):
elapsed_time = current_time - self.fade_start_time
if (elapsed_time < 0.0):
elapsed_time = 0.0
print "elapsed_time < 0", elapsed_time
if (elapsed_time < self.fade_time):
color_scale = (1.0 - (elapsed_time / self.fade_time)) * color_scale
else:
color_scale = 0.0
self.fade_end = True
self.last_update_time = current_time
# remove expired frames
minimum_time = current_time - self.time_window
index = 0
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):
break
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)
self.frame_list = [motion_trail_frame] + self.frame_list
# convert frames and vertices to geometry
total_frames = len (self.frame_list)
"""
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
"""
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]
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.use_nurbs and (total_frames >= 5)):
total_distance = 0.0
vector = Vec3 ( )
nurbs_curve_evaluator_list = [ ]
total_vertex_segments = self.total_vertices - 1
# 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)
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]
vertex_segement_index = 0
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]
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.setVertex (segment_index, v0)
while (vertex_segement_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]
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.setVertex (segment_index, v1)
if (vertex_segement_index == (total_vertex_segments - 1)):
v = v1 - v3
vector.set (v[0], v[1], v[2])
distance = vector.length()
total_distance += distance
vertex_segement_index += 1
segment_index += 1
# evaluate NurbsCurveEvaluator for each vertex
index = 0
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_list = nurbs_curve_result_list + [nurbs_curve_result]
nurbs_start_t = nurbs_curve_result.getStartT()
nurbs_end_t = nurbs_curve_result.getEndT()
index += 1
# create quads from NurbsCurveResult
total_curve_segments = total_distance / self.resolution_distance
if (total_curve_segments < total_segments):
total_curve_segments = total_segments;
v0 = Vec3 ( )
v1 = Vec3 ( )
v2 = Vec3 ( )
v3 = Vec3 ( )
def one_minus_x (x):
x = 1.0 - x
if (x < 0.0):
x = 0.0
return x
curve_segment_index = 0.0
while (curve_segment_index < total_curve_segments):
vertex_segement_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
start_t = st
end_t = et
if (self.square_t):
start_t *= start_t
end_t *= end_t
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)
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):
motion_trail_vertex_start = self.vertex_list [vertex_segement_index]
motion_trail_vertex_end = self.vertex_list [vertex_segement_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_start_t = start_nurbs_curve_result.getStartT()
start_nurbs_end_t = start_nurbs_curve_result.getEndT()
end_nurbs_start_t = end_nurbs_curve_result.getStartT()
end_nurbs_end_t = end_nurbs_curve_result.getEndT()
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 * 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)
# uv
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)
# reuse calculations
c0 = c1
c2 = c3
t0 = t1
t2 = t3
vertex_segement_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]
start_t = (motion_trail_frame_start.time - minimum_time) / delta_time
end_t = (motion_trail_frame_end.time - minimum_time) / delta_time
st = start_t
et = end_t
if (self.square_t):
start_t *= start_t
end_t *= end_t
vertex_segement_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)
else:
start_transform = motion_trail_frame_start.transform
end_transform = motion_trail_frame_end.transform
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)
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 * 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)
while (vertex_segement_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]
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)
c1 = vertex_end_color * color_start_t
c3 = vertex_end_color * color_end_t
# uv
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)
# reuse calculations
v0 = v1
v2 = v3
c0 = c1
c2 = c3
t0 = t1
t2 = t3
vertex_segement_index += 1
segment_index += 1
self.end_geometry ( )
return
def enable_motion_trail(self, enable):
self.enable = enable
return
def reset_motion_trail(self):
self.frame_list = [ ]
self.cmotion_trail.reset ( );
return
def reset_motion_trail_geometry(self):
if (self.geom_node != None):
self.geom_node.removeAllGeoms ( )
return
def attach_motion_trail (self):
self.reset_motion_trail ( )
return
def begin_motion_trail (self):
if (self.continuous_motion_trail == False):
self.reset_motion_trail ( )
self.active = True;
self.playing = True;
return
def end_motion_trail (self):
if (self.continuous_motion_trail == False):
self.active = False
self.reset_motion_trail ( )
self.reset_motion_trail_geometry ( )
self.playing = False;
return
# the following functions are not currently supported in the C++ version
def set_fade (self, time, current_time):
if (self.pause == False):
self.fade_color_scale = 1.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):
self.pause_time = current_time
self.pause = True
return
def resume_motion_trail(self, current_time):
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]
motion_trail_frame.time += delta_time
frame_index += 1
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)
else:
self.pause_motion_trail (current_time)