Beispiel #1
0
    def compute(self, plug, data):

        this_node = self.thisMObject()

        if plug == self.a_instance_data:

            # if the node has yet not been in initialized create the instance
            # data attribute and read all point if some exist
            if not self._state:
                self.initialize_state(plug, data)

            # cache geometry
            is_cached = data.inputValue(self.a_geo_cached).asBool()
            if not is_cached:

                # check if there is another spore node that already has a cache
                # object for the current inmesh
                # note: this does not ensure that the cache is up to date!
                found = False
                for key, node in sys._global_spore_tracking_dir.iteritems():
                    other_in_mesh = node_utils.get_connected_in_mesh(
                        node.thisMObject())
                    in_mesh = node_utils.get_connected_in_mesh(
                        self.thisMObject())
                    if in_mesh == other_in_mesh and node != self:
                        self.geo_cache = node.geo_cache

                        # check if the cache is still valid
                        if self.geo_cache.validate_cache():
                            found = True

                        else:
                            in_mesh = node_utils.get_connected_in_mesh(
                                self.thisMObject(), False)
                            self.geo_cache.flush_cache()
                            self.geo_cache.cache_geometry(in_mesh)

                        # if the cache is invalid break, since we need to recache
                        break

                # if no cache was found start creating a new one
                if not found:
                    in_mesh = node_utils.get_connected_in_mesh(
                        self.thisMObject(), False)
                    self.geo_cache.cache_geometry(in_mesh)

                # set cached to true
                node_fn = om.MFnDependencyNode(self.thisMObject())
                cached_plug = node_fn.findPlug('geoCached')
                cached_plug.setBool(True)

            is_delete = data.inputValue(self.a_clear).asBool()
            if is_delete:
                self._state.clear()

                node_fn = om.MFnDependencyNode(self.thisMObject())
                clear_plug = node_fn.findPlug('clear')
                clear_plug.setBool(False)

            data.setClean(self.a_instance_data)
Beispiel #2
0
    def emit(self, *args):
        """ run the actual sample command and check if transforms are frozen """

        # exit spore context since it looses track of points after sampling
        if cmds.currentCtx().startswith('spore'):
            cmds.setToolTo('selectSuperContext')

        in_mesh = node_utils.get_connected_in_mesh(self._node)
        transform = cmds.listRelatives(in_mesh, p=True, f=True)[0]
        if cmds.getAttr(transform + '.translateX') != 0\
        or cmds.getAttr(transform + '.translateY') != 0\
        or cmds.getAttr(transform + '.translateZ') != 0\
        or cmds.getAttr(transform + '.rotateX') != 0\
        or cmds.getAttr(transform + '.rotateY') != 0\
        or cmds.getAttr(transform + '.rotateZ') != 0\
        or cmds.getAttr(transform + '.scaleX') != 1\
        or cmds.getAttr(transform + '.scaleY') != 1\
        or cmds.getAttr(transform + '.scaleZ') != 1:
            msg = 'Feeze transformations to sample the geomety!'
            result = message_utils.IOHandler().confirm_dialog(msg, 'Freeze Transformations')
            if result:
                cmds.makeIdentity(transform, a=True, s=True, r=True, t=True, n=0)
            else:
                return

        cmds.setAttr('{}.emit'.format(self._node), 1)
        cmds.sporeSampleCmd()
Beispiel #3
0
    def altitude_filter(self, min_altitude, max_altitude, min_fuzziness,
                        max_fuzziness):
        """ filter points based on y position relative to bounding box """

        in_mesh = node_utils.get_connected_in_mesh(self.target, False)
        dag_fn = om.MFnDagNode(in_mesh)

        bb = dag_fn.boundingBox()
        bb_y_min = bb.min().y
        height = bb.height()

        invalid_ids = []
        for i, (position, _, _, _, _) in enumerate(self.point_data):
            y_normalized = position[1] - bb_y_min
            pos_rel = y_normalized / height

            if pos_rel < min_altitude:
                if pos_rel < min_altitude - min_fuzziness:
                    invalid_ids.append(i)

                elif min_altitude - pos_rel > random.uniform(0, min_fuzziness):
                    invalid_ids.append(i)

            elif pos_rel > max_altitude:
                if pos_rel > max_altitude + max_fuzziness:
                    invalid_ids.append(i)

                elif abs(max_altitude - pos_rel) > random.uniform(
                        0, max_fuzziness):
                    invalid_ids.append(i)

        invalid_ids = sorted(invalid_ids, reverse=True)
        [self.point_data.remove(index) for index in invalid_ids]
