def va(vx, vz, iang, sang, n): # shortcut Verts.append for i in range(n): v = Vector((vx, 0, vz)) ai = sang + iang*i E_rot = Euler((0, 0, ai), 'XYZ') v.rotate(E_rot) Verts.append((v.x, v.y, v.z))
def va(vx, vz, iang, sang, n): # shortcut Verts.append for i in range(n): v = Vector((vx, 0, vz)) ai = sang + iang * i E_rot = Euler((0, 0, ai), 'XYZ') v.rotate(E_rot) Verts.append((v.x, v.y, v.z))
def exportProperties(self, exportPath): values = {} values['type'] = "\"camera\"" if bpy.data.objects.find( self.sp_objectProperty) != -1 and bpy.data.cameras.find( bpy.data.objects[self.sp_objectProperty].data.name) != -1: object = bpy.data.objects[self.sp_objectProperty] camera = bpy.data.objects[self.sp_objectProperty].data # Get some parameters rotMatrix = object.matrix_world.to_quaternion() targetVec = Vector((0.0, 0.0, -1.0)) targetVec.rotate(rotMatrix) targetVec += object.matrix_world.to_translation() upVec = Vector((0.0, 1.0, 0.0)) upVec.rotate(rotMatrix) values['eye'] = [ float(object.matrix_world[0][3]), float(object.matrix_world[1][3]), float(object.matrix_world[2][3]) ] values['target'] = [targetVec[0], targetVec[1], targetVec[2]] values['up'] = [upVec[0], upVec[1], upVec[2]] values['fov'] = camera.angle_y * 180.0 / numpy.pi values['principalPoint'] = [ 0.5 - camera.shift_x * 2.0, 0.5 - camera.shift_y * 2.0 ] values['size'] = [ self.inputs['Width'].default_value, self.inputs['Height'].default_value ] return values
def make_verts(n_petals=8, vp_petal=20, profile_radius=1.3, amp=1.0): # variables z_float = 0.0 n_verts = n_petals * vp_petal section_angle = 360.0 / n_verts position = (2 * (pi / (n_verts / n_petals))) # consumables Verts = [] # makes vertex coordinates for i in range(n_verts): # difference is a function of the position on the circumference difference = amp * cos(i * position) arm = profile_radius + difference ampline = Vector((arm, 0.0, 0.0)) rad_angle = radians(section_angle * i) myEuler = Euler((0.0, 0.0, rad_angle), 'XYZ') # changes the vector in place, successive calls are accumulative # we reset at the start of the loop. ampline.rotate(myEuler) x_float = ampline.x y_float = ampline.y Verts.append((x_float, y_float, z_float)) return Verts
def simbone_bake(ob, pose_bone, edit_bone, scene): frames, frame_step = get_scene_frames(scene) matrices = get_matrices(ob, pose_bone, edit_bone, frames) end_mats = get_matrices_single_bone(ob, pose_bone, edit_bone, frames) # Main lists. positions = [ mat.decompose()[0] * scene.simboneworld.spaceScale for mat in matrices ] orientations = [(matrices[i] @ end_mats[i].inverted()).decompose()[1] for i in range(len(end_mats))] velocities = [(positions[i] - positions[max(0, i - 1)]) / frame_step for i in range(len(positions))] accelerations = [(velocities[i] - velocities[max(0, i - 1)]) / frame_step for i in range(len(velocities))] gravity = get_gravity(scene) custom_force = pose_bone.simbone.custom_force.copy() up_quat = Euler((pose_bone.simbone.up_offset - 90, 0, 0)).to_quaternion() start_dir = bone_quat_to_dir(matrices[0].decompose()[1]) # Setup pendulum values. pen = Pendulum() pen.l = edit_bone.length * scene.simboneworld.spaceScale pen.m = pose_bone.simbone.m pen.set_coords(start_dir) pen.c_a = pose_bone.simbone.c_a pen.c_d = pose_bone.simbone.c_d datapath = 'pose.bones["' + pose_bone.name + '"].rotation_quaternion' pose_bone.rotation_mode = 'QUATERNION' pose_bone.rotation_quaternion = end_mats[0].decompose()[1] pose_bone.keyframe_insert(data_path='rotation_quaternion', frame=frames[0]) fcurves = [ ob.animation_data.action.fcurves.find(datapath, index=i) for i in range(4) ] for i in range(1, len(matrices)): matrix = matrices[i] frame = frames[i] g = gravity.copy() f = custom_force.copy() f.rotate(orientations[i]) pen.w = scene.simboneworld.wind - velocities[i] pen.f = g + f - accelerations[i] pen.do_steps(frame_step * scene.simboneworld.timeScale, 1) direction = Vector(pen.get_coords()) direction.rotate(orientations[i].inverted()) direction.rotate(up_quat.inverted()) orient = direction.to_track_quat('Y', 'Z') orient.rotate(up_quat) for i in range(4): fcurves[i].keyframe_points.insert(frame, orient[i])
def _gothic_arc_iterator(radius, cx, res, low_to_high=True) -> Tuple[float, float]: """ Generate (x, z) points on one side of a gothic arc with the given radius. :param radius: the radius of the circle that forms the arc :param cx: the x value of the center of the window :param res: the number of segments on the arc, barring the start and end point :param low_to_high: whether to start at the center and work outwards, or to start level and work to the top :return a tuple of the (x, z) coordinates where x and z are the offset from the center of the arc """ # make angles negative because of how CCW rotation is negative in Blender start_angle, end_angle = 0, -acos(cx / radius) if not low_to_high: # swap order if not low to high start_angle, end_angle = end_angle, start_angle ang_step = Euler((0, (end_angle - start_angle) / (res + 1), 0)) v = Vector((radius, 0, 0)) v.rotate(Euler((0, start_angle, 0))) for _ in range(res + 2): yield (v[0], v[2]) # shift x to be centered around x=0 v.rotate(ang_step)
def get_location_from_sequence_data(sequence_data, frame_id, p_bone, is_convert_local_to_pose): channel_values = get_values_from_sequence_data(sequence_data, frame_id) if len(channel_values) == 1: location = channel_values[0] else: location = Vector([ channel_values[0], channel_values[1], channel_values[2], ]) if not is_convert_local_to_pose: return location mat = p_bone.bone.matrix_local if p_bone.bone.parent is not None: mat = p_bone.bone.parent.matrix_local.inverted() @ p_bone.bone.matrix_local mat_decomposed = mat.decompose() bone_location = mat_decomposed[0] bone_rotation = mat_decomposed[1] diff_location = Vector(( (location.x - bone_location.x), (location.y - bone_location.y), (location.z - bone_location.z), )) diff_location.rotate(bone_rotation.inverted()) return diff_location
def _gimbal_xyz(self, context, am): rotobj = (context.active_pose_bone if context.mode == 'POSE' else None) or context.active_object if (not rotobj) or (rotobj.rotation_mode == 'QUATERNION'): if not am: am = self.calc_active_matrix(context) xyz = am.col[:3] elif rotobj.rotation_mode == 'AXIS_ANGLE': aa = rotobj.rotation_axis_angle z = Vector(aa[1:]).normalized() q = Vector((0, 0, 1)).rotation_difference(z) x = (q * Vector((1, 0, 0))).normalized() y = z.cross(x) else: e = rotobj.rotation_euler m = Matrix.Identity(3) for e_ax in rotobj.rotation_mode: m = Matrix.Rotation(getattr(e, e_ax.lower()), 3, e_ax) * m x, y, z = m.col[:3] if not xyz: m = BlUtil.Object.matrix_parent(rotobj) x.rotate(m) y.rotate(m) z.rotate(m) xyz = x, y, z return xyz
def get_camera_up_vector(cam, rotation=None): with profiler.segment("get_camera_up_vector"): if rotation is None: rotation = get_object_quat(cam) vec = Vector((0, 1, 0)) vec.rotate(rotation) return vec
def shape_circle(context, orientation): center = context.scene.cursor_location active = context.active_object zed = active.location[2] base_dir = active.location.xy - center.xy if orientation == 'XY': zero_dir = get_xy(base_dir).resized(3) else: zero_dir = base_dir.xy.resized(3) num_objects = len(context.selected_objects) delta_angle = 2 * math.pi / num_objects # sort objects based on angle to center sorted_objects = sorted(context.selected_objects, key=lambda ob: get_angle(base_dir, ob, center)) for i in range(num_objects): angle = delta_angle * i euler = Euler((0, 0, -angle)) direction = Vector(zero_dir) direction.rotate(euler) sorted_objects[i].location = center + direction sorted_objects[i].location[2] = zed
def _bay_bow_vertical_filler_strip( corner_v: Vector, start_angle: float, angle: float, height: float, frame_th: float, shift=(0, 0, 0)) -> Tuple[list, list]: """ Create the triangular filler strip found between bay and bow windows. Return vertices and faces. :param corner_v: the location of the corner of the frame :param start_angle: the angle, measured from the +X axis, with positive being CCW of corner_v :param angle: how wide the angle is for the triangle :param height: the height of the strip :param frame_th: the thickness of the frame :param shift: Amount to add to all x, y, and z positions :return: the vertices and faces of the strip """ verts, faces = [], [] dx, dy, dz = shift v1 = Vector((frame_th, 0, 0)) v2 = v1.copy() v1.rotate(Euler((0, 0, start_angle))) v2.rotate(Euler((0, 0, start_angle + angle))) for x, y in (corner_v[:2], (corner_v + v1)[:2], (corner_v + v2)[:2]): verts.append((dx + x, dy + y, dz)) verts.append((dx + x, dy + y, dz + height)) faces.extend( ((0, 2, 3, 1), (2, 4, 5, 3), (4, 0, 1, 5), (1, 3, 5), (0, 4, 2))) return verts, faces
def tilt_rotate(angle): vec = Vector((0, 0, 1)) vec.rotate(Euler((angle, 0, 0), 'ZYX')) vec.rotate(Euler((0, 0, direction))) if simrot.relative_to_orbit: vec = orbit_rotate(vec, simorbit) return vec
def direction_rotate(angle): vec = Vector((0, 0, 1)) vec.rotate(Euler((tilt, 0, 0), 'ZYX')) vec.rotate(Euler((0, 0, angle))) if simrot.relative_to_orbit: vec = orbit_rotate(vec, simorbit) return vec - orbit_normal * cos(tilt)
def tilt_rotate(angle): vec = Vector((0, 0, 1)) vec.rotate(Euler((angle, 0, 0), 'ZYX')) vec.rotate(Euler((0, 0, direction))) if simrot.relative_to_orbit: vec = orbit_orientation(vec, simorbit) return vec
def create_mesh(self): direction = Vector((0, 0, 1)) direction.rotate(bpy.context.region_data.view_rotation) bm = self.bm normal = Vector() last_vector = None last_vert1 = None last_vert2 = None first_vert1 = None first_vert2 = None for point in self.stroke.points: vert1 = bm.verts.new((direction * -500) + point.co) vert2 = bm.verts.new((direction * 500) + point.co) if not first_vert1 and not first_vert2: first_vert1 = vert1 first_vert2 = vert2 if last_vert1 and last_vert2: bm.faces.new((last_vert1, last_vert2, vert2, vert1)) last_vert1 = vert1 last_vert2 = vert2 if self.ciclic and len(self.stroke.points) > 2: bm.faces.new((last_vert1, last_vert2, first_vert2, first_vert1)) bmesh.ops.recalc_face_normals(self.bm, faces=self.bm.faces)
def _gimbal_xyz(self, context, am): rotobj = (context.active_pose_bone if context.mode == 'POSE' else None) or context.active_object if (not rotobj) or (rotobj.rotation_mode == 'QUATERNION'): if not am: am = self.calc_active_matrix(context) xyz = am.col[:3] elif rotobj.rotation_mode == 'AXIS_ANGLE': aa = rotobj.rotation_axis_angle z = Vector(aa[1:]).normalized() q = Vector((0,0,1)).rotation_difference(z) x = (q * Vector((1,0,0))).normalized() y = z.cross(x) else: e = rotobj.rotation_euler m = Matrix.Identity(3) for e_ax in rotobj.rotation_mode: m = Matrix.Rotation(getattr(e, e_ax.lower()), 3, e_ax) * m x, y, z = m.col[:3] if not xyz: m = BlUtil.Object.matrix_parent(rotobj) x.rotate(m) y.rotate(m) z.rotate(m) xyz = x, y, z return xyz
def draw_callback_px(self, context): #context.object.location.x = self.value / 100.0 v3d = context.space_data rv3d = v3d.region_3d cursor_co = bpy.context.scene.cursor_location strokes = getVisibleStrokes() draw_plane_normal = rv3d.view_rotation vn = Vector((0, 0, 1)) vn.rotate(draw_plane_normal) view_dir = vn if (rv3d.is_perspective): view_dir = rv3d.view_location + rv3d.view_distance * view_dir - cursor_co for stroke in strokes: if (len(stroke) <= 1): continue for line in getLineFromPoints(stroke): coord = None subN = line[1] - line[0] t_N = -((line[0].x - cursor_co.x) * view_dir.x + (line[0].y - cursor_co.y) * view_dir.y + (line[0].z - cursor_co.z) * view_dir.z) t_D = subN.x * view_dir.x + subN.y * view_dir.y + subN.z * view_dir.z if (t_D == 0): coord = line[0] continue elif abs(t_N) < abs(t_D): t = t_N / t_D coord = line[0] + subN * t if (coord != None): pos = location_3d_to_region_2d(context.region, rv3d, coord) draw_text_in_view(pos)
def pointLight(): world = bpy.data.worlds['World'] world.use_nodes = True wnodes = world.node_tree.nodes bg_node = wnodes['Background'] bg_node.inputs[1].default_value = 0 d = random.uniform(3, 5) litpos = Vector(config["litpos"]) eul = Euler((0, 0, 0), 'XYZ') eul.rotate_axis('Z', config["litEulerZ"]) eul.rotate_axis('X', config["litEulerX"]) litpos.rotate(eul) bpy.ops.object.add(type='LIGHT', location=litpos) lamp = bpy.data.lights[0] lamp.use_nodes = True nodes = lamp.node_tree.nodes links = lamp.node_tree.links for node in nodes: if node.type == 'OUTPUT': output_node = node elif node.type == 'EMISSION': lamp_node = node strngth = config["litStr"] lamp_node.inputs[1].default_value = strngth # Change warmness of light to simulate more natural lighting bbody = nodes.new(type='ShaderNodeBlackbody') color_temp = config["litColorTemp"] bbody.inputs[0].default_value = color_temp links.new(bbody.outputs[0], lamp_node.inputs[0])
def execute(self, context): obs_in_editmode = context.objects_in_mode_unique_data bpy.ops.object.mode_set(mode='OBJECT') try: for o in obs_in_editmode: viewdir = Vector((0, 0, -1)) for area in context.screen.areas: if area.type != 'VIEW_3D': continue r3d = area.spaces[0].region_3d if r3d is None: continue viewdir.rotate(r3d.view_rotation) break # For every face normal, calculate the dot product # with the view direction nrmls = get_vecs(o.data.polygons, attr='normal') dotprdcs = np.dot(nrmls, np.array(viewdir)) # Select each face with dot product entry > 0 selflags = np.greater(dotprdcs, 0) o.data.polygons.foreach_set('select', selflags) finally: bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'}
def visible_face_from_vertex( vertex: bpy.types.MeshVertex, scene: bpy.types.Scene, mesh_object: bpy.types.Object) -> bpy.types.MeshPolygon: """Convert the vertex's coordinates into camera space, and check whether its coordinates are within the frustum. Then cast a ray at it to see whether it's occluded.""" cam = scene.camera cc = world_to_camera_view(scene, cam, mesh_object.matrix_world @ vertex.co) cs = cam.data.clip_start ce = cam.data.clip_end # If the vertex's screen coordinates are within camera view if 0.0 < cc.x < 1.0 and 0.0 < cc.y < 1.0 and cs < cc.z < ce: # Convert the screen coordinates to a 3D vector frame = cam.data.view_frame(scene=scene) top_left = frame[3] pixel_vector = Vector((cc.x, cc.y, top_left[2])) pixel_vector.rotate(cam.matrix_world.to_quaternion()) # Convert to target object space wmatrix_inv = mesh_object.matrix_world.inverted() origin = wmatrix_inv @ (pixel_vector + cam.matrix_world.translation) # Destination is the original vertex, in the same object space destination = wmatrix_inv @ vertex.co direction = (destination - origin).normalized() # Cast a ray from those screen coordinates to the vertex result, location, normal, index = mesh_object.ray_cast( origin, direction) if result and index > -1: # Return the face the vertex belongs to return mesh_object.data.polygons[index] return False
def direction_rotate(angle): vec = Vector((0, 0, 1)) vec.rotate(Euler((tilt, 0, 0), 'ZYX')) vec.rotate(Euler((0, 0, angle))) if simrot.relative_to_orbit: vec = orbit_orientation(vec, simorbit) return vec - orbit_normal * cos(tilt)
def updatePose(context, bone): sensor = context.scene.k_sensor for target in context.scene.kmc_props.targetBones: if target.value is not None and target.value == bone.name: # update bone pose head = sensor.getJoint(jointType[bonesDefinition[target.name][0]]) tail = sensor.getJoint(jointType[bonesDefinition[target.name][1]]) # axes matching X = 0 # inverted Y = 2 Z = 1 # update only tracked bones if(head[3] == 2) and (tail[3] == 2) : boneV = Vector((head[X] - tail[X], tail[Y] - head[Y], tail[Z] - head[Z])) # if first bone, update position (only for configured axes) if target.name == "Spine0": # initialize firstFramePosition if fit isn't if context.scene.kmc_props.firstFramePosition[1] == -1: context.scene.kmc_props.firstFramePosition = (-1.0*head[X], head[Y], head[Z]) ffp = context.scene.kmc_props.firstFramePosition tx = context.scene.kmc_props.initialOffset[0] ty = context.scene.kmc_props.initialOffset[2] tz = context.scene.kmc_props.initialOffset[1] if not context.scene.kmc_props.lockwidth: tx += -head[X] - ffp[0] if not context.scene.kmc_props.lockHeight: ty += head[Z] - ffp[2] if not context.scene.kmc_props.lockDepth: tz += head[Y] - ffp[1] # translate bone bone.matrix.translation = (tx, tz, ty) # convert rotation in local coordinates boneV = boneV * bone.matrix # compensate rest pose direction if target.name in restDirection : boneV.rotate(restDirection[target.name]) # calculate desired rotation rot = Vector((0,1,0)).rotation_difference(boneV) bone.rotation_quaternion = bone.rotation_quaternion * rot if context.scene.kmc_props.currentFrame == 0: # first captured frame, initiate recording by setting the current frame to 1 context.scene.kmc_props.currentFrame += 1 if context.scene.kmc_props.record : bone.keyframe_insert(data_path="rotation_quaternion", frame=context.scene.kmc_props.currentFrame) if target.name == "Spine0": bone.keyframe_insert(data_path="location", frame=context.scene.kmc_props.currentFrame) # update child bones for child in bone.children : updatePose(context, child)
def __mul__(self, other): t = Transform() v = Vector(other.translation) # dup v.rotate(self.rotation) t.translation = self.translation + v * self.scale t.rotation = self.rotation * other.rotation t.scale = self.scale * other.scale return t
def execute(self, context): print("Invoking Highlight Visible") # Check if a mesh object is selected try: bpy.ops.object.mode_set(mode='EDIT') except: self.report({'ERROR'}, "Must select a mesh object") return {'CANCELLED'} obj = bpy.context.edit_object me = obj.data # Get a BMesh representation mesh = bmesh.from_edit_mesh(me) # bm.from_mesh(me) mesh.faces.active = None cam = bpy.data.objects["Camera"] cam_rotation = cam.rotation_euler cam_dir = Vector((0, 0, 1)) cam_dir.rotate(cam_rotation) print("Camera direction:", cam_dir) try: color_layer = mesh.loops.layers.color['Col'] except: color_layer = mesh.loops.layers.color.new("Col") # NOTE: This does not yet correctly handle occluded geometry! for v in mesh.verts: color = (1, 1, 1, 1) p = world_to_camera_view(bpy.context.scene, cam, v.co) # Check if vertex is visible to camera if (0 < p.x < 1) and (0 < p.y < 1): # v.select = True # Check faces connected to vertex # NOTE: This is not ALL faces adjacent to a vertex. :-/ for face in v.link_faces: direction = face.normal - cam.location if direction.dot(face.normal) < 0: face.select = True color = (1, 0, 1, 1) for face in v.link_faces: # Color faces depending on dot product direction for loop in face.loops: loop[color_layer] = color # Show the updates in the viewport # and recalculate n-gon tessellation. bmesh.update_edit_mesh(me, True) bpy.ops.object.mode_set(mode='OBJECT') return {'FINISHED'}
def make_coil(self): # variables and shortnames amp = self.profile_radius n_verts = self.num_verts n_turns = self.num_turns th = self.height / n_turns ipt = self.iterations_per_turn radius = self.coil_radius diameter = radius * 2 two_pi = 2.0 * pi section_angle = two_pi / n_verts rad_slice = two_pi / ipt total_segments = (ipt * n_turns) + 1 z_jump = self.height / total_segments x_rotation = atan2(th / 2, diameter) n = n_verts Verts = [] for segment in range(total_segments): rad_angle = rad_slice * segment for i in range(n): # create the vector this_angle = section_angle * i x_float = amp * sin(this_angle) + radius z_float = amp * cos(this_angle) v1 = Vector((x_float, 0.0, z_float)) # rotate it xz_euler = Euler((-x_rotation, 0.0, -rad_angle), 'XYZ') v1.rotate(xz_euler) # add extra z height per segment v1 += Vector((0, 0, (segment * z_jump))) # append it Verts.append(v1) Faces = [] # skin it, normals facing outwards for t in range(total_segments-1): for i in range(n-1): p0 = i + (n*t) p1 = i + (n*t) + 1 p2 = i + (n*t + n) + 1 p3 = i + (n*t + n) Faces.append([p3,p2,p1,p0]) p0 = n*t p1 = n*t + n p2 = n*t + (2 * n) - 1 p3 = n*t + n-1 Faces.append([p3,p2,p1,p0]) return Verts, Faces
def transUvVector(self): max_position = 0. min_position_x = 0. min_position_y = 0. # calculate two rotation matrix from normal vector of selected polygons vector_nor = self.averageNormal() theta_x = self.calcRotAngle('X', vector_nor.x, vector_nor.y, vector_nor.z) mat_rotx = Matrix.Rotation(theta_x, 3, 'X') vector_nor.rotate(mat_rotx) theta_y = self.calcRotAngle('Y', vector_nor.x, vector_nor.y, vector_nor.z) mat_roty = Matrix.Rotation(theta_y, 3, 'Y') # apply two rotation matrix to vertex uv_array = self.mesh.uv_layers.active.data for poly in self.select_poly: for id in range(poly.loop_start, poly.loop_start + poly.loop_total): new_vector = Vector(( self.mesh.vertices[self.mesh.loops[id].vertex_index].co[0], self.mesh.vertices[self.mesh.loops[id].vertex_index].co[1], self.mesh.vertices[self.mesh.loops[id].vertex_index].co[2] )) new_vector.rotate(mat_rotx) new_vector.rotate(mat_roty) uv_array[id].uv = new_vector.to_2d() if min_position_x > uv_array[id].uv.x: min_position_x = uv_array[id].uv.x if min_position_y > uv_array[id].uv.y: min_position_y = uv_array[id].uv.y # recalculate uv position for poly in self.select_poly: for id in range(poly.loop_start, poly.loop_start + poly.loop_total): uv_array[id].uv.x = uv_array[id].uv.x + abs(min_position_x) uv_array[id].uv.y = uv_array[id].uv.y + abs(min_position_y) if max_position < uv_array[id].uv.x: max_position = uv_array[id].uv.x if max_position < uv_array[id].uv.y: max_position = uv_array[id].uv.y # scale uv position for poly in self.select_poly: for id in range(poly.loop_start, poly.loop_start + poly.loop_total): uv_array[ id].uv.x = uv_array[id].uv.x * MAX_LOCATION / max_position uv_array[ id].uv.y = uv_array[id].uv.y * MAX_LOCATION / max_position
def make_coil(self): # variables and shortnames amp = self.profile_radius n_verts = self.num_verts n_turns = self.num_turns th = self.height / n_turns ipt = self.iterations_per_turn radius = self.coil_radius diameter = radius * 2 two_pi = 2.0 * pi section_angle = two_pi / n_verts rad_slice = two_pi / ipt total_segments = (ipt * n_turns) + 1 z_jump = self.height / total_segments x_rotation = atan2(th / 2, diameter) n = n_verts Verts = [] for segment in range(total_segments): rad_angle = rad_slice * segment for i in range(n): # create the vector this_angle = section_angle * i x_float = amp * sin(this_angle) + radius z_float = amp * cos(this_angle) v1 = Vector((x_float, 0.0, z_float)) # rotate it xz_euler = Euler((-x_rotation, 0.0, -rad_angle), 'XYZ') v1.rotate(xz_euler) # add extra z height per segment v1 += Vector((0, 0, (segment * z_jump))) # append it Verts.append(v1) Faces = [] # skin it, normals facing outwards for t in range(total_segments - 1): for i in range(n - 1): p0 = i + (n * t) p1 = i + (n * t) + 1 p2 = i + (n * t + n) + 1 p3 = i + (n * t + n) Faces.append([p3, p2, p1, p0]) p0 = n * t p1 = n * t + n p2 = n * t + (2 * n) - 1 p3 = n * t + n - 1 Faces.append([p3, p2, p1, p0]) return Verts, Faces
def get_partial_pc(self, i): ''' Capture a partial point cloud (simulated depth camera) using ray casting: Adapted from https://blender.stackexchange.com/questions/115285/how-to-do-a-ray-cast-from-camera-originposition-to-object-in-scene-in-such-a-w''' # camera object which defines ray source cam = bpy.data.objects['Camera'] container = bpy.data.objects['Empty'] # save current view mode # mode = bpy.context.area.type # set view mode to 3D to have all needed variables available #bpy.context.area.type = "VIEW_3D" # get vectors which define view frustum of camera frame = cam.data.view_frame(scene=bpy.context.scene) topRight = frame[0] bottomRight = frame[2] bottomLeft = frame[2] topLeft = frame[3] label_dict = {'Landscape': 0, 'leaves': 1, 'stems_1': 2, 'trunk': 2} # setup vectors to match pixels xRange = np.linspace(topLeft[0], topRight[0], self.resolutionX) yRange = np.linspace(topLeft[1], bottomLeft[1], self.resolutionY) # array to store hit information values = np.empty((xRange.size, yRange.size, 3), dtype=object) labels = np.empty((xRange.size, yRange.size), dtype=object) # indices for array mapping indexX = 0 indexY = 0 # iterate over all X/Y coordinates for x in xRange: for y in yRange: # get current pixel vector from camera center to pixel pixelVector = Vector((x, y, topLeft[2])) # rotate that vector according to camera rotation pixelVector.rotate(cam.matrix_world.to_quaternion()) #pixelVector.rotate(container.matrix_world.to_quaternion()) #destination = matrixWorldInverted @ (pixelVector + cam.matrix_world.translation) #direction = (destination - origin).normalized() origin = cam.matrix_world.to_translation() # perform the actual ray casting hit, location, norm, face, hit_object, matrix = bpy.context.scene.ray_cast( bpy.context.view_layer, origin, pixelVector) if hit: values[indexX, indexY] = np.array(location) labels[indexX, indexY] = label_dict[hit_object.name.split('.')[0]] # update indices indexY += 1 indexX += 1 indexY = 0 return values, labels
def pan3dView(self, sv3d: bpy.types.SpaceView3D, rv3d: bpy.types.RegionView3D, delta: Vector): viewPos, rot, _viewDir = prepareCameraTransformation(sv3d, rv3d) yawRot = Quaternion(Vector((0, 0, 1)), -delta[0]) pitchAxis = Vector((1, 0, 0)) pitchAxis.rotate(rot) pitchRot = Quaternion(pitchAxis, delta[1]) rot.rotate(pitchRot) rot.rotate(yawRot) applyCameraTranformation(sv3d, rv3d, viewPos, rot)
def hitbox_location(data, shape, length): origin = length * (data.origin - 0.5) location = Vector((0.0, -origin * hitbox_scale_y(data, shape), 0.0)) location.rotate(data.rotation) location.y += origin - (length * 0.5) location += data.location return location
def rotation(self, xyz): xyz = Euler(xyz, 'XYZ') self._rotation = self.ProxyRotation(xyz) for line in self._lines: line.rotation = xyz length = (line.position - self.position).length pos = Vector([0,-length,0]) pos.rotate(xyz) line.position = pos + self.position
def execute(self, context): # FIXME: Undo is inconsistent. # FIXME: Would be nicer if rotate could pick some object-local axis. from mathutils import Vector print_3d = context.scene.print_3d face_areas = print_3d.use_alignxy_face_area self.context = context mode_orig = context.mode skip_invalid = [] for obj in context.selected_objects: orig_loc = obj.location.copy() orig_scale = obj.scale.copy() # When in edit mode, do as the edit mode does. if mode_orig == 'EDIT_MESH': bm = bmesh.from_edit_mesh(obj.data) faces = [f for f in bm.faces if f.select] else: faces = [p for p in obj.data.polygons if p.select] if not faces: skip_invalid.append(obj.name) continue # Rotate object so average normal of selected faces points down. normal = Vector((0.0, 0.0, 0.0)) if face_areas: for face in faces: normal += (face.normal * face.calc_area()) else: for face in faces: normal += face.normal normal = normal.normalized() normal.rotate(obj.matrix_world) # local -> world. offset = normal.rotation_difference(Vector((0.0, 0.0, -1.0))) offset = offset.to_matrix().to_4x4() obj.matrix_world = offset @ obj.matrix_world obj.scale = orig_scale obj.location = orig_loc if len(skip_invalid) > 0: for name in skip_invalid: print( f"Align to XY: Skipping object {name}. No faces selected.") if len(skip_invalid) == 1: self.report( {'WARNING'}, f"Skipping object {skip_invalid[0]}. No faces selected.") else: self.report( {'WARNING'}, f"Skipping some objects. No faces selected. See terminal.") return {'FINISHED'}
def rotation(self, xyz): xyz = Euler(xyz, 'XYZ') self._rotation = self.ProxyRotation(xyz) for line in self._lines: line.rotation = xyz length = (line.position - self.position).length pos = Vector([0, -length, 0]) pos.rotate(xyz) line.position = pos + self.position
def scale_along_global_z(factor, ref=None, point=None): obj = get_object(ref) axis = Vector((0.0, 0.0, 1.0)) temp = obj.rotation_euler.copy() temp.x *= -1 temp.y *= -1 temp.z *= -1 axis.rotate(temp) scale_along_axis(factor, axis, ref, point)
def to_3d(vec, co=None, rot=None): """Take a 2D vector (or the XY of a 3D vector) and transform it according to co and rot.""" newvec = Vector((vec[0], vec[1], 0)) if rot: newvec.rotate(rot) if co: newvec += co return newvec
def get_point_in_circle(max_radius): # Take vector pointing along X axis with random length less than radius radius = random.uniform(0, max_radius) vec = Vector((radius, 0.0, 0.0)) # Rotate it random amount (0-360 degrees) along Z axis angle_degrees = random.uniform(0, 360) rot = Matrix.Rotation(radians(angle_degrees), 3, "Z") vec.rotate(rot) return vec
def add_lighting(): world=bpy.data.worlds['World'] world.use_nodes = True wnodes=world.node_tree.nodes wlinks=world.node_tree.links bg_node=wnodes['Background'] # hdr lighting # remove old node for node in wnodes: if node.type in ['OUTPUT_WORLD', 'BACKGROUND']: continue else: wnodes.remove(node) # hdr world lighting if random.random() > 0.3: texcoord = wnodes.new(type='ShaderNodeTexCoord') mapping = wnodes.new(type='ShaderNodeMapping') # print( 'mapping : ', mapping.rotation[2]) mapping.rotation[2] = random.uniform(0, 6.28) wlinks.new(texcoord.outputs[0], mapping.inputs[0]) envnode=wnodes.new(type='ShaderNodeTexEnvironment') wlinks.new(mapping.outputs[0], envnode.inputs[0]) idx = random.randint(0, len(envlist) - 1) envp = envlist[idx] envnode.image = bpy.data.images.load(envp[0]) envstr = int(envp[1]) bg_node.inputs[1].default_value=random.uniform(0.4 * envstr, 0.6 * envstr) wlinks.new(envnode.outputs[0], bg_node.inputs[0]) else: # point light bg_node.inputs[1].default_value=0 d = random.uniform(3, 5) litpos = Vector((0, d, 0)) eul = Euler((0, 0, 0), 'XYZ') eul.rotate_axis('Z', random.uniform(0, 3.1415)) eul.rotate_axis('X', random.uniform(math.radians(45), math.radians(135))) litpos.rotate(eul) bpy.ops.object.add(type='LIGHT', location=litpos) lamp = bpy.data.lights[0] lamp.use_nodes = True nodes=lamp.node_tree.nodes links=lamp.node_tree.links for node in nodes: if node.type=='OUTPUT': output_node=node elif node.type=='EMISSION': lamp_node=node strngth=random.uniform(200,500) lamp_node.inputs[1].default_value=strngth #Change warmness of light to simulate more natural lighting bbody=nodes.new(type='ShaderNodeBlackbody') color_temp=random.uniform(2700,10200) bbody.inputs[0].default_value=color_temp links.new(bbody.outputs[0],lamp_node.inputs[0])
def make_coil(): # variables amp = prad th = height / n_turns ipt = n_iter radius = crad diameter = radius * 2 section_angle = 360.0 / n_verts rad_slice = 2.0 * pi / ipt total_segments = (ipt * n_turns) + 1 z_jump = height / total_segments x_rotation = atan2(th / 2, diameter) n = n_verts Verts = [] for segment in range(total_segments): rad_angle = rad_slice * segment for i in range(n): # create the vector this_angle = section_angle * i x_float = amp * sin(radians(this_angle)) + radius z_float = amp * cos(radians(this_angle)) v1 = Vector((x_float, 0.0, z_float)) # rotate it some_euler = Euler((-x_rotation, 0.0, -rad_angle), 'XYZ') v1.rotate(some_euler) # add extra z height per segment v1 += Vector((0, 0, (segment * z_jump))) # append it Verts.append(v1.to_tuple()) Faces = [] for t in range(total_segments - 1): for i in range(n - 1): p0 = i + (n * t) p1 = i + (n * t) + 1 p2 = i + (n * t + n) + 1 p3 = i + (n * t + n) Faces.append([p0, p1, p2, p3]) p0 = n * t p1 = n * t + n p2 = n * t + (2 * n) - 1 p3 = n * t + n - 1 Faces.append([p0, p1, p2, p3]) return Verts, Faces
def mirrorPlane(vertex, matrix): vert = [] a = Matrix(matrix) eul = a.to_euler() normal = Vector((0.0, 0.0, 1.0)) normal.rotate(eul) tras = Matrix.Translation(2*a.to_translation()) for i in vertex: v = Vector(i) r = v.reflect(normal) vert.append((tras*r)[:]) return vert
def sv_main(n_petals=8, vp_petal=20, profile_radius=1.3, amp=1.0): in_sockets = [ ['s', 'Num Petals', n_petals], ['s', 'Verts per Petal', vp_petal], ['s', 'Profile Radius', profile_radius], ['s', 'Amp', amp], ] from math import sin, cos, radians, pi from mathutils import Vector, Euler # variables z_float = 0.0 n_verts = n_petals * vp_petal section_angle = 360.0 / n_verts position = (2 * (pi / (n_verts / n_petals))) # consumables Verts = [] Edges = [] # makes vertex coordinates for i in range(n_verts): # difference is a function of the position on the circumference difference = amp * cos(i * position) arm = profile_radius + difference ampline = Vector((arm, 0.0, 0.0)) rad_angle = radians(section_angle * i) myEuler = Euler((0.0, 0.0, rad_angle), 'XYZ') # changes the vector in place, successive calls are accumulative # we reset at the start of the loop. ampline.rotate(myEuler) x_float = ampline.x y_float = ampline.y Verts.append((x_float, y_float, z_float)) # makes edge keys for i in range(n_verts): if i == n_verts - 1: Edges.append([i, 0]) break Edges.append([i, i + 1]) out_sockets = [ ['v', 'Verts', [Verts]], ['s', 'Edges', [Edges]], ] return in_sockets, out_sockets
class Turtle: def __init__(self, position=(0, 0, 0),direction =(0,0,1), orientation=(0, 1, 0),axe_rotation = (0,0,1), vitesse=1, angle=90,imperfection = 0.2): self.position = Vector(position) self.direction = Vector(direction).normalized() self.orientation = Vector(orientation).normalized() self.vitesse = vitesse self.angle = radians(angle) self.memoireEtat = [] self.comportement_initialisation() self.imperfection = imperfection def comportement_initialisation(self): self.comportements = { '+':self.comportement_plus, '-':self.comportement_moins, 'F':self.comportement_F, '[':self.comportement_save_etat, ']':self.comportement_restor_etat } def comportement_F(self): p_debut = self.position.copy() self.position += self.direction * self.vitesse dx = (random() - 0.5) * self.imperfection dy = (random() - 0.5) * self.imperfection dz = (random() - 0.5) * self.imperfection self.direction += Vector((dx,dy,dz)) p_fin = self.position.copy() return Section(debut = p_debut,fin = p_fin) def comportement_save_etat(self): etat = (self.position.copy(), self.direction.copy(), self.vitesse, self.angle) self.memoireEtat.append(etat) def comportement_restor_etat(self): (self.position, self.direction, self.vitesse, self.angle) = self.memoireEtat.pop() def comportement_plus(self): rotation = Matrix.Rotation(self.angle,4,self.orientation) self.direction.rotate(rotation) def comportement_moins(self): rotation = Matrix.Rotation(- self.angle, 4,self.orientation) self.direction.rotate(rotation) def interpretation(self,s): for char in s: comportement = self.comportements[char]() if char in self.comportements else None yield comportement
def transUvVector(self): max_position = 0. min_position_x = 0. min_position_y = 0. # calculate two rotation matrix from normal vector of selected polygons vector_nor = self.averageNormal() theta_x = self.calcRotAngle('X', vector_nor.x, vector_nor.y, vector_nor.z) mat_rotx = Matrix.Rotation(theta_x, 3, 'X') vector_nor.rotate(mat_rotx) theta_y = self.calcRotAngle('Y', vector_nor.x, vector_nor.y, vector_nor.z) mat_roty = Matrix.Rotation(theta_y, 3, 'Y') # apply two rotation matrix to vertex uv_array = self.mesh.uv_layers.active.data for poly in self.select_poly: for id in range(poly.loop_start, poly.loop_start + poly.loop_total): new_vector = Vector((self.mesh.vertices[self.mesh.loops[id].vertex_index].co[0], self.mesh.vertices[self.mesh.loops[id].vertex_index].co[1], self.mesh.vertices[self.mesh.loops[id].vertex_index].co[2])) new_vector.rotate(mat_rotx) new_vector.rotate(mat_roty) uv_array[id].uv = new_vector.to_2d() if min_position_x > uv_array[id].uv.x: min_position_x = uv_array[id].uv.x if min_position_y > uv_array[id].uv.y: min_position_y = uv_array[id].uv.y # recalculate uv position for poly in self.select_poly: for id in range(poly.loop_start, poly.loop_start + poly.loop_total): uv_array[id].uv.x = uv_array[id].uv.x + abs(min_position_x) uv_array[id].uv.y = uv_array[id].uv.y + abs(min_position_y) if max_position < uv_array[id].uv.x: max_position = uv_array[id].uv.x if max_position < uv_array[id].uv.y: max_position = uv_array[id].uv.y # scale uv position for poly in self.select_poly: for id in range(poly.loop_start, poly.loop_start + poly.loop_total): uv_array[id].uv.x = uv_array[id].uv.x * MAX_LOCATION / max_position uv_array[id].uv.y = uv_array[id].uv.y * MAX_LOCATION / max_position
def setupStartPos(self): """Creates the starting position information""" player = bpy.data.objects["Player"] viewDir = Vector((0.0, 0.0, -1.0)) viewDir.rotate(player.rotation_euler) upDir = Vector((0.0, 1.0, 0.0)) upDir.rotate(player.rotation_euler) start = { } start['position'] = { 'x': ConvertHelper.convert_pos(player.location.x), 'y': ConvertHelper.convert_pos(player.location.y), 'z': ConvertHelper.convert_pos(player.location.z) } start['viewDirection'] = { 'x': ConvertHelper.convert_pos(viewDir.x), 'y': ConvertHelper.convert_pos(viewDir.y), 'z': ConvertHelper.convert_pos(viewDir.z) } start['upDirection'] = { 'x': ConvertHelper.convert_pos(upDir.x), 'y': ConvertHelper.convert_pos(upDir.y), 'z': ConvertHelper.convert_pos(upDir.z) } return start
def setupEnvironments(self): """Sets up the environment with the different light sources""" environments = { } lighting = { } pointLights = [ ] dirLights = [ ] storeEnv = False storeLighting = False for lamp in bpy.data.lamps: if "Ambient" == lamp.name: storeEnv = True environments['ambient'] = [ ConvertHelper.convert_pos(lamp.color.r), ConvertHelper.convert_pos(lamp.color.g), ConvertHelper.convert_pos(lamp.color.b), 1.0 ] if "AmbientLight" == lamp.name: storeLighting = True lighting['ambientLight'] = [ ConvertHelper.convert_pos(lamp.color.r), ConvertHelper.convert_pos(lamp.color.g), ConvertHelper.convert_pos(lamp.color.b), 1.0 ] if "Point" in lamp.name: storeLighting = True lightObject = bpy.data.objects[lamp.name] pointLight = { } pointLight['color'] = [ ConvertHelper.convert_pos(lamp.color.r), ConvertHelper.convert_pos(lamp.color.g), ConvertHelper.convert_pos(lamp.color.b), 1.0 ] pointLight['position'] = [ ConvertHelper.convert_pos(lightObject.location.x), ConvertHelper.convert_pos(lightObject.location.y), ConvertHelper.convert_pos(lightObject.location.z) ] pointLight['intensity'] = lamp.energy pointLights.append(pointLight) if "Spot" in lamp.name: storeLighting = True lightObject = bpy.data.objects[lamp.name] dirLight = { } dirLight['color'] = [ ConvertHelper.convert_pos(lamp.color.r), ConvertHelper.convert_pos(lamp.color.g), ConvertHelper.convert_pos(lamp.color.b), 1.0 ] dir = Vector((0.0, 0.0, -1.0)) dir.rotate(lightObject.rotation_euler) dirLight['direction'] = [ ConvertHelper.convert_pos(dir.x), ConvertHelper.convert_pos(dir.y), ConvertHelper.convert_pos(dir.z),] dirLights.append(dirLight) if storeLighting: lighting['pointLights'] = pointLights lighting['directionalLights'] = dirLights environments['lighting'] = lighting if storeEnv: self.data['environments'] = environments
def draw_circle(point, orientation, size, fraction=1.0, steps=36, colour=[1, 0, 0]): last_point = None shift = (2 * pi / steps) for step in range(int(steps / fraction) + 1): index = step * shift x = sin(index) * size z = cos(index) * size step_point = Vector((x, 0, z)) step_point.rotate(orientation) step_point += point if last_point: render.drawLine(last_point, step_point, colour) last_point = step_point
class Turtle: def __init__(self, position=(0, 0, 0), orientation=(1, 0, 0), vitesse=1, angle=radians(90)): self.position = Vector(position) self.orientation = Vector(orientation).normalized() self.vitesse = vitesse self.angle = angle self.memoireEtat = [] self.comportement_initialisation() def comportement_initialisation(self): self.comportements = { '+':self.comportement_plus, '-':self.comportement_moins, 'F':self.comportement_F, '[':self.comportement_save_etat, ']':self.comportement_restor_etat } def comportement_F(self): p_debut = self.position.copy() self.position += self.orientation * self.vitesse p_fin = self.position.copy() return Section(debut = p_debut,fin = p_fin) def comportement_save_etat(self): etat = (self.position.copy(), self.orientation.copy(), self.vitesse, self.angle) self.memoireEtat.append(etat) def comportement_restor_etat(self): (self.position, self.orientation, self.vitesse, self.angle) = self.memoireEtat.pop() def comportement_plus(self): rotation = Matrix.Rotation(self.angle,4,(0,1,0)) self.orientation.rotate(rotation) def comportement_moins(self): rotation = Matrix.Rotation(- self.angle, 4,(0,1,0)) self.orientation.rotate(rotation) def interpretation(self,s): for char in s: comportement = self.comportements[char]() if char in self.comportements else None yield comportement
def onKeyPressed(self, keys): rot = self.obj.worldOrientation.to_euler() pos = Vector([0,0,0]) if key.W in keys: rot.x += 0.01 if key.S in keys: rot.x -= 0.01 if key.A in keys: rot.z += 0.01 if key.D in keys: rot.z -= 0.01 if key.WHEELUPMOUSE in keys: pos.z = -self.obj.worldPosition.z * 0.3 if key.WHEELDOWNMOUSE in keys: pos.z = self.obj.worldPosition.z * 0.3 #Max speed is dependent of the Tile sizes, ex (200m/s = size) / 50fps = 4m/tick #Since we are using an extra radius we can guarante a speed of 8m/tick without glitches: 8*60fps = 480m/s = 1728 km/h #if pos.length > 8: pos.length = 8 #But we don't care for now if pos.length > 50: pos.length = 50 pos.rotate(self.obj.worldOrientation) self.obj.worldPosition += pos self.obj.worldOrientation = rot
def orientation_rotation(context, orientation: str, view: str) -> list: rotations = [] if orientation == "vertical": if view == "top": rotations.append(Quaternion((0.0, 0.0, 1.0), radians(90.0))) elif view == "right": rotations.append(Quaternion((1.0, 0.0, 0.0), radians(90.0))) elif view == "front": rotations.append(Quaternion((0.0, 1.0, 0.0), radians(90.0))) elif view == "align": space_data = context.space_data if space_data and space_data.type != 'VIEW_3D': space_data = None if space_data: to_user_view = space_data.region_3d.view_rotation z_axis = Vector((0.0, 0.0, 1.0)) z_axis.rotate(to_user_view) rotation_in_user_view = Quaternion(z_axis, radians(90.0)) rotations.append(rotation_in_user_view) return rotations
def copy(self, text = None, offset = [0,0,0]): """ Retuns a copy of the label :param string text: The text of the new Label or None to copy the current text. :param offset: A vector representing the desplacment to be applied on the new Label from this Label position in local coordinates. """ if text == None: text = self._text size = self.scale.x * 100 offset = Vector(offset) offset.rotate(self.rotation) label = Label(self.font, text, size, self.align, self.position + offset, self.rotation) label.color = self.color label.visible = self.visible label.blur = self.blur label.shadow = self.shadow label.shadow_blur = self.shadow_blur label.shadow_color = self.shadow_color label.shadow_offset = self.shadow_offset return label
def shape_square(context, orientation): center = context.scene.cursor_location active = context.active_object zed = active.location[2] base_dir = active.location.xy - center.xy diagonal = base_dir.length if orientation == 'XY': zero_dir = get_xy_corner(base_dir).resized(3) else: zero_dir = base_dir.xy.resized(3) num_objects = len(context.selected_objects) num_side_objects = (num_objects // 4) - 1 ortho_angle = math.pi * 0.5 ortho_dir = Vector(zero_dir) ortho_dir.rotate(Euler((0, 0, ortho_angle * 0.5))) ortho_dir.normalize() # sort objects based on angle to center sorted_objects = sorted(context.selected_objects, key=lambda ob: get_angle(base_dir, ob, center)) corners = [] for i in range(0, num_objects, num_side_objects + 1): corners.append(sorted_objects[i]) assert(len(corners) == 4) sides = [ob for ob in sorted_objects if ob not in corners] side_step = math.sqrt(2 * (diagonal ** 2)) / (num_side_objects + 1) for i in range(4): # corner angle = ortho_angle * i euler = Euler((0, 0, -angle)) direction = Vector(zero_dir) direction.rotate(euler) corners[i].location = center + direction corners[i].location[2] = zed side_dir = Vector(ortho_dir) side_dir.rotate(euler) for j in range(num_side_objects): ob = sides[(i * num_side_objects) + j] step = (j + 1) * side_step ob.location = center + direction ob.location.xy -= side_dir.xy * step ob.location[2] = zed
def vectorsFromMatrix(matrix): rot = matrix.to_euler("XYZ") # re-add 90° on x-axis rot.x=math.radians(math.degrees(rot.x)+90) vx = Vector((1.0,0.0,0.0)) vy = Vector((0.0,1.0,0.0)) vz = Vector((0.0,0.0,1.0)) vx.rotate(rot) vy.rotate(rot) vz.rotate(rot) vectors = (vx,vy,vz) return vectors
def draw_arrow(point, orientation, length=1.5, branch_length=0.4, angle=30, colour=[1, 0, 0]): left = Vector((sin(radians(angle)), -cos(radians(angle)), 0)) right = Vector((-sin(radians(angle)), -cos(radians(angle)), 0)) left.rotate(orientation) right.rotate(orientation) left.length = branch_length right.length = branch_length direction = Vector((0, 1, 0)) * length direction.rotate(orientation) render.drawLine(point, point + direction, colour) render.drawLine(point + direction, point + direction + right, colour) render.drawLine(point + direction, point + direction + left, colour)
def updateMesh(self, context): o = context.object material_list = getMaterialList(o) if o.pattern == 'Regular': nplanks = (o.width + o.originy) / o.plankwidth verts, faces, uvs = planks(nplanks, o.length + o.originx, o.planklength, o.planklengthvar, o.plankwidth, o.plankwidthvar, o.longgap, o.shortgap, o.offset, o.randomoffset, o.minoffset, o.randomseed, o.randrotx, o.randroty, o.randrotz, o.originx, o.originy) elif o.pattern == 'Herringbone': # note that there is a lot of extra length and width here to make sure that we create a pattern w.o. gaps at the edges v = o.plankwidth * sqrt(2.0) w = o.planklength * sqrt(2.0) nplanks = int((o.width+o.planklength + o.originy*2) / v)+1 nplanksc = int((o.length + o.originx*2) / w)+1 verts, faces, uvs = herringbone(nplanks, nplanksc, o.planklength, o.plankwidth, o.longgap, o.shortgap, o.randomseed, o.randrotx, o.randroty, o.randrotz, o.originx, o.originy) elif o.pattern == 'Square': rows = int((o.width + o.originy)/ o.planklength)+1 cols = int((o.length + o.originx)/ o.planklength)+1 verts, faces, uvs = square(rows, cols, o.planklength, o.nsquare, o.border, o.longgap, o.shortgap, o.randomseed, o.randrotx, o.randroty, o.randrotz, o.originx, o.originy) elif o.pattern == 'Versaille': rows = int((o.width + o.originy)/ o.planklength)+2 cols = int((o.length + o.originx)/ o.planklength)+2 verts, faces, uvs = versaille(rows, cols, o.planklength, o.plankwidth, o.longgap, o.shortgap, o.randrotx, o.randroty, o.randrotz, o.originx, o.originy, o.borderswitch) # create mesh &link object to scene emesh = o.data mesh = bpy.data.meshes.new(name='Planks') mesh.from_pydata(verts, [], faces) mesh.update(calc_edges=True) # more than one object can refer to the same emesh for i in bpy.data.objects: if i.data == emesh: i.data = mesh name = emesh.name emesh.user_clear() # this way the old mesh is marked as used by noone and not saved on exit bpy.data.meshes.remove(emesh) mesh.name = name if bpy.context.mode != 'EDIT_MESH': bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() bpy.ops.object.shade_smooth() # add uv-coords and per face random vertex colors rot = Euler((0,0,o.uvrotation)) mesh.uv_textures.new() uv_layer = mesh.uv_layers.active.data vertex_colors = mesh.vertex_colors.new().data offset = Vector() # note that the uvs that are returned are shuffled for poly in mesh.polygons: color = [rand(), rand(), rand()] if o.randomuv == 'Random': offset = Vector((rand(), rand(), 0)) if o.randomuv == 'Restricted': offset = Vector((rand()*2-1, rand()*2-1, 0)) for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total): co = offset + mesh.vertices[mesh.loops[loop_index].vertex_index].co if co.x > o.length or co.x < 0: offset[0] = 0 if co.y > o.width or co.y < 0: offset[1] = 0 elif o.randomuv == 'Packed': x = [] y = [] for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total): x.append(uvs[mesh.loops[loop_index].vertex_index].x) y.append(uvs[mesh.loops[loop_index].vertex_index].y) offset = Vector((-min(x), -min(y), 0)) for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total): if o.randomuv == 'Shuffle': coords = uvs[mesh.loops[loop_index].vertex_index] elif o.randomuv in ('Random', 'Restricted'): coords = mesh.vertices[mesh.loops[loop_index].vertex_index].co + offset elif o.randomuv == 'Packed': coords = uvs[mesh.loops[loop_index].vertex_index] + offset else: coords = mesh.vertices[mesh.loops[loop_index].vertex_index].co coords = Vector(coords) # copy coords.x *= o.uvscalex coords.y *= o.uvscaley coords.rotate(rot) uv_layer[loop_index].uv = coords.xy vertex_colors[loop_index].color = color # subdivide mesh and warp it warped = o.hollowlong > 0 or o.hollowshort > 0 or o.twist > 0 if warped: bm = bmesh.new() bm.from_mesh(mesh) # calculate hollowness for each face dshortmap = {} dlongmap = {} for face in bm.faces: dshort = o.hollowshort * rand() dlong = o.hollowlong * rand() for v in face.verts: dshortmap[v.index] = dshort dlongmap[v.index] = dlong bm.to_mesh(mesh) bm.free() # at this point all new geometry is selected and subdivide works in all selection modes bpy.ops.object.editmode_toggle() bpy.ops.mesh.subdivide() # bmesh subdivide doesn't work for me ... bpy.ops.object.editmode_toggle() bm = bmesh.new() bm.from_mesh(mesh) for v in bm.verts: if o.twist and len(v.link_edges) == 4: # vertex in the middle of the plank dtwist = o.twist * randuni(-1, 1) for e in v.link_edges: v2 = e.other_vert(v) # the vertices on the side of the plank if shortside(v2): for e2 in v2.link_edges: v3 = e2.other_vert(v2) if len(v3.link_edges) == 2: v3.co.z += dtwist dtwist = -dtwist # one corner up, the other corner down elif len(v.link_edges) == 3: # vertex in the middle of a side of the plank for e in v.link_edges: v2 = e.other_vert(v) if len(v2.link_edges) == 2: # hollowness values are stored with the all original corner vertices dshort = dshortmap[v2.index] dlong = dlongmap[v2.index] break if shortside(v): v.co.z -= dlong else: v.co.z -= dshort creases = bm.edges.layers.crease.new() for edge in bm.edges: edge[creases] = 1 for vert in edge.verts: if len(vert.link_edges) == 4: edge[creases] = 0 break bm.to_mesh(mesh) bm.free() # remove all modifiers to make sure the boolean will be last & only modifier n = len(o.modifiers) while n > 0: n -= 1 bpy.ops.object.modifier_remove(modifier=o.modifiers[-1].name) # add thickness bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(o.data) # extrude to give thickness ret=bmesh.ops.extrude_face_region(bm,geom=bm.faces[:]) ret=bmesh.ops.translate(bm,vec=Vector((0,0,self.thickness)),verts=[el for el in ret['geom'] if isinstance(el, bmesh.types.BMVert)] ) # trim excess flooring ret = bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(o.length,0,0), plane_no=(1,0,0), clear_outer=True) ret = bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0,0,0), plane_no=(-1,0,0), clear_outer=True) ret = bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0,o.width,0), plane_no=(0,1,0), clear_outer=True) ret = bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0,0,0), plane_no=(0,-1,0), clear_outer=True) # fill in holes caused by the trimming open_edges = [e for e in bm.edges if len(e.link_faces)==1] bmesh.ops.edgeloop_fill(bm, edges=open_edges, mat_nr=0, use_smooth=False) creases = bm.edges.layers.crease.active if creases is not None: for edge in open_edges: edge[creases] = 1 bmesh.update_edit_mesh(o.data) bpy.ops.object.mode_set(mode='OBJECT') # intersect with a floorplan. Note the floorplan must be 2D (all z-coords must be identical) and a closed polygon. if self.usefloorplan and self.floorplan != ' None ': # make the floorplan the only active an selected object bpy.ops.object.select_all(action='DESELECT') context.scene.objects.active = bpy.data.objects[self.floorplan] bpy.data.objects[self.floorplan].select = True # duplicate the selected geometry into a separate object me = context.scene.objects.active.data selected_faces = [p.index for p in me.polygons if p.select] bpy.ops.object.editmode_toggle() bpy.ops.mesh.duplicate() bpy.ops.mesh.separate() bpy.ops.object.editmode_toggle() me = context.scene.objects.active.data for i in selected_faces: me.polygons[i].select = True # now there will be two selected objects # the one with the new name will be the copy for ob in context.selected_objects: if ob.name != self.floorplan: fpob = ob # make that copy active and selected for ob in context.selected_objects: ob.select = False fpob.select = True context.scene.objects.active = fpob # add thickness # let normals of select faces point in same direction bpy.ops.object.editmode_toggle() bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.normals_make_consistent(inside=False) bpy.ops.object.editmode_toggle() # add solidify modifier # NOTE: for some reason bpy.ops.object.modifier_add doesn't work here # even though fpob at this point is verifyable the active and selected object ... mod = fpob.modifiers.new(name='Solidify', type='SOLIDIFY') mod.offset = 1.0 # in the direction of the normals mod.thickness = 2000 # very thick bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Solidify") bpy.ops.object.editmode_toggle() bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.normals_make_consistent(inside=False) bpy.ops.object.editmode_toggle() fpob.location -= Vector((0,0,1000)) # actually this should be in the negative direction of the normals not just plain downward... # at this point the floorplan object is the active and selected object if True: # make the floorboards active and selected for ob in context.selected_objects: ob.select = False context.scene.objects.active = o o.select = True # add-and-apply a boolean modifier to get the intersection with the floorplan copy bpy.ops.object.modifier_add(type='BOOLEAN') # default is intersect o.modifiers[-1].object = fpob if True: bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean") # delete the copy bpy.ops.object.select_all(action='DESELECT') context.scene.objects.active = fpob fpob.select = True bpy.ops.object.delete() # make the floorboards active and selected context.scene.objects.active = o o.select = True if self.modify: mods = o.modifiers if len(mods) == 0: # always true bpy.ops.object.modifier_add(type='BEVEL') #bpy.ops.object.modifier_add(type='EDGE_SPLIT') mods = o.modifiers mods[0].show_expanded = False #mods[1].show_expanded = False mods[0].width = self.bevel mods[0].segments = 2 mods[0].limit_method = 'ANGLE' mods[0].angle_limit = (85/90.0)*PI/2 if warped and not ('SUBSURF' in [m.type for m in mods]): bpy.ops.object.modifier_add(type='SUBSURF') mods[-1].show_expanded = False mods[-1].levels = 2 if not warped and ('SUBSURF' in [m.type for m in mods]): bpy.ops.object.modifier_remove(modifier='Subsurf') if self.preservemats and len(material_list)>0: rebuildMaterialList(o, material_list) assignRandomMaterial(len(material_list))
from math import sin, cos, radians, pi from mathutils import Vector, Euler # create flower n_verts = n_petals * vp_petal section_angle = 360.0 / n_verts position = (2 * (pi / (n_verts / n_petals))) x, y, z = origin[:3] Verts = [] Edges = [] for i in range(n_verts): difference = amp * cos(i * position) arm = profile_radius + difference ampline = Vector((arm, 0.0, 0.0)) rad_angle = radians(section_angle * i) myEuler = Euler((0.0, 0.0, rad_angle), 'XYZ') ampline.rotate(myEuler) Verts.append((ampline.x+x, ampline.y+y, 0.0+z)) # makes edge keys, ensure cyclic if Verts: i = 0 Edges.extend([[i, i + 1] for i in range(n_verts - 1)]) Edges.append([len(Edges), 0]) verts = [Verts] edges = [Edges]
def createIvyGeometry(IVY, growLeaves): '''Create the curve geometry for IVY''' # Compute the local size and the gauss weight filter #local_ivyBranchSize = IVY.ivyBranchSize # * radius * IVY.ivySize gaussWeight = (1.0, 2.0, 4.0, 7.0, 9.0, 10.0, 9.0, 7.0, 4.0, 2.0, 1.0) # Create a new curve and intialise it curve = bpy.data.curves.new("IVY", type='CURVE') curve.dimensions = '3D' curve.bevel_depth = 1 curve.use_fill_front = curve.use_fill_back = False curve.resolution_u = 4 if growLeaves: # Create the ivy leaves # Order location of the vertices signList = ((-1.0, +1.0), (+1.0, +1.0), (+1.0, -1.0), (-1.0, -1.0), ) # Get the local size #local_ivyLeafSize = IVY.ivyLeafSize # * radius * IVY.ivySize # Initialise the vertex and face lists vertList = deque() # Store the methods for faster calling addV = vertList.extend rotMat = Matrix.Rotation # Loop over all roots to generate its nodes for root in IVY.ivyRoots: # Only grow if more than one node numNodes = len(root.ivyNodes) if numNodes > 1: # Calculate the local radius local_ivyBranchRadius = 1.0 / (root.parents + 1) + 1.0 prevIvyLength = 1.0 / root.ivyNodes[-1].length splineVerts = [ax for n in root.ivyNodes for ax in n.pos.to_4d()] radiusConstant = local_ivyBranchRadius * IVY.ivyBranchSize splineRadii = [radiusConstant * (1.3 - n.length * prevIvyLength) for n in root.ivyNodes] # Add the poly curve and set coords and radii newSpline = curve.splines.new(type='POLY') newSpline.points.add(len(splineVerts) // 4 - 1) newSpline.points.foreach_set('co', splineVerts) newSpline.points.foreach_set('radius', splineRadii) # Loop over all nodes in the root for i, n in enumerate(root.ivyNodes): for k in range(len(gaussWeight)): idx = max(0, min(i + k - 5, numNodes - 1)) n.smoothAdhesionVector += (gaussWeight[k] * root.ivyNodes[idx].adhesionVector) n.smoothAdhesionVector /= 56.0 n.adhesionLength = n.smoothAdhesionVector.length n.smoothAdhesionVector.normalize() if growLeaves and (i < numNodes - 1): node = root.ivyNodes[i] nodeNext = root.ivyNodes[i + 1] # Find the weight and normalise the smooth adhesion vector weight = pow(node.length * prevIvyLength, 0.7) # Calculate the ground ivy and the new weight groundIvy = max(0.0, -node.smoothAdhesionVector.z) weight += groundIvy * pow(1 - node.length * prevIvyLength, 2) # Find the alignment weight alignmentWeight = node.adhesionLength # Calculate the needed angles phi = atan2(node.smoothAdhesionVector.y, node.smoothAdhesionVector.x) - pi / 2.0 theta = (0.5 * node.smoothAdhesionVector.angle(Vector((0, 0, -1)), 0)) # Find the size weight sizeWeight = 1.5 - (cos(2 * pi * weight) * 0.5 + 0.5) # Randomise the angles phi += (rand_val() - 0.5) * (1.3 - alignmentWeight) theta += (rand_val() - 0.5) * (1.1 - alignmentWeight) # Calculate the leaf size an append the face to the list leafSize = IVY.ivyLeafSize * sizeWeight for j in range(10): # Generate the probability probability = rand_val() # If we need to grow a leaf, do so if (probability * weight) > IVY.leafProbability: # Generate the random vector randomVector = Vector((rand_val() - 0.5, rand_val() - 0.5, rand_val() - 0.5, )) # Find the leaf center center = (node.pos.lerp(nodeNext.pos, j / 10.0) + IVY.ivyLeafSize * randomVector) # For each of the verts, rotate/scale and append basisVecX = Vector((1, 0, 0)) basisVecY = Vector((0, 1, 0)) horiRot = rotMat(theta, 3, 'X') vertRot = rotMat(phi, 3, 'Z') basisVecX.rotate(horiRot) basisVecY.rotate(horiRot) basisVecX.rotate(vertRot) basisVecY.rotate(vertRot) basisVecX *= leafSize basisVecY *= leafSize addV([k1 * basisVecX + k2 * basisVecY + center for k1, k2 in signList]) # Add the object and link to scene newCurve = bpy.data.objects.new("IVY_Curve", curve) bpy.context.scene.objects.link(newCurve) if growLeaves: faceList = [[4 * i + l for l in range(4)] for i in range(len(vertList) // 4)] # Generate the new leaf mesh and link me = bpy.data.meshes.new('IvyLeaf') me.from_pydata(vertList, [], faceList) me.update(calc_edges=True) ob = bpy.data.objects.new('IvyLeaf', me) bpy.context.scene.objects.link(ob) tex = me.uv_textures.new("Leaves") # Set the uv texture coords # TODO, this is non-functional, default uvs are ok? ''' for d in tex.data: uv1, uv2, uv3, uv4 = signList ''' ob.parent = newCurve
def write_leveldata(context, filepath, overwrite_existing, clean): print("running write_leveldata...") #prepare axes xx = 0 yx = 2 zx = 1 xm = +1 ym = +1 zm = -1 #activate object mode and deselect all objects if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set( mode = 'OBJECT' ) bpy.ops.object.select_all( action = 'DESELECT' ) #apend convenience methods setattr( xml.dom.minidom.Element, 'appendElement', appendElement ) setattr( xml.dom.minidom.Element, 'appendTextElement', appendTextElement ) #create xml document doc = xml.dom.minidom.Document() rootElem = doc.createElement( 'Level' ) doc.appendChild( rootElem ) directory = os.path.dirname( filepath ) + '/' if clean: for file in os.listdir( directory ): if re.search( '.*\.bmd$|.*.\.xml$|.*\.png$', file ) != None: os.remove( directory + file ) #add data to dom for ob in bpy.context.scene.objects: if ob.type == 'MESH': if 'noexport' in ob.keys(): continue elif 'player' in ob.keys(): #player start elem = rootElem.appendElement( doc, 'Player' ) pos = elem.appendElement( doc, 'StartPosition' ) pos.appendTextElement( doc, 'X', str( ob.location[xx] * xm ) ) pos.appendTextElement( doc, 'Y', str( ob.location[yx] * ym ) ) pos.appendTextElement( doc, 'Z', str( ob.location[zx] * zm ) ) elif 'direction' in ob.keys(): elem = rootElem.appendElement( doc, 'DirectionChanger' ) pos = elem.appendElement( doc, 'Position' ) pos.appendTextElement( doc, 'X', str( ob.location[xx] * xm ) ) pos.appendTextElement( doc, 'Y', str( ob.location[yx] * ym ) ) pos.appendTextElement( doc, 'Z', str( ob.location[zx] * zm ) ) d = Vector() d[xx] = 0; d[yx] = 0; d[zx] = 1; d.rotate( ob.rotation_euler ) direction = elem.appendElement( doc, 'Direction' ) direction.appendTextElement( doc, 'X', str( d[xx] * xm ) ) direction.appendTextElement( doc, 'Y', str( d[yx] * ym ) ) direction.appendTextElement( doc, 'Z', str( d[zx] * zm ) ) elem.appendTextElement( doc, 'Radius', str( ob.dimensions[0] / 2 ) ) mode = 'Fixed' if ob.id_data['direction'] == 'free': mode = 'Free' elem.appendTextElement( doc, 'Mode', mode ) else: #level geometry name = ob.name if len( ob.users_group ) > 0: if( ob.users_group[0].name == 'NoCollision' ): if len( ob.users_group ) > 1: name = ob.users_group[1].name else: name = ob.users_group[0].name if overwrite_existing or not os.path.exists( directory + name + '.bmd' ): bpy.ops.object.select_name( name = ob.name ) bpy.ops.object.mode_set( mode = 'OBJECT' ) bpy.ops.export_mesh.bmd( filepath = directory + name + '.bmd' ) bpy.ops.object.select_all( action = 'DESELECT' ) elem = rootElem.appendElement( doc, 'Mesh' ) elem.appendTextElement( doc, 'File', name + '.bmd' ) pos = elem.appendElement( doc, 'Position' ) pos.appendTextElement( doc, 'X', str( ob.location[xx] * xm ) ) pos.appendTextElement( doc, 'Y', str( ob.location[yx] * ym ) ) pos.appendTextElement( doc, 'Z', str( ob.location[zx] * zm ) ) rot = elem.appendElement( doc, 'Rotation' ) rot.appendTextElement( doc, 'X', str( ob.rotation_euler[xx] * xm ) ) rot.appendTextElement( doc, 'Y', str( ob.rotation_euler[yx] * ym ) ) rot.appendTextElement( doc, 'Z', str( ob.rotation_euler[zx] * zm ) ) noCollision = 'False' for group in ob.users_group: if group.name == 'NoCollision': noCollision = 'True' elem.appendTextElement( doc, 'NoCollision', noCollision ) elif ob.type == 'CURVE': if ob.data.splines[0].type == 'BEZIER' and 'enemy' in ob.keys(): #enemy paths elem = rootElem.appendElement( doc, 'Enemy' ) elem.appendTextElement( doc, 'Name', ob.id_data['enemy'] ) hp = 3 if 'hitpoints' in ob.keys(): hp = ob.id_data['hitpoints'] elem.appendTextElement( doc, 'Hitpoints', str( hp ) ) elem.appendTextElement( doc, 'Looping', str( ob.data.splines[0].use_cyclic_u ) ) numNodes = len( ob.data.splines[0].bezier_points ) - 1 for index in range( 0, numNodes ): node = ob.data.splines[0].bezier_points[index] nx = ob.location[xx] * xm ny = ob.location[yx] * ym nz = ob.location[zx] * zm point = elem.appendElement( doc, 'PathNode' ) point.appendTextElement( doc, 'X', str( node.co[xx] * xm + nx ) ) point.appendTextElement( doc, 'Y', str( node.co[yx] * ym + ny ) ) point.appendTextElement( doc, 'Z', str( node.co[zx] * zm + nz) ) point = elem.appendElement( doc, 'PathNode' ) point.appendTextElement( doc, 'X', str( node.handle_right[xx] * xm + nx ) ) point.appendTextElement( doc, 'Y', str( node.handle_right[yx] * ym + ny ) ) point.appendTextElement( doc, 'Z', str( node.handle_right[zx] * zm + nz ) ) node = ob.data.splines[0].bezier_points[index + 1] point = elem.appendElement( doc, 'PathNode' ) point.appendTextElement( doc, 'X', str( node.handle_left[xx] * xm + nx ) ) point.appendTextElement( doc, 'Y', str( node.handle_left[yx] * ym + ny ) ) point.appendTextElement( doc, 'Z', str( node.handle_left[zx] * zm + nz ) ) point = elem.appendElement( doc, 'PathNode' ) point.appendTextElement( doc, 'X', str( node.co[xx] * xm + nx ) ) point.appendTextElement( doc, 'Y', str( node.co[yx] * ym + ny ) ) point.appendTextElement( doc, 'Z', str( node.co[zx] * zm + nz ) ) #write xml file = open( filepath, 'w+' ) file.write( doc.toprettyxml() ) #file.write( doc.toxml() ) file.close() return {'FINISHED'}
def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Furthermore, primitives are split up, if the indices range is exceeded. Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) use_tangents = False if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console('WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.') # material_map = {} # # Gathering position, normal and tex_coords. # no_material_attributes = { POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: [] } if use_tangents: no_material_attributes[TANGENT_ATTRIBUTE] = [] # # Directory of materials with its primitive. # no_material_primitives = { MATERIAL_ID: '', INDICES_ID: [], ATTRIBUTES_ID: no_material_attributes } material_name_to_primitives = {'': no_material_primitives} # vertex_index_to_new_indices = {} material_map[''] = vertex_index_to_new_indices # # Create primitive for each material. # for blender_material in blender_mesh.materials: if blender_material is None: continue attributes = { POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: [] } if use_tangents: attributes[TANGENT_ATTRIBUTE] = [] primitive = { MATERIAL_ID: blender_material.name, INDICES_ID: [], ATTRIBUTES_ID: attributes } material_name_to_primitives[blender_material.name] = primitive # vertex_index_to_new_indices = {} material_map[blender_material.name] = vertex_index_to_new_indices tex_coord_max = 0 if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) # vertex_colors = {} color_index = 0 for vertex_color in blender_mesh.vertex_colors: vertex_color_name = COLOR_PREFIX + str(color_index) vertex_colors[vertex_color_name] = vertex_color color_index += 1 if color_index >= GLTF_MAX_COLORS: break color_max = color_index # bone_max = 0 for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index bones_count = len(blender_mesh.vertices[vertex_index].groups) if bones_count > 0: if bones_count % 4 == 0: bones_count -= 1 bone_max = max(bone_max, bones_count // 4 + 1) # morph_max = 0 blender_shape_keys = [] if blender_mesh.shape_keys is not None: morph_max = len(blender_mesh.shape_keys.key_blocks) - 1 for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key != blender_shape_key.relative_key: blender_shape_keys.append(ShapeKey( blender_shape_key, blender_shape_key.normals_vertex_get(), # calculate vertex normals for this shape key blender_shape_key.normals_polygon_get())) # calculate polygon normals for this shape key # # Convert polygon to primitive indices and eliminate invalid ones. Assign to material. # for blender_polygon in blender_mesh.polygons: export_color = True # if blender_polygon.material_index < 0 or blender_polygon.material_index >= len(blender_mesh.materials) or \ blender_mesh.materials[blender_polygon.material_index] is None: primitive = material_name_to_primitives[''] vertex_index_to_new_indices = material_map[''] else: primitive = material_name_to_primitives[blender_mesh.materials[blender_polygon.material_index].name] vertex_index_to_new_indices = material_map[blender_mesh.materials[blender_polygon.material_index].name] # attributes = primitive[ATTRIBUTES_ID] face_normal = blender_polygon.normal face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) if use_tangents: for loop_index in blender_polygon.loop_indices: temp_vertex = blender_mesh.loops[loop_index] face_tangent += temp_vertex.tangent face_bitangent += temp_vertex.bitangent face_tangent.normalize() face_bitangent.normalize() # indices = primitive[INDICES_ID] loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline,)) for triangle in triangles: loop_index_list.append(blender_polygon.loop_indices[triangle[0]]) loop_index_list.append(blender_polygon.loop_indices[triangle[2]]) loop_index_list.append(blender_polygon.loop_indices[triangle[1]]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index if vertex_index_to_new_indices.get(vertex_index) is None: vertex_index_to_new_indices[vertex_index] = [] # v = None n = None t = None b = None uvs = [] colors = [] joints = [] weights = [] target_positions = [] target_normals = [] target_tangents = [] vertex = blender_mesh.vertices[vertex_index] v = convert_swizzle_location(vertex.co, export_settings) if blender_polygon.use_smooth: if blender_mesh.has_custom_normals: n = convert_swizzle_location(blender_mesh.loops[loop_index].normal, export_settings) else: n = convert_swizzle_location(vertex.normal, export_settings) if use_tangents: t = convert_swizzle_tangent(blender_mesh.loops[loop_index].tangent, export_settings) b = convert_swizzle_location(blender_mesh.loops[loop_index].bitangent, export_settings) else: n = convert_swizzle_location(face_normal, export_settings) if use_tangents: t = convert_swizzle_tangent(face_tangent, export_settings) b = convert_swizzle_location(face_bitangent, export_settings) if use_tangents: tv = Vector((t[0], t[1], t[2])) bv = Vector((b[0], b[1], b[2])) nv = Vector((n[0], n[1], n[2])) if (nv.cross(tv)).dot(bv) < 0.0: t[3] = -1.0 if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[loop_index].uv uvs.append([uv.x, 1.0 - uv.y]) # if color_max > 0 and export_color: for color_index in range(0, color_max): color_name = COLOR_PREFIX + str(color_index) color = vertex_colors[color_name].data[loop_index].color colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), 1.0 ]) # bone_count = 0 if blender_vertex_groups is not None and vertex.groups is not None and len(vertex.groups) > 0 and export_settings[gltf2_blender_export_keys.SKINS]: joint = [] weight = [] vertex_groups = vertex.groups if not export_settings['gltf_all_vertex_influences']: # sort groups by weight descending vertex_groups = sorted(vertex.groups, key=attrgetter('weight'), reverse=True) for group_element in vertex_groups: if len(joint) == 4: bone_count += 1 joints.append(joint) weights.append(weight) joint = [] weight = [] # joint_weight = group_element.weight if joint_weight <= 0.0: continue # vertex_group_index = group_element.group vertex_group_name = blender_vertex_groups[vertex_group_index].name joint_index = None if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: modifier = modifiers_dict["ARMATURE"] armature = modifier.object if armature: skin = gltf2_blender_gather_skins.gather_skin(armature, modifier.id_data, export_settings) for index, j in enumerate(skin.joints): if j.name == vertex_group_name: joint_index = index break # if joint_index is not None: joint.append(joint_index) weight.append(joint_weight) if len(joint) > 0: bone_count += 1 for fill in range(0, 4 - len(joint)): joint.append(0) weight.append(0.0) joints.append(joint) weights.append(weight) for fill in range(0, bone_max - bone_count): joints.append([0, 0, 0, 0]) weights.append([0.0, 0.0, 0.0, 0.0]) # if morph_max > 0 and export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): blender_shape_key = blender_shape_keys[morph_index] v_morph = convert_swizzle_location(blender_shape_key.shape_key.data[vertex_index].co, export_settings) # Store delta. v_morph -= v target_positions.append(v_morph) # n_morph = None if blender_polygon.use_smooth: temp_normals = blender_shape_key.vertex_normals n_morph = (temp_normals[vertex_index * 3 + 0], temp_normals[vertex_index * 3 + 1], temp_normals[vertex_index * 3 + 2]) else: temp_normals = blender_shape_key.polygon_normals n_morph = ( temp_normals[blender_polygon.index * 3 + 0], temp_normals[blender_polygon.index * 3 + 1], temp_normals[blender_polygon.index * 3 + 2]) n_morph = convert_swizzle_location(n_morph, export_settings) # Store delta. n_morph -= n target_normals.append(n_morph) # if use_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector((t[0], t[1], t[2])) t_morph.rotate(rotation) target_tangents.append(t_morph) # # create = True for current_new_index in vertex_index_to_new_indices[vertex_index]: found = True for i in range(0, 3): if attributes[POSITION_ATTRIBUTE][current_new_index * 3 + i] != v[i]: found = False break if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 + i] != n[i]: found = False break if use_tangents: for i in range(0, 4): if attributes[TANGENT_ATTRIBUTE][current_new_index * 4 + i] != t[i]: found = False break if not found: continue for tex_coord_index in range(0, tex_coord_max): uv = uvs[tex_coord_index] tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) for i in range(0, 2): if attributes[tex_coord_id][current_new_index * 2 + i] != uv[i]: found = False break if export_color: for color_index in range(0, color_max): color = colors[color_index] color_id = COLOR_PREFIX + str(color_index) for i in range(0, 3): # Alpha is always 1.0 - see above. current_color = attributes[color_id][current_new_index * 4 + i] if color_srgb_to_scene_linear(current_color) != color[i]: found = False break if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint = joints[bone_index] weight = weights[bone_index] joint_id = JOINTS_PREFIX + str(bone_index) weight_id = WEIGHTS_PREFIX + str(bone_index) for i in range(0, 4): if attributes[joint_id][current_new_index * 4 + i] != joint[i]: found = False break if attributes[weight_id][current_new_index * 4 + i] != weight[i]: found = False break if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position = target_positions[morph_index] target_normal = target_normals[morph_index] if use_tangents: target_tangent = target_tangents[morph_index] target_position_id = MORPH_POSITION_PREFIX + str(morph_index) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) target_tangent_id = MORPH_TANGENT_PREFIX + str(morph_index) for i in range(0, 3): if attributes[target_position_id][current_new_index * 3 + i] != target_position[i]: found = False break if attributes[target_normal_id][current_new_index * 3 + i] != target_normal[i]: found = False break if use_tangents: if attributes[target_tangent_id][current_new_index * 3 + i] != target_tangent[i]: found = False break if found: indices.append(current_new_index) create = False break if not create: continue new_index = 0 if primitive.get('max_index') is not None: new_index = primitive['max_index'] + 1 primitive['max_index'] = new_index vertex_index_to_new_indices[vertex_index].append(new_index) # # indices.append(new_index) # attributes[POSITION_ATTRIBUTE].extend(v) attributes[NORMAL_ATTRIBUTE].extend(n) if use_tangents: attributes[TANGENT_ATTRIBUTE].extend(t) if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) if attributes.get(tex_coord_id) is None: attributes[tex_coord_id] = [] attributes[tex_coord_id].extend(uvs[tex_coord_index]) if export_color: for color_index in range(0, color_max): color_id = COLOR_PREFIX + str(color_index) if attributes.get(color_id) is None: attributes[color_id] = [] attributes[color_id].extend(colors[color_index]) if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint_id = JOINTS_PREFIX + str(bone_index) if attributes.get(joint_id) is None: attributes[joint_id] = [] attributes[joint_id].extend(joints[bone_index]) weight_id = WEIGHTS_PREFIX + str(bone_index) if attributes.get(weight_id) is None: attributes[weight_id] = [] attributes[weight_id].extend(weights[bone_index]) if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position_id = MORPH_POSITION_PREFIX + str(morph_index) if attributes.get(target_position_id) is None: attributes[target_position_id] = [] attributes[target_position_id].extend(target_positions[morph_index]) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) if attributes.get(target_normal_id) is None: attributes[target_normal_id] = [] attributes[target_normal_id].extend(target_normals[morph_index]) if use_tangents: target_tangent_id = MORPH_TANGENT_PREFIX + str(morph_index) if attributes.get(target_tangent_id) is None: attributes[target_tangent_id] = [] attributes[target_tangent_id].extend(target_tangents[morph_index]) # # Add primitive plus split them if needed. # result_primitives = [] for material_name, primitive in material_name_to_primitives.items(): export_color = True # indices = primitive[INDICES_ID] if len(indices) == 0: continue position = primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE] normal = primitive[ATTRIBUTES_ID][NORMAL_ATTRIBUTE] if use_tangents: tangent = primitive[ATTRIBUTES_ID][TANGENT_ATTRIBUTE] tex_coords = [] for tex_coord_index in range(0, tex_coord_max): tex_coords.append(primitive[ATTRIBUTES_ID][TEXCOORD_PREFIX + str(tex_coord_index)]) colors = [] if export_color: for color_index in range(0, color_max): tex_coords.append(primitive[ATTRIBUTES_ID][COLOR_PREFIX + str(color_index)]) joints = [] weights = [] if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joints.append(primitive[ATTRIBUTES_ID][JOINTS_PREFIX + str(bone_index)]) weights.append(primitive[ATTRIBUTES_ID][WEIGHTS_PREFIX + str(bone_index)]) target_positions = [] target_normals = [] target_tangents = [] if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_positions.append(primitive[ATTRIBUTES_ID][MORPH_POSITION_PREFIX + str(morph_index)]) target_normals.append(primitive[ATTRIBUTES_ID][MORPH_NORMAL_PREFIX + str(morph_index)]) if use_tangents: target_tangents.append(primitive[ATTRIBUTES_ID][MORPH_TANGENT_PREFIX + str(morph_index)]) # count = len(indices) if count == 0: continue max_index = max(indices) # # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed. # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index. # https://github.com/KhronosGroup/glTF/issues/1142 # https://github.com/KhronosGroup/glTF/pull/1476/files range_indices = 65535 # if max_index >= range_indices: # # Splitting result_primitives. # # At start, all indices are pending. pending_attributes = { POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: [] } if use_tangents: pending_attributes[TANGENT_ATTRIBUTE] = [] pending_primitive = { MATERIAL_ID: material_name, INDICES_ID: [], ATTRIBUTES_ID: pending_attributes } pending_primitive[INDICES_ID].extend(indices) pending_attributes[POSITION_ATTRIBUTE].extend(position) pending_attributes[NORMAL_ATTRIBUTE].extend(normal) if use_tangents: pending_attributes[TANGENT_ATTRIBUTE].extend(tangent) tex_coord_index = 0 for tex_coord in tex_coords: pending_attributes[TEXCOORD_PREFIX + str(tex_coord_index)] = tex_coord tex_coord_index += 1 if export_color: color_index = 0 for color in colors: pending_attributes[COLOR_PREFIX + str(color_index)] = color color_index += 1 if export_settings[gltf2_blender_export_keys.SKINS]: joint_index = 0 for joint in joints: pending_attributes[JOINTS_PREFIX + str(joint_index)] = joint joint_index += 1 weight_index = 0 for weight in weights: pending_attributes[WEIGHTS_PREFIX + str(weight_index)] = weight weight_index += 1 if export_settings[gltf2_blender_export_keys.MORPH]: morph_index = 0 for target_position in target_positions: pending_attributes[MORPH_POSITION_PREFIX + str(morph_index)] = target_position morph_index += 1 morph_index = 0 for target_normal in target_normals: pending_attributes[MORPH_NORMAL_PREFIX + str(morph_index)] = target_normal morph_index += 1 if use_tangents: morph_index = 0 for target_tangent in target_tangents: pending_attributes[MORPH_TANGENT_PREFIX + str(morph_index)] = target_tangent morph_index += 1 pending_indices = pending_primitive[INDICES_ID] # Continue until all are processed. while len(pending_indices) > 0: process_indices = pending_primitive[INDICES_ID] max_index = max(process_indices) pending_indices = [] # # all_local_indices = [] for i in range(0, (max_index // range_indices) + 1): all_local_indices.append([]) # # # For all faces ... for face_index in range(0, len(process_indices), 3): written = False face_min_index = min(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) face_max_index = max(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) # ... check if it can be but in a range of maximum indices. for i in range(0, (max_index // range_indices) + 1): offset = i * range_indices # Yes, so store the primitive with its indices. if face_min_index >= offset and face_max_index < offset + range_indices: all_local_indices[i].extend( [process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]]) written = True break # If not written, the triangle face has indices from different ranges. if not written: pending_indices.extend([process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]]) # Only add result_primitives, which do have indices in it. for local_indices in all_local_indices: if len(local_indices) > 0: current_primitive = extract_primitive_floor(pending_primitive, local_indices, use_tangents) result_primitives.append(current_primitive) print_console('DEBUG', 'Adding primitive with splitting. Indices: ' + str( len(current_primitive[INDICES_ID])) + ' Vertices: ' + str( len(current_primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3)) # Process primitive faces having indices in several ranges. if len(pending_indices) > 0: pending_primitive = extract_primitive_pack(pending_primitive, pending_indices, use_tangents) print_console('DEBUG', 'Creating temporary primitive for splitting') else: # # No splitting needed. # result_primitives.append(primitive) print_console('DEBUG', 'Adding primitive without splitting. Indices: ' + str( len(primitive[INDICES_ID])) + ' Vertices: ' + str( len(primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3)) print_console('INFO', 'Primitives created: ' + str(len(result_primitives))) return result_primitives
class Turtle(object): def __init__(self, tropism=(0, 0, 0), tropismsize=0, pitch_angle=radians(30), yaw_angle=radians(30), roll_angle=radians(30), radius=0.2, iseed=42): self.tropism = Vector(tropism).normalized() self.magnitude = tropismsize self.forward = Vector((1, 0, 0)) self.up = Vector((0, 0, 1)) self.right = self.forward.cross(self.up) self.stack = [] self.stack_curly = [] self.position = Vector((0, 0, 0)) self.pitch_angle = pitch_angle self.yaw_angle = yaw_angle self.roll_angle = roll_angle self.radius = radius self.__init_terminals() seed(iseed) def __init_terminals(self): """ Initialize a map of predefined terminals. """ self.terminals = { '+': self.term_plus, '-': self.term_minus, '[': self.term_push, ']': self.term_pop, '(': self.term_push_curly, ')': self.term_pop_curly, '/': self.term_slash, '\\': self.term_backslash, '<': self.term_less, '>': self.term_greater, '&': self.term_amp, '!': self.term_expand, '@': self.term_shrink, '#': self.term_fatten, '%': self.term_slink, '^': self.term_expand_g, '*': self.term_shrink_g, '=': self.term_fatten_g, '|': self.term_slink_g, 'F': self.term_edge, 'Q': self.term_quad, # '{': self.term_object } def apply_tropism(self): # tropism is a normalized vector t = self.tropism * self.magnitude tf = self.forward + t tf.normalize() q = tf.rotation_difference(self.forward) self.forward.rotate(q) self.up.rotate(q) self.right.rotate(q) def term_plus(self, value=None): val = radians(value) if not value is None else self.pitch_angle r = Matrix.Rotation(val, 4, self.right) self.forward.rotate(r) self.up.rotate(r) def term_minus(self, value=None): val = radians(value) if not value is None else self.pitch_angle r = Matrix.Rotation(-val, 4, self.right) self.forward.rotate(r) self.up.rotate(r) def term_amp(self, value=30): k = (random() - 0.5) * value self.term_plus(value=k) k = (random() - 0.5) * value self.term_slash(value=k) def term_slash(self, value=None): r = Matrix.Rotation(radians(value) if not value is None else self.yaw_angle, 4, self.up) self.forward.rotate(r) self.right.rotate(r) def term_backslash(self, value=None): r = Matrix.Rotation(-radians(value) if not value is None else -self.yaw_angle, 4, self.up) self.forward.rotate(r) self.right.rotate(r) def term_less(self, value=None): r = Matrix.Rotation(radians(value) if not value is None else self.roll_angle, 4, self.forward) self.up.rotate(r) self.right.rotate(r) def term_greater(self, value=None): r = Matrix.Rotation(-radians(value) if not value is None else -self.roll_angle, 4, self.forward) self.up.rotate(r) self.right.rotate(r) def term_pop(self, value=None): t = self.stack.pop() (self.forward, self.up, self.right, self.position, self.radius) = t def term_push(self, value=None): t = (self.forward.copy(), self.up.copy(), self.right.copy(), self.position.copy(), self.radius) self.stack.append(t) def term_pop_curly(self, value=None): t = self.stack_curly.pop() (self.forward, self.up, self.right, self.position, self.radius) = t def term_push_curly(self, value=None): t = (self.forward.copy(), self.up.copy(), self.right.copy(), self.position.copy(), self.radius) self.stack_curly.append(t) expand_shrink_factor = 0.1 fatten_slink_factor = 0.045 expand_shrink_factor_g = 0.2 fatten_slink_factor_g = 0.48 def term_expand(self, value=1 + expand_shrink_factor): self.forward *= value self.up *= value self.right *= value def term_shrink(self, value=1 - expand_shrink_factor): self.forward *= value self.up *= value self.right *= value def term_fatten(self, value=1 + fatten_slink_factor): self.radius *= value def term_slink(self, value=1 - fatten_slink_factor): self.radius *= value def term_expand_g(self, value=1 + expand_shrink_factor_g): self.term_expand(value) def term_shrink_g(self, value=1 - expand_shrink_factor_g): self.term_shrink(value) def term_fatten_g(self, value=1 + fatten_slink_factor_g): self.term_fatten(value) def term_slink_g(self, value=1 - fatten_slink_factor_g): self.term_slink(value) def term_edge(self, value=None): s = self.position.copy() self.apply_tropism() self.position += self.forward e = self.position.copy() return Edge(start=s, end=e, radius=self.radius) def term_quad(self, value=0.5): return Quad(pos=self.position, right=self.right, up=self.up, forward=self.forward) def term_object(self, value=None, name=None): s = self.position.copy() self.apply_tropism() self.position += self.forward return BObject(name=name, pos=s, right=self.right, up=self.up, forward=self.forward) def interpret(self, s): """ interpret the iterable s, yield Quad, Edge or Object named tuples. """ print('interpret:', s) name = '' for c in s: t = None #print(c,name) if c == '}': t = self.term_object(name=name[1:]) name = '' elif c == '{' or name != '': name += c continue elif name != '': continue elif c in self.terminals: t = self.terminals[c]() #print('yield',t) if not t is None: yield t