def init_origin(scene: Scene): """Origin object, construction cone, so user knows ARB is running.""" # TODO: migrate to shared-scene setting size = [0.2, 0.4, 0.2] scene.add_object(Cone( # 370mm x 370mm # 750mm object_id="arb-origin", material=Material( color=Color(255, 114, 33), transparent=True, opacity=0.5, shader="flat"), position=Position(0, size[1] / 2, 0), scale=Scale(size[0] / 2, size[1], size[2] / 2))) scene.add_object(Cone( object_id="arb-origin-hole", **{"material-extras": {"transparentOccluder": True}}, position=Position(0, size[1] - (size[1] / 2 / 15), 0), scale=Scale(size[0] / 15, size[1] / 10, size[2] / 15))) scene.add_object(Box( object_id="arb-origin-base", material=Material( color=Color(0, 0, 0), transparent=True, opacity=0.5, shader="flat"), position=Position(0, size[1] / 20, 0), scale=Scale(size[0], size[1] / 10, size[2])))
def set_clipboard(self, callback=None, object_type=None, scale=Scale(0.05, 0.05, 0.05), position=Position(0, 0, -CLIP_RADIUS/SCL_HUD), color=Color(255, 255, 255), url=None): if object_type: self.clipboard = Object( # show item to be created object_id=f"{self.camname}_clipboard", object_type=object_type, position=position, parent=self.hud.object_id, scale=Scale(scale.x/SCL_HUD, scale.y/SCL_HUD, scale.z/SCL_HUD), material=Material(color=color, transparent=True, opacity=0.4), url=url, clickable=True, evt_handler=callback) self.scene.add_object(self.clipboard) self.cliptarget = Circle( # add helper target object to find true origin object_id=f"{self.camname}_cliptarget", position=position, parent=self.hud.object_id, scale=Scale(0.005/SCL_HUD, 0.005/SCL_HUD, 0.005/SCL_HUD), material=Material(color=Color(255, 255, 255), transparent=True, opacity=0.4), clickable=True, evt_handler=callback) self.scene.add_object(self.cliptarget)
def set_active(self, active): self.active = active if active: self.scene.update_object( self.button, material=Material(color=CLR_SELECT)) else: self.scene.update_object( self.button, material=Material(color=CLR_BUTTON)) self.scene.update_object( self.text, material=Material(color=self.colortxt))
def dir_clickers(object_id, axis, direction, delim, position, color, cones, callback, move, parent): position = Position(x=position.x / SCL_CLICK, y=position.y / SCL_CLICK, z=position.z / SCL_CLICK) loc = Position(x=position.x / SCL_CLICK, y=position.y / SCL_CLICK, z=position.z / SCL_CLICK) npos = 0.1 / SCL_CLICK if direction == "p": npos = -0.1 / SCL_CLICK if axis == "x": loc = Position(x=position.x + npos, y=position.y, z=position.z) elif axis == "y": loc = Position(x=position.x, y=position.y + npos, z=position.z) elif axis == "z": loc = Position(x=position.x, y=position.y, z=position.z + npos) name_pos = f"{object_id}{delim}{axis}p_{direction}" name_neg = f"{object_id}{delim}{axis}n_{direction}" if name_pos not in CONTROLS[object_id]: CONTROLS[object_id][name_pos] = Cone( # click object positive object_id=name_pos, clickable=True, position=position, rotation=cones[axis + direction][0], scale=Scale(0.05 / SCL_CLICK, 0.09 / SCL_CLICK, 0.05 / SCL_CLICK), material=Material(color=color, transparent=True, opacity=arblib.OPC_CLINE), # TODO: restore ttl=arblib.TTL_TEMP, parent=parent, evt_handler=callback) scene.add_object(CONTROLS[object_id][name_pos]) elif move: scene.update_object(CONTROLS[object_id][name_pos], position=position) if name_neg not in CONTROLS[object_id]: CONTROLS[object_id][name_neg] = Cone( # click object negative object_id=name_neg, clickable=True, position=loc, rotation=cones[axis + direction][1], scale=Scale(0.05 / SCL_CLICK, 0.09 / SCL_CLICK, 0.05 / SCL_CLICK), material=Material(color=color, transparent=True, opacity=arblib.OPC_CLINE), # TODO: restore ttl=arblib.TTL_TEMP, parent=parent, evt_handler=callback) scene.add_object(CONTROLS[object_id][name_neg]) elif move: scene.update_object(CONTROLS[object_id][name_neg], position=loc)
def temp_loc_marker(position, color): return Sphere(ttl=120, material=Material(color=color, transparent=True, opacity=0.5), position=position, scale=Scale(0.02, 0.02, 0.02), clickable=True)
def boxline(object_id, axis, direction, delim, suffix, start, end, line_width, move, color, parent): line_width = line_width / SCL_CLICK end = Position(x=(end.x - start.x) / SCL_CLICK, y=(end.y - start.y) / SCL_CLICK, z=(end.z - start.z) / SCL_CLICK) if start.y == end.y and start.z == end.z: scale = Scale(x=abs(start.x - end.x), y=line_width, z=line_width) elif start.x == end.x and start.z == end.z: scale = Scale(x=line_width, y=abs(start.y - end.y), z=line_width) elif start.x == end.x and start.y == end.y: scale = Scale(x=line_width, y=line_width, z=abs(start.z - end.z)) name = f"{object_id}{delim}{axis}{direction}_{suffix}" position = Position(statistics.median([start.x, end.x]), statistics.median([start.y, end.y]), statistics.median([start.z, end.z])) if name not in CONTROLS[object_id]: CONTROLS[object_id][name] = Box( object_id=name, # TODO: restore ttl=arblib.TTL_TEMP, parent=parent, scale=scale, position=position, material=Material(color=color, transparent=True, opacity=0.4, shader="flat"), ) scene.add_object(CONTROLS[object_id][name]) elif move: scene.update_object(CONTROLS[object_id][name], position=position, scale=scale)
def occlude_obj(scene: Scene, object_id, occlude): if object_id in scene.all_objects: # NOTE: transparency does not allow occlusion so remove transparency here. scene.update_object(scene.all_objects[object_id], **{"material-extras": {"transparentOccluder": (occlude != BOOLS[1])}}, material=Material(transparent=False, opacity=1)) print(f"Occluded {object_id}")
def temp_rot_marker(position, rotation): return Box(ttl=120, rotation=rotation, material=Material(color=Color(255, 255, 255)), position=position, scale=Scale(0.02, 0.01, 0.15), clickable=True)
def set_hover(self, hover): if hover: opacity = OPC_BUTTON_HOVER else: opacity = OPC_BUTTON self.scene.update_object( self.button, material=Material(transparent=True, opacity=opacity, shader="flat"))
def set_lamp(self, enabled): if enabled: self.lamp = Light(object_id=f"{self.camname}_lamp", parent=self.hud.object_id, material=Material(color=Color(144, 144, 173)), type="point", intensity=0.75) self.scene.add_object(self.lamp) elif self.lamp: self.scene.delete_object(self.lamp)
def handle_clickline_event(event, mode): # naming order: objectname_clicktype_axis_direction click_id = event.object_id.split(f"_{mode.value}_") object_id = click_id[0] direction = (click_id[1])[0:2] move = (click_id[1])[1:4] if event.type == EVT_MOUSEENTER: scene.update_object(CONTROLS[object_id][event.object_id], material=Material(transparent=True, opacity=arblib.OPC_CLINE_HOVER)) elif event.type == EVT_MOUSELEAVE: scene.update_object(CONTROLS[object_id][event.object_id], material=Material(transparent=True, opacity=arblib.OPC_CLINE)) # allow any user to change an object if event.type != EVT_MOUSEDOWN: return None, None, None if USERS[event.data.source].mode != mode: return None, None, None obj = scene.get_persisted_obj(object_id) return (obj, direction, move)
def make_followspot(object_id, position, delim, color, parent, move=False): name = f"{object_id}{delim}spot" if name not in CONTROLS[object_id]: CONTROLS[object_id][name] = Circle( # follow spot on ground object_id=name, scale=Scale(0.1, 0.1, 0.1), # TODO: restore ttl=arblib.TTL_TEMP, position=Position(position.x, arblib.FLOOR_Y, position.z), rotation=Rotation(-0.7, 0, 0, 0.7), parent=parent, material=Material(color=color, transparent=True, opacity=0.4, shader="flat"), ) scene.add_object(CONTROLS[object_id][name]) elif move: scene.update_object(CONTROLS[object_id][name], position=position)
def create_obj(camname, clipboard, position): randstr = str(random.randrange(0, 1000000)) # make a copy of static object in place new_obj = Object( persist=True, object_id=f"{clipboard.data.object_type}_{randstr}", object_type=clipboard.data.object_type, position=position, # undo clipboard rotation for visibility rotation=Rotation(0, 0, 0, 1), scale=clipboard.data.scale, material=Material(color=clipboard.data.material.color, transparent=False), url=clipboard.data.url, clickable=True) scene.add_object(new_obj) USERS[camname].target_id = new_obj.object_id print("Created " + new_obj.object_id)
def make_clickroot(objid, position, delim, rotation=None, move=False): if objid not in CONTROLS.keys(): CONTROLS[objid] = {} name = f"{objid}{delim}clickroot" if rotation: name += "_rotated" else: rotation = Rotation(0, 0, 0, 1) if name not in CONTROLS[objid]: CONTROLS[objid][name] = Box( object_id=name, material=Material(transparent=True, opacity=0), position=position, scale=Scale(SCL_CLICK, SCL_CLICK, SCL_CLICK), rotation=rotation, ) scene.add_object(CONTROLS[objid][name]) elif move: scene.update_object(CONTROLS[objid][name], position=position, rotation=rotation) return name
def color_obj(scene: Scene, object_id, color): if object_id in scene.all_objects: scene.update_object( scene.all_objects[object_id], material=Material(color=color)) print(f"Colored {object_id}")
def show_redpill_scene(enabled): # any scene changes must not persist # show gridlines name = "grid_redpill" path = [] glen = arblib.GRIDLEN y = arblib.FLOOR_Y for z in range(-glen, glen + 1): if (z % 2) == 0: path.append(Position(-glen, y, z)) path.append(Position(glen, y, z)) else: path.append(Position(glen, y, z)) path.append(Position(-glen, y, z)) for x in range(-glen, glen + 1): if (x % 2) == 0: path.append(Position(x, y, glen)) path.append(Position(x, y, -glen)) else: path.append(Position(x, y, -glen)) path.append(Position(x, y, glen)) if enabled: scene.add_object( ThickLine(object_id=name, path=path, color=arblib.CLR_GRID)) else: arblib.delete_obj(scene, name) objs = scene.get_persisted_objs() for object_id in objs: obj = objs[object_id] # show occluded objects if "material-extras" in obj.data and "transparentOccluder" in obj.data[ "material-extras"]: name = "redpill_" + obj.object_id if enabled: object_type = "box" if "object_type" in obj.data: object_type = obj.data.object_type position = Position() if "position" in obj.data: position = obj.data.position rotation = Rotation() if "rotation" in obj.data: rotation = obj.data.rotation scale = Scale() if "scale" in obj.data: scale = obj.data.scale url = None if "url" in obj.data: url = obj.data.url color = Color() if "material" in obj.data and "color" in obj.data.material: color = obj.data.material.color scene.add_object( Object( object_id=name, object_type=object_type, position=position, rotation=rotation, scale=scale, clickable=True, url=url, material=Material(color=color, transparent=True, opacity=0.5), )) print("Wrapping occlusion " + name) else: arblib.delete_obj(scene, name)
def opaque_obj(scene: Scene, object_id, opacity): if object_id in scene.all_objects: scene.update_object(scene.all_objects[object_id], material=Material(transparent=True, opacity=opacity)) print(f"Opaqued {object_id}")
def make_wall(camname): # Wall theory: capture two poses and use them to place a wall object. # Also assumes first corner easier to capture accurate rotation than last. # Click 1: Capture the position and rotation. # Click 2: Capture the position only. sloc = USERS[camname].wloc_start eloc = USERS[camname].wloc_end srot = USERS[camname].wrot_start erot = USERS[camname].wrot_end print(f"S POS {str(sloc)}") print(f"E POS {str(eloc)}") # center point (blue) locx = statistics.median([sloc.x, eloc.x]) locy = statistics.median([sloc.y, eloc.y]) locz = statistics.median([sloc.z, eloc.z]) pos = Position(locx, locy, locz) scene.add_object(arblib.temp_loc_marker(pos, Color(0, 0, 255))) print(f"wall position {str(pos)}") # rotation print(f"S ROT {str(srot)}") print(f"E ROT {str(erot)}") rotx = arblib.probable_quat(srot.x) roty = arblib.probable_quat(srot.y) rotz = arblib.probable_quat(srot.z) rotw = arblib.probable_quat(srot.w) rot = Rotation(rotx, roty, rotz, rotw) gaze = (rotx, roty, rotz, rotw) scene.add_object(arblib.temp_rot_marker(pos, rot)) print(f"wall rotation {str(rot)}") # which axis to use for wall? use camera gaze # TODO: rotation still off if gaze in arblib.GAZES[0]: height = abs(sloc.y - eloc.y) width = abs(sloc.x - eloc.x) elif gaze in arblib.GAZES[1]: height = abs(sloc.y - eloc.y) width = abs(sloc.z - eloc.z) elif gaze in arblib.GAZES[2]: height = abs(sloc.z - eloc.z) width = abs(sloc.x - eloc.x) else: # TODO: (placeholder) add direction and hypotenuse height = abs(sloc.y - eloc.y) width = abs(sloc.x - eloc.x) print(f"Non-axis parallel rotation: {str(rot)}") # scale scax = width scay = height scaz = arblib.WALL_WIDTH sca = Scale(scax, scay, scaz) print(f"wall scale {str(sca)}") # make wall randstr = str(random.randrange(0, 1000000)) new_wall = Box( persist=True, clickable=True, object_id=f"wall_{randstr}", position=pos, rotation=rot, scale=sca, material=Material(color=Color(200, 200, 200), transparent=True, opacity=0.5), ) scene.add_object(new_wall) USERS[camname].target_id = new_wall.object_id print(f"Created {new_wall.object_id} r{str(rot)} s{str(sca)}")
def __init__(self, scene: Scene, camname, panel_callback): self.scene = scene self.camname = camname self.mode = Mode.NONE self.clipboard = self.cliptarget = None self.target_id = self.target_control_id = None self.position = self.rotation = None self.position_last = self.rotation_last = None self.gesturing = False self.target_style = self.typetext = "" self.locky = LOCK_YOFF self.lockx = LOCK_XOFF self.wloc_start = self.wloc_end = None self.wrot_start = self.wrot_end = None self.lamp = None init_origin(self.scene) # set HUD to each user self.hud = Box( object_id=f"hud_{camname}", parent=camname, material=Material(transparent=True, opacity=0), position=Position(0, 0, 0), scale=Scale(SCL_HUD, SCL_HUD, SCL_HUD), rotation=Rotation(0, 0, 0, 1), ) self.scene.add_object(self.hud) self.hudtext_left = self.make_hudtext( "hudTextLeft", Position(-0.15, 0.15, -0.5), str(self.mode)) self.hudtext_right = self.make_hudtext( "hudTextRight", Position(0.1, 0.15, -0.5), "") self.hudtext_status = self.make_hudtext( "hudTextStatus", Position(0.02, -0.15, -0.5), "") # workaround x=0 bad? # AR Control Panel self.follow_lock = False self.follow = Box( object_id=f"follow_{camname}", parent=camname, material=Material(transparent=True, opacity=0), position=Position(0, 0, -PANEL_RADIUS * 0.1), scale=Scale(0.1, 0.01, 0.1), rotation=Rotation(0.7, 0, 0, 0.7), ) self.scene.add_object(self.follow) self.redpill = False self.slider = False self.panel = {} # button dictionary self.dbuttons = {} buttons = [ # top row [Mode.ROTATE, -2, 1, True, ButtonType.ACTION], [Mode.NUDGE, -1, 1, True, ButtonType.ACTION], [Mode.SCALE, 0, 1, True, ButtonType.ACTION], [Mode.STRETCH, 1, 1, True, ButtonType.ACTION], [Mode.MODEL, 2, 1, True, ButtonType.ACTION], [Mode.CREATE, 3, 1, True, ButtonType.ACTION], # center row [Mode.REDPILL, -2, 0, True, ButtonType.TOGGLE], [Mode.MOVE, -1, 0, True, ButtonType.ACTION], [Mode.LOCK, 0, 0, True, ButtonType.TOGGLE], [Mode.DELETE, 1, 0, True, ButtonType.ACTION], [Mode.PARENT, 2, 0, True, ButtonType.ACTION], # bottom row [Mode.WALL, -2, -1, True, ButtonType.ACTION], [Mode.OCCLUDE, -1, -1, True, ButtonType.ACTION], [Mode.RENAME, 0, -1, True, ButtonType.ACTION], [Mode.COLOR, 1, -1, True, ButtonType.ACTION], [Mode.LAMP, 2, -1, True, ButtonType.TOGGLE], [Mode.SLIDER, 3, -1, False, ButtonType.TOGGLE], # TODO: adjust scale ] for but in buttons: pbutton = Button( scene, camname, but[0], but[1], but[2], enable=but[3], btype=but[4], parent=self.follow.object_id, callback=panel_callback) self.panel[pbutton.button.object_id] = pbutton
def __init__(self, scene: Scene, camname, mode, x=0, y=0, label="", parent=None, drop=None, color=CLR_BUTTON, enable=True, callback=None, btype=ButtonType.ACTION): self.scene = scene if label == "": label = mode.value if parent is None: parent = camname scale = Scale(0.1, 0.1, 0.01) else: scale = Scale(1, 1, 1) self.type = btype self.enabled = enable if enable: self.colorbut = color else: self.colorbut = CLR_BUTTON_DISABLED self.colortxt = CLR_BUTTON_TEXT if len(label) > 8: # easier to read self.label = f"{label[:6]}..." else: self.label = label self.mode = mode self.dropdown = drop self.active = False if drop is None: obj_name = f"{camname}_button_{mode.value}" else: obj_name = f"{camname}_button_{mode.value}_{drop}" shape = Box.object_type if btype == ButtonType.TOGGLE: shape = Cylinder.object_type scale = Scale(scale.x / 2, scale.y, scale.z / 2) self.button = Object( # box is main button object_id=obj_name, object_type=shape, parent=parent, material=Material( color=self.colorbut, transparent=True, opacity=OPC_BUTTON, shader="flat"), position=Position(x * 1.1, PANEL_RADIUS, y * -1.1), scale=scale, clickable=True, evt_handler=callback, ) scene.add_object(self.button) scale = Scale(1, 1, 1) if btype == ButtonType.TOGGLE: scale = Scale(scale.x * 2, scale.y * 2, scale.z) self.text = Text( # text child of button object_id=f"{self.button.object_id}_text", parent=self.button.object_id, text=self.label, # position inside to prevent ray events position=Position(0, -0.1, 0), rotation=Rotation(-0.7, 0, 0, 0.7), scale=scale, color=self.colortxt, ) scene.add_object(self.text)