Beispiel #4
0
    def get_spore_setups(self):
        """ return a dictionary with an entry for each target mesh
        and for each entry a list with all connected spore nodes """

        spore_nodes = cmds.ls(type='sporeNode', l=True)
        targets = collections.defaultdict(list)

        #  TODO - once this can be debugged this would be the way to go
        #  [targets[node_utils.get_connected_in_mesh(node)].append(node) for node in spore_nodes]

        for node in spore_nodes:
            target = node_utils.get_connected_in_mesh(node)
            if target:
                targets[target].append(node)

            else: # May help to debbug a situation wherfe target is sometime None
                target = cmds.getConnection('{}.inMesh'.format(node))
                target_shape = cmds.listRelatives(cmds.ls(target), s=True, f=True)

                if len(target_shape) == 1:
                    obj_type = cmds.objectType(target_shape[0])
                    self.logger.warning(
                        'Getting target mesh failed but spore has been able '
                        'to use fallback. target: {}, shape: {}, type: '
                        '{}'.format(target, target_shape[0], obj_type))
                    targets[target_shape] = node
                else:
                    obj_type = [cmds.objectType(s) for s in target_shape]
                    raise RuntimeError(
                        'Could not get target mesh and spore failed to use '
                        'fallback. target {}, shapes {}, types: {}'.format(
                            target, target_shape, obj_type)
                    )

        return targets
Beispiel #5
0
    def voxelize(self, cell_size):
        """ partition the spatial domain with the given cellsize.
        than assign each point to the cell is spatialy belongs to.
        :return dict: where:
                      key == the cell index
                      value == list of points indexes from the point_data obj """

        partition = {}

        in_mesh = node_utils.get_connected_in_mesh(self.target, False)
        bb = om.MFnDagNode(in_mesh).boundingBox()

        self.w_count = int(math.ceil(bb.width() / cell_size))
        self.h_count = int(math.ceil(bb.height() / cell_size))
        self.d_count = int(math.ceil(bb.depth() / cell_size))

        bb_min = bb.min()
        for i in xrange(self.point_data.position.length()):
            p_normalized = self.point_data.position[i] - bb_min
            p_x = int(p_normalized.x / cell_size)
            p_y = int(p_normalized.y / cell_size)
            p_z = int(p_normalized.z / cell_size)

            index = p_x + p_y * self.w_count + p_z * self.w_count * self.h_count

            partition.setdefault(index, []).append(i)

        return partition
Beispiel #6
0
    def get_spore_setups(self):
        """ return a dictionary with an entry for each target mesh
        and for each entry a list with all connected spore nodes """

        spore_nodes = cmds.ls(type='sporeNode', l=True)
        targets = collections.defaultdict(list)
        [targets[node_utils.get_connected_in_mesh(node)].append(node) for node in spore_nodes]
        return targets
Beispiel #7
0
    def random_sampling(self, num_points):
        """ sample a given number of points on the previously cached triangle
        mesh. note: evaluating uvs on high poly meshes may take a long time """

        if not self.geo_cache.validate_cache():
            in_mesh = node_utils.get_connected_in_mesh(self.target, False)
            self.geo_cache.cache_geometry(in_mesh)

        self.point_data.set_length(num_points)
        [self.sample_triangle(random.choice(self.geo_cache.weighted_ids), i) for i in xrange(num_points)]
Beispiel #8
0
    def evaluate_uvs(self):
        """ evaluate uv coords for all points in point data.
        note: this may take a long time for large meshes """

        in_mesh = node_utils.get_connected_in_mesh(self.target, False)

        for i, (position, normal, poly_id, u_coord, v_coord) in enumerate(self.point_data):
            if not u_coord and not v_coord:

                pos = om.MPoint(position[0], position[1], position[2])
                u_coord, v_coord = mesh_utils.get_uv_at_point(in_mesh, pos)

                self.point_data.u_coord[i] = u_coord
                self.point_data.v_coord[i] = v_coord
Beispiel #9
0
    def estimate_num_samples(self, node):
        """ estimate how many random samples we need for grid or disk sampling """

        self._node = node
        emit_type = cmds.getAttr('{}.emitType'.format(node))
        if emit_type == 1:
            cell_size = cmds.getAttr(self._node + '.cellSize')
        elif emit_type == 2:
            cell_size = cmds.getAttr(self._node + '.minRadius') / math.sqrt(3)
        else:
            return

        in_mesh = node_utils.get_connected_in_mesh(self._node)
        area = cmds.polyEvaluate(in_mesh, worldArea=True)
        cmds.setAttr(self._node + '.numSamples', int(area/cell_size) * 5)
