def spawn_resource(tiles, min_amount, max_amount): cont = bge.logic.getCurrentController() for i in range(0, random.randint(min_amount, max_amount)): tile = Vector(( random.randint(-128, 128), random.randint(-128, 128) )) # leave a clearing in the center of the map, but offset if (tile - Vector((0,-6))).length > 14: d = spawn_object(cont, "Large_clearance_detector", tile.to_3d()) Detector(d, random.choice(tiles), i)
def skin_normal(norm, pidx): # TODO: not sure this is right norm = Vector([norm[0], norm[1], norm[2], 0]) out = Vector((0, 0, 0, 0)) weight_sum = 0 for joint_set, weight_set in zip(joint_sets, weight_sets): for j in range(0, 4): weight = weight_set[pidx][j] if weight != 0.0: weight_sum += weight joint = joint_set[pidx][j] out += weight * (joint_mats[joint] @ norm) out /= weight_sum out = out.to_3d().normalized() return out
def normals_from_colors(mesh): """ Replace normals based on vertex colors, only applies to verts affected by __NORMALS__ """ #if not mesh.vertex_colors['__NORMALS__']: # return #mesh = object.data if '__NORMALS__' in mesh.vertex_colors: color_map = mesh.vertex_colors['__NORMALS__'] for poly in mesh.polygons: for vert_idx, loop_idx in zip(poly.vertices, poly.loop_indices): #groups = [grp.group for grp in mesh.vertices[vert_idx].groups] rgba = Vector(color_map.data[loop_idx].color) if rgba.x == 0 and rgba.y == 0 and rgba.z == 0: continue rgb = rgba.to_3d() normal = rgb * 2 - Vector((1,1,1)) mesh.vertices[vert_idx].normal = normal
def draw_polygon_object(mat, vertices, faces, face_color, draw_faces, draw_wires, wire_lines=None, wire_color=(0, 0, 0), face_transforms=None, wire_transforms=None): """ Draw a collider polygon object. It takes matrix, vertices (coordinates), faces (vertex index references), face color (RGB), faces drawing state (bool), wires drawing state (bool) and optional values wire lines (list of lists of vertex positions resulting in closed lines), wire color (RGB), face transformations (list of vertex indices) and wire transformations (list of vertex indices). :param mat: :param vertices: :param faces: :param face_color: :param draw_faces: :param draw_wires: :param wire_lines: :param wire_color: :param face_transforms: :param wire_transforms: :return: """ if draw_faces: for face in faces: glBegin(GL_POLYGON) glColor3f(face_color[0], face_color[1], face_color[2]) for vert in face: if face_transforms: trans = mat for transformation in face_transforms: if vert in transformation[1]: trans = trans * transformation[0] glVertex3f(*(trans * Vector(vertices[vert]))) else: glVertex3f(*(mat * Vector(vertices[vert]))) glEnd() if draw_wires: if wire_lines: # DRAW CUSTOM LINES vert_i_global = 0 for line in wire_lines: # glLineWidth(2.0) glEnable(GL_LINE_STIPPLE) glBegin(GL_LINES) glColor3f(wire_color[0], wire_color[1], wire_color[2]) for vert_i, vert1 in enumerate(line): if vert_i + 1 < len(line): vert2 = line[vert_i + 1] else: continue # SEPARATE PART TRANSFORMATIONS if wire_transforms: trans1 = trans2 = mat for transformation in wire_transforms: if vert_i_global in transformation[1]: trans1 = trans1 * transformation[0] if vert_i_global + 1 in transformation[1]: trans2 = trans2 * transformation[0] glVertex3f(*(trans1 * Vector(vert1))) glVertex3f(*(trans2 * Vector(vert2))) else: glVertex3f(*(mat * Vector(vert1))) glVertex3f(*(mat * Vector(vert2))) vert_i_global += 1 vert_i_global += 1 glEnd() glDisable(GL_LINE_STIPPLE) # glLineWidth(1.0) else: for face in faces: # glLineWidth(2.0) glEnable(GL_LINE_STIPPLE) glBegin(GL_LINES) glColor3f(wire_color[0], wire_color[1], wire_color[2]) for vert_i, vert1 in enumerate(face): if vert_i + 1 == len(face): vert2 = face[0] else: vert2 = face[vert_i + 1] if face_transforms: trans1 = mat trans2 = mat vec1 = Vector(vertices[vert1]) vec2 = Vector(vertices[vert2]) for transformation in face_transforms: if vert1 in transformation[1]: trans1 = trans1 * transformation[0] if vert2 in transformation[1]: trans2 = trans2 * transformation[0] glVertex3f(*(trans1 * vec1)) glVertex3f(*(trans2 * vec2)) else: glVertex3f(*(mat * Vector(vertices[vert1]))) glVertex3f(*(mat * Vector(vertices[vert2]))) glEnd() glDisable(GL_LINE_STIPPLE) # glLineWidth(1.0) if 0: # DEBUG: draw points from faces geometry glPointSize(3.0) glBegin(GL_POINTS) glColor3f(0.5, 0.5, 1) for vertex_i, vertex in enumerate(vertices): vec = Vector(vertex) if face_transforms: trans = mat for transformation in face_transforms: if vertex_i in transformation[1]: trans = trans * transformation[0] glVertex3f(*(trans * vec)) else: glVertex3f(*(mat * vec.to_3d())) glEnd() glPointSize(1.0) if 0: # DEBUG: draw points from lines geometry if wire_lines: glPointSize(3.0) glBegin(GL_POINTS) glColor3f(1, 0, 0.5) vert_i_global = 0 for line in wire_lines: for vert_i, vertex in enumerate(line): if vert_i + 1 < len(line): vec = Vector(vertex) else: continue if wire_transforms: trans = mat for transformation in wire_transforms: if vert_i_global in transformation[1]: trans = trans * transformation[0] glVertex3f(*(trans * vec.to_3d())) else: glVertex3f(*(mat * vec.to_3d())) vert_i_global += 1 vert_i_global += 1 glEnd() glPointSize(1.0)
def texdata(self, face, mesh, obj): mat = None width = height = 64 if obj.material_slots: mat = obj.material_slots[face.material_index].material if mat: for node in mat.node_tree.nodes: if node.type == 'TEX_IMAGE': width, height = node.image.size break texstring = mat.name else: texstring = self.option_skip V = [loop.vert.co for loop in face.loops] uv_layer = mesh.loops.layers.uv.active if uv_layer is None: uv_layer = mesh.loops.layers.uv.new("dummy") T = [loop[uv_layer].uv for loop in face.loops] # UV handling ported from: https://bitbucket.org/khreathor/obj-2-map if self.option_format == 'Valve': # [ Ux Uy Uz Uoffs ] [ Vx Vy Vz Voffs ] rotation scaleU scaleV dummy = ' [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1\n' height = -height # workaround for flipped v # Set up "2d world" coordinate system with the 01 edge along X world01 = V[1] - V[0] world02 = V[2] - V[0] if world01.length < 0.00001 or world02.length < 0.00001: world01_02Angle = 0 else: world01_02Angle = world01.angle(world02) if face.normal.dot(world01.cross(world02)) < 0: world01_02Angle = -world01_02Angle world01_2d = Vector((world01.length, 0.0)) world02_2d = Vector((math.cos(world01_02Angle), math.sin(world01_02Angle))) * world02.length # Get 01 and 02 vectors in UV space and scale them tex01 = T[1] - T[0] tex02 = T[2] - T[0] tex01.x *= width tex02.x *= width tex01.y *= height tex02.y *= height ''' a = world01_2d b = world02_2d p = tex01 q = tex02 [ px ] [ m11 m12 0 ] [ ax ] [ py ] = [ m21 m22 0 ] [ ay ] [ 1 ] [ 0 0 1 ] [ 1 ] [ qx ] [ m11 m12 0 ] [ bx ] [ qy ] = [ m21 m22 0 ] [ by ] [ 1 ] [ 0 0 1 ] [ 1 ] px = ax * m11 + ay * m12 py = ax * m21 + ay * m22 qx = bx * m11 + by * m12 qy = bx * m21 + by * m22 [ px ] [ ax ay 0 0 ] [ m11 ] [ py ] = [ 0 0 ax ay ] [ m12 ] [ qx ] [ bx by 0 0 ] [ m21 ] [ qy ] [ 0 0 bx by ] [ m22 ] ''' # Find an affine transformation to convert # world01_2d and world02_2d to their respective UV coords texCoordsVec = Vector((tex01.x, tex01.y, tex02.x, tex02.y)) world2DMatrix = Matrix(((world01_2d.x, world01_2d.y, 0, 0), (0, 0, world01_2d.x, world01_2d.y), (world02_2d.x, world02_2d.y, 0, 0), (0, 0, world02_2d.x, world02_2d.y))) try: mCoeffs = solve(world2DMatrix, texCoordsVec) except: return texstring + dummy right_2dworld = Vector(mCoeffs[0:2]) up_2dworld = Vector(mCoeffs[2:4]) # These are the final scale values # (avoid division by 0 for degenerate or missing UVs) scalex = 1 / max(0.00001, right_2dworld.length) scaley = 1 / max(0.00001, up_2dworld.length) scale = Vector((scalex, scaley)) # Get the angles of the texture axes. These are in the 2d world # coordinate system, so they're relative to the 01 vector right_2dworld_angle = math.atan2(right_2dworld.y, right_2dworld.x) up_2dworld_angle = math.atan2(up_2dworld.y, up_2dworld.x) # Recreate the texture axes in 3d world coordinates, # using the angles from the 01 edge rt = world01.normalized() up = rt.copy() rt.rotate(Matrix.Rotation(right_2dworld_angle, 3, face.normal)) up.rotate(Matrix.Rotation(up_2dworld_angle, 3, face.normal)) # Now we just need the offsets rt_full = rt.to_4d() up_full = up.to_4d() test_s = V[0].dot(rt) / (width * scale.x) test_t = V[0].dot(up) / (height * scale.y) rt_full[3] = (T[0].x - test_s) * width up_full[3] = (T[0].y - test_t) * height texstring += f" [ {self.printvec(rt_full)} ]"\ f" [ {self.printvec(up_full)} ]"\ f" 0 {self.printvec(scale)}\n" elif self.option_format == 'Quake': # offsetU offsetV rotation scaleU scaleV dummy = ' 0 0 0 1 1\n' # 01 and 02 in 3D space world01 = V[1] - V[0] world02 = V[2] - V[0] # 01 and 02 projected along the closest axis maxn = max(abs(round(crd, 5)) for crd in face.normal) for i in [2, 0, 1]: # axis priority for 45 degree angles if round(abs(face.normal[i]), 5) == maxn: axis = i break world01_2d = Vector((world01[:axis] + world01[(axis + 1):])) world02_2d = Vector((world02[:axis] + world02[(axis + 1):])) # 01 and 02 in UV space (scaled to texture size) tex01 = T[1] - T[0] tex02 = T[2] - T[0] tex01.x *= width tex02.x *= width tex01.y *= height tex02.y *= height # Find affine transformation between 2D and UV texCoordsVec = Vector((tex01.x, tex01.y, tex02.x, tex02.y)) world2DMatrix = Matrix(((world01_2d.x, world01_2d.y, 0, 0), (0, 0, world01_2d.x, world01_2d.y), (world02_2d.x, world02_2d.y, 0, 0), (0, 0, world02_2d.x, world02_2d.y))) try: mCoeffs = solve(world2DMatrix, texCoordsVec) except: return texstring + dummy # Build the transformation matrix and decompose it tformMtx = Matrix(((mCoeffs[0], mCoeffs[1], 0), (mCoeffs[2], mCoeffs[3], 0), (0, 0, 1))) t0 = Vector((T[0].x * width, T[0].y * height)).to_3d() v0 = Vector((V[0][:axis] + V[0][(axis + 1):])).to_3d() offset = t0 - (tformMtx @ v0) rotation = math.degrees(tformMtx.inverted_safe().to_euler().z) scale = tformMtx.inverted_safe().to_scale() # always positive # Compare normals between UV and projection to get the scale sign tn = tex01.to_3d().cross(tex02.to_3d()) vn = world01_2d.to_3d().cross(world02_2d.to_3d()) if tn.dot(vn) < 0: scale.x *= -1 # fudge offset.x += width offset.y *= -1 finvals = [offset.x, offset.y, rotation, scale.x, scale.y] texstring += f" {self.printvec(finvals)}\n" return texstring
class Ant(bge.types.BL_ArmatureObject): antlist = [] def __init__(self, own): self["ant_init"] = True Ant.antlist.append(self) bge.logic.globalDict["pop"] = len(Ant.antlist) # Modes: # DROP: drop off self.carrying at an appropriate building # GOGET: go to self.collect # GOTO: go to self.target # GOBACK: go to 0,0 self.mode = "GOTO" self.idle = True self.target = Vector((0, 0)) self.collect = None # useful to store this in case self.collect becomes invalid due to being used up self.collect_category = None self.carrying = None self.carry_type = None self.carry_category = None self.destination = None self.vision_distance = 5 self.stopping_margin = .5 self.acceleration = .005 self.max_speed = .1 #self.max_turning_speed self.near_sens = self.sensors["Near"] self.zoffset = self.worldPosition.copy().z self.nearest_ant = 100 self.speed = 0 self.target_direction = Vector((0, -1, 0)) self.direction = self.getAxisVect((0,-1,0)) #self.wander_direction = Vector((.5,.5)) self.currently_considering = 0 self.ticks_since_last_meal = random.randint(0, 600) self.return_home_timer = None def eat(self): if self.ticks_since_last_meal > 900: if bge.logic.globalDict["food"] > 0: bge.logic.globalDict["food"] -= 1 bge.logic.sendMessage("GUI") self.ticks_since_last_meal = 0 else: # go without food self.ticks_since_last_meal = 0 if random.random() < .3: bge.logic.sendMessage("notify", "An ant starved!") self.die() else: bge.logic.sendMessage("notify", "An ant is hungry") self.ticks_since_last_meal += 1 def die(self): if self.carrying is not None: self.carrying.endObject() self.carrying = None for o in self.children: o.endObject() Ant.antlist.remove(self) bge.logic.globalDict["pop"] = len(Ant.antlist) bge.logic.sendMessage("GUI") self.endObject() def towards_target(self): dist, vect, lvect = self.getVectTo(self.target.to_3d()) vect.normalize() if dist < self.vision_distance: # apply braking force proportional to distance vect = vect - (vect * min((dist/-self.vision_distance) + 1/self.vision_distance + self.stopping_margin, 1)) self.stopping_margin = min(self.stopping_margin +.01, 1) else: self.stopping_margin = .5 pass return vect def around_obstacles(self): here = self.worldPosition ahead = self.worldPosition + self.direction * self.vision_distance obstacle = self.rayCastTo(ahead, self.vision_distance, "obstacle") if obstacle: dist, go_around, l = self.getVectTo(obstacle) if dist < self.vision_distance: # apply braking force proportional to distance go_around = go_around - (go_around * min((dist/-self.vision_distance) + 1/self.vision_distance + self.stopping_margin, 1)) return -go_around else: return Vector((0,0,0)) def separate(self): # in case of death if len(Ant.antlist) <= self.currently_considering: self.currently_considering = 0 if Ant.antlist[self.currently_considering] != self: next_ant = Ant.antlist[self.currently_considering] else: self.currently_considering = (self.currently_considering + 1) % len(Ant.antlist) next_ant = Ant.antlist[self.currently_considering] dist, vect, lvect = self.getVectTo(next_ant) if dist < self.nearest_ant: self.nearest_ant = dist self.currently_considering = (self.currently_considering + 1) % len(Ant.antlist) if self.nearest_ant < .5 and vect: #bge.render.drawLine(self.worldPosition, self.worldPosition + vect*10 , (.3, 0, 1)) self.nearest_ant = 100 return -vect else: return Vector((0,0,0)) def wander(self): t = bge.logic.getRealTime() v = Vector((t, t, t)) + self.worldPosition n = noise.noise_vector(v) #return self.wander_direction return n.to_2d() def move(self): if not self.isPlayingAction(): self.playAction("antwalking", 0, 12, 0, 0, 0, bge.logic.KX_ACTION_MODE_LOOP) self.setActionFrame((self.getActionFrame()+self.direction.length)%12) if not self.direction.length < 0.000001: self.alignAxisToVect(-self.direction, 1) self.worldPosition += self.direction * self.max_speed def find_nearest(self, ids): nearest_dist = 300 nearest_dest = None for dest in bge.logic.getCurrentScene().objects: for id in ids: if dest.get("id", None) == id: dist = (dest.worldPosition - self.worldPosition).length if dist < nearest_dist: nearest_dist = dist nearest_dest = dest return nearest_dest def update_workercount(self, recount=False): gd = bge.logic.globalDict print(gd["idleworkers"]) if recount: gd["foodworkers"] = 0 gd["materialworkers"] = 0 gd["scienceworkers"] = 0 gd["idleworkers"] = 0 for ant in Ant.antlist: if ant.collect is not None and not ant.collect.invalid: print(ant.collect["category"]) gd[ant.collect["category"] + "workers"] += 1 else: gd["idleworkers"] += 1 else: if self.collect: gd[self.collect["category"] + "workers"] += 1 else: gd["idleworkers"] += 1 bge.logic.sendMessage("GUI") def go_to(self, coords): self.target = coords.copy() self.mode = "GOTO" def go_get(self, obj): if self.carrying is not None: # need to drop something off first self.go_drop() if obj is not None: self.collect = obj else: if obj is not None: self.collect = obj self.target = self.collect.worldPosition.copy() self.mode = "GOGET" else: self.collect = None self.collect_category = None self.go_back() #self.update_workercount() def go_back(self): self.return_home_timer = None self.go_to(Vector((0,-3,0))) def go_drop(self): # determine nearest possible dropoff point destination = None if self.carry_type == "leaf": destination = self.find_nearest(['Farm']) elif self.carry_type == "honey": destination = self.find_nearest(['Honey Den']) else: destination = self.find_nearest(['Storage', 'Den']) # if there's no special buildings to deliver to, a generic one will do if destination is None: destination = self.find_nearest(['Storage', 'Den']) # if *still* none we now have a real problem if destination is None: bge.logic.sendMessage("notify", "Nowhere to store resources!!") self.destination = destination self.target = self.destination.worldPosition.copy() self.mode = "DROP" def main(self): scene = bge.logic.getCurrentScene() for s in bge.logic.getSceneList(): if s.name == "wait for pause": pscene = s # check for invalid gameobject references if self.collect is not None and self.collect.invalid: self.collect = None if self.destination is not None and self.destination.invalid: self.destination = None # order taking if not scene.objects["Placement_Empty"]['BuildModeActive'] and pscene.objects["OpenMenuControls"]["OpenMenus?"]: if self["selected"]: if self.sensors["Click"].positive: if self.sensors["CanGo"].positive: obj, hitPoint, normal = self.rayCast(self.sensors["CanGo"].rayTarget, self.sensors["CanGo"].raySource, 300) # only resources have a "points" property if "points" in obj: self.go_get(obj) else: self.go_to(self.sensors["CanGo"].hitPosition) # decision making if self.mode == "GOTO": if self.return_home_timer is not None: #counting away the ticks self.return_home_timer -= 1 #have we arrived (away from home)? if (Vector((0,-3,0)) - self.target).length > 1.5: if (self.worldPosition - self.target).length < 1.5: if self.return_home_timer is not None and self.return_home_timer < 1: self.go_back() elif self.return_home_timer is None: self.return_home_timer = 60 * 10 elif self.mode == "GOGET": # is there still something to collect? if self.collect is not None: # go get resource if (self.worldPosition - self.collect.worldPosition).length < 1.5: if self.collect["points"] > 0: self.collect["points"] -= 1 self.carry_type = self.collect["type"] self.carry_category = self.collect["category"] self.carrying = scene.addObject(self.carry_type + "fragment", self) # if self.collect_type == "honey": # print("replacing mesh") # self.replaceMesh("Cube.001") # if we grabbed the last one, make resource vanish if self.collect["points"] <= 0: self.collect.parent.endObject() self.collect.endObject() self.go_drop() else: #send him back #self.update_workercount(recount=True) self.go_get(None) self.go_back() elif self.mode == "DROP": # return with resource if (self.worldPosition - self.destination.worldPosition).length < 1.5: if self.carry_category == "food": if "stored" in self.destination: self.destination['stored'] += 1 else: increase_resource(self, "food") else: increase_resource(self, self.carry_category) if self.carrying: self.carrying.endObject() self.carrying = None # go back for more self.go_get(self.collect) elif self.mode == "GOBACK": # if resource is gone but we still are carrying some around if self.carrying is not None: self.mode = "DROP" if self.carrying is not None and not self.carrying.invalid: self.carrying.worldPosition = self.worldPosition # other stuff # insist upon being at ground level at all times obj, hitpoint, normal = self.rayCast(self.worldPosition + Vector((0,0,-1)), self.worldPosition + Vector((0,0,1)), 10, "Ground", 0, 1) self.worldPosition.z = hitpoint.z + self.zoffset self.alignAxisToVect(normal, 2) #self.accelerate() #bge.render.drawLine(self.worldPosition, self.target.to_3d(), (1, 1, 1)) o = self.around_obstacles() t = self.towards_target() s = self.separate() w = self.wander() self.target_direction = o.to_2d() + t.to_2d() + s.to_2d() self.target_direction = self.target_direction + (w.to_2d() * self.target_direction.length) self.target_direction.resize_3d() self.target_direction.normalize() #bge.render.drawLine(self.worldPosition, self.worldPosition + self.target_direction*10, (1, 0, 0)) self.direction = self.direction.lerp(self.target_direction, .05) self.move() self.eat()
def build_matrix(self): #the largest dimeions of image is scaled to 1 blender unit #in the empty image #unsure how things work with non square pixels img_scl = 1 / max([self.image.size[0], self.image.size[1]]) print('the world unit to pixel scale is') print(img_scl) #Condyle Position center = Vector((self.pixel_coords[0][0], self.image.size[1] - self.pixel_coords[0][1])) tV = -img_scl * center.to_3d() T = Matrix.Translation(tV) #Frankfurt Horizontal is desired to align with X axis X = self.pixel_coords[1] - self.pixel_coords[0] X[1] = -X[1] #the Y coodinates go from the top down print(X) X.normalize() X = X.to_3d() #The Image Z should ultimately be the world -Y Y = Vector((0, 0, -1)) Z = X.cross(Y) #build a rotation matrix from x,y,z R = Matrix.Identity(3) #make the columns of matrix U, V, W R[0][0], R[0][1], R[0][2] = X[0], Y[0], Z[0] R[1][0], R[1][1], R[1][2] = X[1], Y[1], Z[1] R[2][0], R[2][1], R[2][2] = X[2], Y[2], Z[2] Q = R.to_quaternion() Qi = Q.inverted() Rqi = Qi.to_matrix().to_4x4() Rr = R.to_4x4() Ri = R.to_4x4().inverted() #scale factor and occlusal plane vy = self.pixel_coords[3] - self.pixel_coords[2] #remember dumb y axis is top down vy[1] = -vy[1] scl_x = 100 / vy.length / img_scl vz = self.pixel_coords[4] - self.pixel_coords[5] scl_y = 40 / vz.length / img_scl print('Are scl_x and scl_y close?') print((scl_x, scl_y)) scl_final = .5 * (scl_x + scl_y) S = Matrix.Identity(4) S[0][0], S[1][1] = scl_final, scl_final self.empty.matrix_world = Ri * S * T return