def run(self, lastFrame=0): """ Main render loop for this OpenGL window """ while not glfw.window_should_close(self.win): # clear draw buffer and depth buffer (<-TP2) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) currentFrame = glfw.get_time() deltaTime = currentFrame - lastFrame lastFrame = currentFrame for d in self.drawables: if isinstance(d, ParticleGenerator): d.update(dt=deltaTime) winsize = glfw.get_window_size(self.win) view = self.trackball.view_matrix() projection = self.trackball.projection_matrix(winsize) # draw our scene objects for drawable in self.drawables: drawable.draw( projection, view, identity(), color_shader=self.color_shader, win=self.win, texture_shader_skybox=self.texture_shader_skybox, texture_shader_particle=self.texture_shader_particle) # flush render commands, and swap draw buffers glfw.swap_buffers(self.win) # Poll for and process events glfw.poll_events()
def run(self): """ Main render loop for this OpenGL window """ while not glfw.window_should_close(self.win): # clear draw buffer GL.glClear(GL.GL_COLOR_BUFFER_BIT) # clear depth buffer GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) # self.angle += 0.1 winsize = glfw.get_window_size(self.win) view = self.trackball.view_matrix() projection = self.trackball.projection_matrix(winsize) # draw our scene objects for drawable in self.drawables: # drawable.draw(None, None, None, self.color_shader, self.color, self.angle) drawable.draw(projection, view, identity(), self.color_shader, self.color) # flush render commands, and swap draw buffers glfw.swap_buffers(self.win) # Poll for and process events glfw.poll_events()
def construct_forest(self, transform=identity()): for _ in range(350): self.children.extend([ add_object(self.trees[randint(0, 4)], transform=transform @ translate( randint(0, 10000), randint(0, 4000), 0) @ scale( randint(60, 100))) ])
def main(): """ create a window, add scene objects, then run rendering loop """ viewer = Viewer() # place instances of our basic objects # phi, theta, psi = 30, 20, 40 # # construct our robot arm hierarchy for drawing in viewer cylinder = Cylinder(40) # re-use same cylinder instance limb_shape = Node(transform=scale(1 / 4, 1 / 4, 5)) # make a thin cylinder limb_shape.add(cylinder) # common shape of arm and forearm rotate2 = RotationControlNode(glfw.KEY_E, glfw.KEY_D, (1, 0, 0)) rotate2.add(limb_shape) forearm_node = Node(transform=translate(0, 0, 5 - 1 / 4) @ rotate( (1, 0, 0), psi)) # robot arm rotation with phi angle forearm_node.add(rotate2) arm_node = Node(transform=translate(0, 0, 0.5) @ rotate( (1, 0, 0), phi)) # robot arm rotation with phi angle arm_node.add(limb_shape, forearm_node) rotate1 = RotationControlNode(glfw.KEY_F, glfw.KEY_S, (1, 0, 0)) rotate1.add(arm_node) # make a flat cylinder base_shape = Node(transform=identity(), children=[cylinder]) # viewer.add(base_node) viewer.add(TexturedPlane("control/arrows.png")) # viewer.add(Cylinder(200)) translate_keys = {0: vec(0, 0, 0), 2: vec(1, 1, 0), 4: vec(0, 0, 0)} rotate_keys = { 0: quaternion(), 2: quaternion_from_euler(180, 45, 90), 3: quaternion_from_euler(180, 0, 180), 4: quaternion() } scale_keys = {0: 1, 2: 0.5, 4: 1} # keynode = KeyFrameControlNode(translate_keys, rotate_keys, scale_keys) base_node = KeyFrameControlNode(translate_keys, rotate_keys, scale_keys) base_node.add(base_shape, rotate1) viewer.add(base_node) # meshes = load_textured("cube/cube/cube.obj") # meshes = load("suzanne.obj") # for m in meshes: # keynode.add(m) # viewer.add(keynode) # start rendering loop viewer.run()
def __init__(self, *keys, **kwargs): super().__init__(**kwargs) self.keyframes = TransformKeyFrames(*keys) if keys[0] else None self.world_transform = identity() # Local time self.time = glfw.get_time() # Accelerate the time self.acceleration = 1 self.duration = 1.02
def main(): """ create a window, add scene objects, then run rendering loop """ viewer = Viewer() # paramètre de transformation des paramètres #sol ground_size = 512 ground_offset = 20 #dinosaure characters_offset_x = 0 characters_offset_y = -20 characters_offset_z = 0 characters_scale = 15 characters_rotate_deg = 180 #forêt forest_offset = -15 forest_scale = 1.5 #skybox Skysphere_scale = 3 characters = Node( transform=translate(characters_offset_x, characters_offset_y, characters_offset_z) @ scale(characters_scale) @ rotate(axis=(0, 1, 0), angle=characters_rotate_deg)) characters.add(*load_skinned("dino/Dinosaurus_roar.dae")) forest = Node( transform=translate(0, forest_offset, 0) @ scale(forest_scale)) forest.add(*load_textured("trees9/forest.obj")) ground = Node(transform=translate(-ground_size >> 1, ground_offset, -ground_size >> 1)) ground.add(sol(ground_size)) Skysphere = Node(transform=scale(Skysphere_scale)) Skysphere.add(*load_textured("Skysphere/skysphere.obj")) scene = Node(transform=identity(), children=[characters, forest, ground, Skysphere]) viewer.add(scene) viewer.run()
def run(self): """ Main render loop for this OpenGL window """ while not glfw.window_should_close(self.win): # clear draw buffer and depth buffer (<-TP2) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) win_size = glfw.get_window_size(self.win) view = self.trackball.view_matrix() projection = self.trackball.projection_matrix(win_size) # draw our scene objects self.draw(projection, view, identity()) # flush render commands, and swap draw buffers glfw.swap_buffers(self.win) # Poll for and process events glfw.poll_events()
def run(self): """ Main render loop for this OpenGL window """ while not glfw.window_should_close(self.win): # clear draw buffer GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) # draw our scene objects projection = self.trackball.projection_matrix( glfw.get_window_size(self.win)) view = self.trackball.view_matrix() model = identity() # NodeStorage.get("cube1") super().draw(projection, view, model, win=self.win) # flush render commands, and swap draw buffers glfw.swap_buffers(self.win) # Poll for and process events glfw.poll_events()
def __init__(self): # initiate cross section instance self.xsection = XSection(f, Nl = 20, Nw = 20,clim=(0,0.1), f_args=(self.farg1,self.farg2)) # add cross sections T = transform.identity() T += transform.point_stretch([2.0,2.0,1.0]) T += transform.point_translation([-1.0,0.0,0.0]) self.xsection.add_transform(T) T += transform.point_rotation_x(np.pi/2.0) self.xsection.add_transform(T) T += transform.point_rotation_x(np.pi/2.0) self.xsection.add_transform(T) T += transform.point_rotation_x(np.pi/2.0) self.xsection.add_transform(T) # run HasTraits initiation routine HasTraits.__init__(self)
def __init__(self, color_shader, quad_vertex_array, life=0, color=(1, 1, 1, 1), height=15, width=1, x_offset=0, z_offset=0): super().__init__(color, quad_vertex_array, color_shader) self.color = color self.width = width self.height = height self.life = life # Remaining life of the particle. if < 0 : dead and unused. self.total_life = life self.x_offset = x_offset self.z_offset = z_offset self.model = identity()
def run(self): """ Main render loop for this OpenGL window """ while not glfw.window_should_close(self.win): # clear draw buffer GL.glClear(GL.GL_COLOR_BUFFER_BIT) # draw our scene objects winsize = glfw.get_window_size(self.win) view = self.trackball.view_matrix() projection = self.trackball.projection_matrix(winsize) for obj in self.drawables: for mesh in obj: mesh.draw(projection, view, identity(), self.color_shader) # flush render commands, and swap draw buffers glfw.swap_buffers(self.win) # Poll for and process events glfw.poll_events()
def construct_houses(self, shader_1, shader_2, transform=identity()): big_houses = [] for j in range(0, 5, 2): if (j % 4 == 2): for i in range(0, 5, 2): big_houses.append( add_object(self.big_house, transform=transform @ translate( 100 * i, 100 * j, 0) @ rotate( (0, 0, 1), random() * 180))) elif (j % 4 == 0): for i in range(1, 4, 2): big_houses.append( add_object(self.big_house, transform=transform @ translate( 100 * i, 100 * j, 0) @ rotate( (0, 0, 1), random() * 90))) else: pass #self.children.extend([add_object(self.trees['autumn_tree'], shader_2, transform=transform@translate(100*3, 100*1, 0)@scale(50))]) self.children.extend(big_houses)
v = np.random.random((10)) w = np.random.random((10)) mlab.quiver3d(x,y,z,u,v,w,mode='arrow') mlab.show() dphi = pi/1000. phi = arange(0.0, 2*pi + 0.5*dphi, dphi, 'd') def f(p,farg1,farg2): x = p[:,0] y = p[:,1] z = p[:,2] t = np.sqrt(farg1*x**2 + farg2*y**2) return t t = transform.identity() print('start1') m = VectorXSection(foo,transforms=[t]) print('end1') m.draw() m.view() class InteractiveXSection(HasTraits): # define range of f_arg values farg1 = Range(0, 30, .6) farg2 = Range(0, 30, .11) scene = Instance(MlabSceneModel, ()) view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene), height=250, width=300, show_label=False), Group('farg1', 'farg2'), resizable=True)
def __init__(self, shader, attributes, index=None): self.shader = shader self.vao = VertexArray(attributes, index) self.model = identity()
def add_object(object, transform=identity()): obj = Node(transform=transform) obj.add(object) return obj
def __init__(self, sections=11, quarters=20, **params): # this "arm" node and its transform serves as control node for bone 0 # we give it the default identity keyframe transform, doesn't move super().__init__({0: (0, 0, 0)}, {0: quaternion()}, {0: 1}, **params) # we add a son "forearm" node with animated rotation for the second # part of the cylinder self.add( SkinningControlNode({0: (0, 0, 0)}, { 0: quaternion(), 2: quaternion_from_euler(90), 4: quaternion() }, {0: 1})) # there are two bones in this animation corresponding to above noes bone_nodes = [self, self.children[0]] # these bones have no particular offset transform bone_offsets = [identity(), identity()] # vertices, per vertex bone_ids and weights vertices, faces, bone_id, bone_weights = [], [], [], [] for x_c in range(sections + 1): for angle in range(quarters): # compute vertex coordinates sampled on a cylinder z_c, y_c = sincos(360 * angle / quarters) vertices.append((x_c - sections / 2, y_c, z_c)) # the index of the 4 prominent bones influencing this vertex. # since in this example there are only 2 bones, every vertex # is influenced by the two only bones 0 and 1 bone_id.append((0, 1, 0, 0)) # per-vertex weights for the 4 most influential bones given in # a vec4 vertex attribute. Not using indices 2 & 3 => 0 weight # vertex weight is currently a hard transition in the middle # of the cylinder # TODO: modify weights here for TP7 exercise 2 weight = 1 if x_c <= sections / 2 else 0 bone_weights.append((weight, 1 - weight, 0, 0)) # face indices faces = [] for x_c in range(sections): for angle in range(quarters): # indices of the 4 vertices of the current quad, % helps # wrapping to finish the circle sections ir0c0 = x_c * quarters + angle ir1c0 = (x_c + 1) * quarters + angle ir0c1 = x_c * quarters + (angle + 1) % quarters ir1c1 = (x_c + 1) * quarters + (angle + 1) % quarters # add the 2 corresponding triangles per quad on the cylinder faces.extend([(ir0c0, ir0c1, ir1c1), (ir0c0, ir1c1, ir1c0)]) # the skinned mesh itself. it doesn't matter where in the hierarchy # this is added as long as it has the proper bone_node table self.add( SkinnedMesh([vertices, bone_weights, bone_id, bone_weights], bone_nodes, bone_offsets, faces))
def __init__(self, name='', children=(), transform=identity(), **param): self.transform, self.param, self.name = transform, param, name self.children = list(iter(children))
def __init__(self, name='', children=(), transform=identity(), **param): self.transform, self.param, self.name = transform, param, name self.children = list(iter(children)) pos = self.transform[:4,:4]@vec(0,0,0,1) self.position = pos[:3]
def __init__(self, *keys, **kwargs): super().__init__(**kwargs) self.keyframes = TransformKeyFrames(*keys) if keys[0] else None self.world_transform = identity()
def construct_town(self, shader, shader_2, transform=identity()): towers = [] castle_walls = [] towers.append( add_object(self.tower, transform=transform @ translate(32.5 * 20, 33 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(87.5 * 20, 33 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(115 * 20, 33 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(172.5 * 20, 33 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(172.5 * 20, 105 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(172.5 * 20, 129 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(172.5 * 20, 159 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(172.5 * 20, 187.5 * 20, 0) @ scale(2) @ rotate( (1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(75 * 20, 187.5 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(20 * 20, 162.5 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(20 * 20, 131.5 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(20 * 20, 120.5 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(20 * 20, 92.5 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(120 * 20, 185 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) towers.append( add_object(self.tower, transform=transform @ translate(140 * 20, 185 * 20, 0) @ scale(2) @ rotate((1, 0, 0), 90))) self.children.extend(towers) for i in range(33 * 20, 87 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(i, 38 * 20, -100) @ scale(2) @ rotate((1, 0, 0), 90))) for i in range(115 * 20, 172 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(i, 38 * 20, -100) @ scale(2) @ rotate((1, 0, 0), 90))) for j in range(37 * 20, 105 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(172 * 20, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 90))) for j in range(105 * 20, 130 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(172 * 20, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 90))) for j in range(162 * 20, 187 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(172 * 20, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 90))) for i in range(75 * 20, 120 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(i, 190 * 20, -100) @ scale(2) @ rotate((1, 0, 0), 90))) for i in range(140 * 20, 172 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(i, 190 * 20, -100) @ scale(2) @ rotate((1, 0, 0), 90))) j = 166.5 * 20 for i in range(20 * 20, 73 * 20, 9): castle_walls.append( add_object(self.wall, transform=transform @ translate(i, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 24.44))) j = j + 4 for j in range(96 * 20, 121 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(18 * 20, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 90))) for j in range(134 * 20, 164 * 20, 100): castle_walls.append( add_object(self.wall, transform=transform @ translate(18 * 20, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 90))) j = 92 * 20 for i in range(20 * 20, 32 * 20, 21): castle_walls.append( add_object(self.wall, transform=transform @ translate(i, j, -100) @ scale(2) @ rotate((1, 0, 0), 90) @ rotate( (0, 1, 0), 90 + 11.86))) j = j - 99 self.construct_houses(shader, shader_2, transform=translate(700, 1000, 0) @ scale(1.5)) self.construct_houses(shader, shader_2, transform=translate(650, 150, 0) @ scale(1.5)) self.construct_houses(shader, shader_2, transform=translate(700, -1000, 0) @ scale(1.5)) self.construct_houses(shader, shader_2, transform=translate(-200, 1000, 0) @ scale(1.5)) self.construct_houses(shader, shader_2, transform=translate(100, -700, 0) @ scale(1.5)) self.construct_houses(shader, shader_2, transform=translate(-1000, 720, 0) @ scale(1.5)) self.construct_houses(shader, shader_2, transform=translate(-1250, 0, 0) @ scale(1.5)) self.construct_houses( shader, shader_2, transform=translate(-1200, -1000, 0) @ scale(1.5)) for _ in range(50): x = randint(-1300, 1300) y = randint(-1200, 1400) self.children.extend([ add_object( self.trees[randint(0, 3)], transform=translate(x, y, 0) @ scale(randint(40, 70))) ]) self.children.extend( [add_object(self.trees[-1], translate(40, 40, 0) @ scale(120))]) self.children.extend([ add_object(self.church, transform=translate(-150, -400, 0) @ rotate( (0, 0, 1), 180) @ scale(2)) ]) self.children.extend(castle_walls) for i in range(-220, 200, 100): self.children.extend([ add_object(self.rock_wall, transform=translate(i, 600, 5) @ scale(2)) ]) self.children.extend([ add_object(self.rock_wall, transform=translate(i, 0, 5) @ scale(2)) ]) for i in range(100, 600, 100): self.children.extend([ add_object(self.rock_wall, transform=translate(-300, i, 5) @ scale(2) @ rotate( (0, 0, 1), 90)) ]) for i in range(300, 400, 100): self.children.extend([ add_object(self.rock_wall, transform=translate(500, i, 5) @ scale(2) @ rotate( (0, 0, 1), 90)) ]) self.children.extend([ add_object(self.rock_wall, transform=translate(450, 150, 5) @ scale(2) @ rotate( (0, 0, 1), 90 - 45)) ]) self.children.extend([ add_object(self.rock_wall, transform=translate(350, 50, 5) @ scale(2) @ rotate( (0, 0, 1), 90 - 45)) ]) self.children.extend([ add_object(self.rock_wall, transform=translate(450, 450, 5) @ scale(2) @ rotate( (0, 0, 1), 90 + 45)) ]) self.children.extend([ add_object(self.rock_wall, transform=translate(350, 550, 5) @ scale(2) @ rotate( (0, 0, 1), 90 + 45)) ])
def __init__(self, children=(), transform=identity()): self.transform = transform self.children = list(iter(children))
def construct_castle(self, transform=identity()): objects = [] # ADD TOWERS objects.append( add_object(self.tower, transform=transform @ translate(4, 7, 0) @ scale(1.5))) objects.append( add_object(self.tower, transform=transform @ translate(0, 7, 0) @ scale(1.5))) objects.append( add_object( self.tower, transform=transform @ translate(-5, 5.5, 0) @ scale(1.5))) objects.append( add_object( self.tower, transform=transform @ translate(-7, -2, 0) @ scale(1.5))) objects.append( add_object( self.tower, transform=transform @ translate(-3, -9, 0) @ scale(1.5))) objects.append( add_object( self.tower, transform=transform @ translate(2, -7.5, 0) @ scale(1.5))) objects.append( add_object( self.tower, transform=transform @ translate(6, -6.5, 0) @ scale(1.5))) objects.append( add_object(self.tower, transform=transform @ translate(5, 0, 0) @ scale(1.5))) # ADD WALLS objects.append( add_object(self.wall, transform=transform @ translate(2, 7, 0) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(-2.5, 6.25, 0) @ rotate( (0, 0, 1), 180 + degrees(atan(1.5 / 5.0))) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(-5.5, 3.5, 0) @ rotate( (0, 0, 1), 180 + degrees(atan(7.5 / 2.0))) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(-6.5, 0, 0) @ rotate( (0, 0, 1), 180 + degrees(atan(7.5 / 2.0))) @ scale(2.5))) objects.append( add_object(self.wall, transform=transform @ translate(-6, -3.75, 0) @ rotate( (0, 0, 1), 180 + 60 + degrees(atan(7.0 / 4.0))) @ scale(2.5))) objects.append( add_object(self.wall, transform=transform @ translate(-4, -7, 0) @ rotate( (0, 0, 1), 180 + 60 + degrees(atan(7.0 / 4.0))) @ scale(2.5))) objects.append( add_object(self.wall, transform=transform @ translate(-.5, -8.25, 0) @ rotate( (0, 0, 1), 180 + 180 + degrees(atan(1.5 / 5.0))) @ scale(2.5))) objects.append( add_object(self.wall, transform=transform @ translate(4, -7, 0) @ rotate( (0, 0, 1), 180 + 180 + degrees(atan(1.5 / 5.0))) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(5.25, -1.75, 0) @ rotate( (0, 0, 1), 180 + 270 + degrees(atan(1 / 7))) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(5.75, -5, 0) @ rotate( (0, 0, 1), 180 + 270 + degrees(atan(1 / 7))) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(5, 1.75, 0) @ rotate( (0, 0, 1), 180 + 270 + degrees(atan(1 / 7))) @ scale(2.5))) objects.append( add_object( self.wall, transform=transform @ translate(4.5, 5, 0) @ rotate( (0, 0, 1), 180 + 270 + degrees(atan(1 / 7))) @ scale(2.5))) #ADD LARGE TOWERS & TUNNEL & BRIDGE objects.append( add_object( self.tower, transform=transform @ translate(-1.5, 3, 0) @ scale(2.5))) objects.append( add_object(self.tower, transform=transform @ translate(1.5, 4, 0) @ scale(3))) objects.append( add_object(self.tower, transform=transform @ translate(2, -3, 0) @ scale(3.5))) objects.append( add_object(self.tunnel, transform=transform @ translate(-6, -6.5, 0) @ rotate( (0, 0, 1), 180 + 180 + 60 + degrees(atan(7.0 / 4.0))) @ scale(2.5))) objects.append( add_object( self.bridge, transform=transform @ translate(-6.66, -7.15, 0) @ rotate( (0, 0, 1), +180 + 60 + degrees(atan(7.0 / 4.0))) @ scale(2.5))) #ADD TREE objects.append( add_object(self.tree, transform=transform @ translate(-3, -3, 0) @ scale(3))) objects.append( add_object( self.wall, transform=transform @ translate(2, .5, 0) @ rotate( (0, 0, 1), 270 + degrees(atan(1 / 7))) @ scale(3.5))) # ADD OBJECTS TO CASTLE' CHILDREN self.children.extend(objects)