Beispiel #10
0
    def toolOnSetup(self, event):
        """ tool setup:
        - get the node's inMesh and set it as target for the tool
        - update the context controller
        - install mouse & key events
        - build the canvas frot drawing """

        self.logger.debug('Set up Spore context')

        # get spore_node's inMesh and set it as target
        # note: we expect the target node to be selected when we setup the tool
        # if no sporeNode is selected we try to use the last target as fallback
        # if there is no fallback, tool initialization will fail and display a
        # warning
        try: # try to get selection of type sporeNode
            node_name = cmds.ls(sl=True, l=True, type='sporeNode')[0]
        except IndexError:
            node_name = None

        # try to get inMesh of selected spore node
        if node_name:
            self.state.target = node_utils.get_connected_in_mesh(node_name)
            self.state.node = node_name

            if not self.state.target or not self.state.node:
                self.logger.error('Failed to initialize Spore Context')
                return

        # fallback to old target, just pass since target is already set
        elif self.state.target and self.state.node:
            pass

        # if we neither have a sporeNode selected nor have a fallback, tool init fails
        else:
            self.msg_io.set_message('No sporeNode selected: Can\'t operate on: {}'.format(cmds.ls(sl=1), 1))
            self.logger.warn('Context could not find target spore node')
            return

        # get node state & cache points for editing
        #  self.instance_data = instance_data.SporeState(self.state.node)
        spore_obj = node_utils.get_mobject_from_name(node_name)
        obj_handle = om.MObjectHandle(spore_obj)
        spore_locator = sys._global_spore_tracking_dir[obj_handle.hashCode()]
        self.instance_data = spore_locator._state
        self.state.get_brush_settings()

        if self.state.settings['mode'] == 'scale'\
        or self.state.settings['mode'] == 'align'\
        or self.state.settings['mode'] == 'smooth'\
        or self.state.settings['mode'] == 'move' \
        or self.state.settings['mode'] == 'id'\
        or self.state.settings['mode'] == 'remove':
            try:
                self.instance_data.build_kd_tree()
            except ValueError: # the spore node is empty
                self.msg_io.set_message('SporeNode is empty. Nothing to edit')
                return

        # install event filter
        view = window_utils.active_view_wdg()
        view.installEventFilter(self.mouse_event_filter)
        window = window_utils.maya_main_window()
        window.installEventFilter(self.key_event_filter)

        # set up canvas for drawing
        if self.state.settings['mode'] == 'place': #'place':
            self._setCursor(omui.MCursor.crossHairCursor)
        else:
            self.canvas = canvas.CircularBrush(self.state)
        self.help_display = canvas.HelpDisplay(self.state.settings['mode'])
        self.help_display.set_visible(False)
Beispiel #11
0
 def boundingBox(self):
     in_mesh = node_utils.get_connected_in_mesh(self.thisMObject(), False)
     mesh_fn = om.MFnDagNode(in_mesh)
     return mesh_fn.boundingBox()
Beispiel #12
0
    def disk_sampling_2d(self, radius, u=1, v=1):
        """ sample poisson disk samples in uv space
        note: the given radius must be between 0 and 1 """
        #  def poisson(radius, k, w, h, n):

        # TODO - make uv sample space editable for user

        grid = []
        active = []
        ordered = []

        cellsize = radius / math.sqrt(2)

        col = int(u / cellsize)
        row = int(v / cellsize)

        grid = [None] * col * row

        x = random.random() * u  # random.uniform( 0, w )
        y = random.random() * v  # random.uniform( 0, h )
        pos = (x, y)

        current_col = int(x / cellsize)
        current_row = int(y / cellsize)

        grid[current_col + current_row * col] = pos
        active.append(pos)
        ordered.append(pos)

        while len(active) > 0:

            rand_index = int(len(active) - 1)
            active_pos = active[rand_index]
            found = False

            k = 30
            for each in xrange(k):

                # find a new point withhin a the range of radius - 2r
                rand_distance = random.uniform(radius, 2 * radius)
                theta = 360 * random.random()
                offset_x = math.cos(theta) * rand_distance
                offset_y = math.sin(theta) * rand_distance

                # get the absolut position of the new pos
                new_x = active_pos[0] + offset_x
                new_y = active_pos[1] + offset_y
                new_pos = (new_x, new_y)

                # get the new col & row position of the point
                active_col = int(new_x / cellsize)
                active_row = int(new_y / cellsize)

                if active_col > 0 \
                and active_row > 0 \
                and active_col < col \
                and active_row < row \
                and not grid[active_col + active_row * col]:

                    valid = True
                    for j in xrange(-1, 2):
                        for f in xrange(-1, 2):
                            index = (active_col + j) + (active_row + f) * col
                            try:
                                neighbor = grid[index]
                            except IndexError:
                                continue
                            if neighbor:
                                distance = math.sqrt(
                                    (neighbor[0] - new_pos[0])**2 +
                                    (neighbor[1] - new_pos[1])**2)
                                if (distance < radius):
                                    valid = False

                    if valid:
                        found = True
                        grid[active_col + active_row * col] = new_pos
                        active.append(new_pos)
                        ordered.append(new_pos)

                        # TODO

            if not found:
                active.pop(rand_index)

        self.point_data.set_length(len(ordered))

        util = om.MScriptUtil()
        in_mesh = node_utils.get_connected_in_mesh(self.target, False)
        mesh_fn = om.MFnMesh(in_mesh)

        for i, (u_coord, v_coord) in enumerate(ordered):

            face_ids = self.geo_cache.get_close_face_ids(u_coord, 1 - v_coord)

            position = om.MPoint()
            normal = om.MVector()
            for face_id in face_ids:
                util.createFromList([u_coord, 1 - v_coord], 2)
                ptr = util.asFloat2Ptr()

                try:
                    mesh_fn.getPointAtUV(face_id, position, ptr,
                                         om.MSpace.kWorld)
                    mesh_fn.getClosestNormal(position, normal)
                    self.point_data.set(i, position, normal, 1, u_coord,
                                        1 - v_coord)
                except:
                    continue
Beispiel #13
0
    def disk_sampling_3d(self, min_radius, grid_partition, cell_size):

        in_mesh = node_utils.get_connected_in_mesh(self.target, False)
        bb = om.MFnDagNode(in_mesh).boundingBox()

        # pick randomly an initial point from where we start sampling
        initial_key = random.choice(grid_partition.keys())
        init_p_ref = random.choice(grid_partition[initial_key])

        # create two list for active (not yet processed) and valid (sampled) points
        active = []
        valid_points = [None] * self.w_count * self.h_count * self.d_count

        # append the first point to both lists
        active.append(init_p_ref)
        valid_points[initial_key] = init_p_ref

        while len(active) > 0:

            # pick a random point from the active points list
            p_active_index = int(len(active) * random.random())
            p_active = active[p_active_index]
            # TODO - get each active point only once?

            # normalize the point and get it's x,y,z index in the grid
            p_normalized = self.point_data.position[p_active] - bb.min()
            p_grid_x = int(p_normalized.x / cell_size)
            p_grid_y = int(p_normalized.y / cell_size)
            p_grid_z = int(p_normalized.z / cell_size)

            # assume no point will be found
            found = False

            # try k times to find a new sample
            k = 30
            for i in xrange(k):

                # get neighboring cell
                new_p_x, new_p_y, new_p_z = self.get_valid_neighbouring_cell(
                    p_grid_x, p_grid_y, p_grid_z)

                # get liear index from x,y,z position.
                key = new_p_x + new_p_y * self.w_count + new_p_z * self.w_count * self.h_count

                # check if key is valid and if there isnt already a valid point...
                if grid_partition.has_key(key)\
                and not valid_points[key]:

                    # get a random point from the list associated with the key
                    new_index = int(len(grid_partition[key]) * random.random())
                    point_index = grid_partition[key][new_index]
                    point = self.point_data.position[point_index]

                # ...otherwise try again
                else:
                    continue

                valid = True
                # check against all nearby cells if the sample is valid
                for x in xrange(new_p_x - 1, new_p_x + 2):
                    for y in xrange(new_p_y - 1, new_p_y + 2):
                        for z in xrange(new_p_z - 1, new_p_z + 2):

                            # ignore invalid cells
                            if x < 0 or y < 0 or z < 0:
                                continue
                            elif x > self.w_count - 1 or y > self.h_count - 1 or z > self.d_count - 1:
                                continue

                            # get the index for the current cell
                            index = x + y * self.w_count + z * self.w_count * self.h_count

                            # check if there is already a valid point in the cell
                            if index >= 0 and index <= len(valid_points) - 1:
                                if valid_points[index]:
                                    neighbor = self.point_data.position[
                                        valid_points[index]]

                                else:
                                    continue
                            else:
                                raise RuntimeError
                                continue

                            # check distance to the next neighbour
                            # if it conflicts tag the point invalid and break
                            # out of the loop
                            distance = point.distanceTo(
                                neighbor)  # + min_radius / 10
                            if distance < min_radius:  # - (min_radius / 10) :
                                valid = False
                                break

                        if valid is False:
                            break
                    if valid is False:
                        break

                if valid:
                    found = True
                    valid_points[key] = point_index
                    active.append(point_index)

            if not found:
                active.remove(p_active)
                #  active.pop(p_active_index)

        return [i for i in valid_points if i is not None]
Beispiel #14
0
    def disk_sampling_2d(self, radius, u=1, v=1):
        """ sample poisson disk samples in uv space
        note: the given radius must be between 0 and 1 """

        # TODO - make uv sample space editable for user

        grid = []
        active = []
        ordered = []

        cellsize = radius / math.sqrt(2)

        col = int(u / cellsize)
        row = int(v / cellsize)

        grid = [None] * col * row

        #  x = random.random() * u
        #  y = random.random() * v
        #  since x=1 + y=1 would raise an index err let's assume the first point
        #  at 0.5 and 0.5
        x = 0.5
        y = 0.5
        pos = (x, y)

        current_col = int(x / cellsize)
        current_row = int(y / cellsize)

        try:
            grid[current_col + current_row * col] = pos
        except IndexError as e:
            # this should not happen any more since the initial point is at
            # 0.5 / 0.5 but just in case let's log what causes the index error
            self.logger.error(
                'Error intializing 2d poisson sampler. '
                'grid of len({}) build with {} columns, {} rows. '
                'Initial index is: {}, {}'.format(len(grid), col, row,
                                                  current_col, current_row))
            raise IndexError(e)

        active.append(pos)
        ordered.append(pos)

        while len(active) > 0:

            rand_index = int(len(active) - 1)
            active_pos = active[rand_index]
            found = False

            k = 30
            for each in xrange(k):

                # find a new point withhin a the range of radius - 2r
                rand_distance = random.uniform(radius, 2 * radius)
                theta = 360 * random.random()
                offset_x = math.cos(theta) * rand_distance
                offset_y = math.sin(theta) * rand_distance

                # get the absolut position of the new pos
                new_x = active_pos[0] + offset_x
                new_y = active_pos[1] + offset_y
                new_pos = (new_x, new_y)

                # get the new col & row position of the point
                active_col = int(new_x / cellsize)
                active_row = int(new_y / cellsize)

                if active_col > 0 \
                and active_row > 0 \
                and active_col < col \
                and active_row < row \
                and not grid[active_col + active_row * col]:

                    valid = True
                    for j in xrange(-1, 2):
                        for f in xrange(-1, 2):
                            index = (active_col + j) + (active_row + f) * col
                            try:
                                neighbor = grid[index]
                            except IndexError:
                                continue
                            if neighbor:
                                distance = math.sqrt(
                                    (neighbor[0] - new_pos[0])**2 +
                                    (neighbor[1] - new_pos[1])**2)
                                if (distance < radius):
                                    valid = False

                    if valid:
                        found = True
                        grid[active_col + active_row * col] = new_pos
                        active.append(new_pos)
                        ordered.append(new_pos)

                        # TODO

            if not found:
                active.pop(rand_index)

        self.point_data.set_length(len(ordered))

        util = om.MScriptUtil()
        in_mesh = node_utils.get_connected_in_mesh(self.target, False)
        mesh_fn = om.MFnMesh(in_mesh)

        for i, (u_coord, v_coord) in enumerate(ordered):

            face_ids = self.geo_cache.get_close_face_ids(u_coord, 1 - v_coord)

            position = om.MPoint()
            normal = om.MVector()
            for face_id in face_ids:
                util.createFromList([u_coord, 1 - v_coord], 2)
                ptr = util.asFloat2Ptr()

                try:
                    mesh_fn.getPointAtUV(face_id, position, ptr,
                                         om.MSpace.kWorld)
                    mesh_fn.getClosestNormal(position, normal)
                    self.point_data.set(i, position, normal, 1, u_coord,
                                        1 - v_coord)
                except:
                    continue