Пример #1
0
def obj_to_ursinamesh(path=application.compressed_models_folder,
                      outpath=application.compressed_models_folder,
                      name='*',
                      return_mesh=True,
                      save_to_file=False,
                      delete_obj=False):

    if name.endswith('.obj'):
        name = name[:-4]

    for f in path.glob(f'**/{name}.obj'):
        # filepath = path / (os.path.splitext(f)[0] + '.obj')
        print('read obj at:', f)

        with f.open('r') as file:
            lines = file.readlines()

        verts = []
        tris = []

        uv_indices = []
        uvs = []
        norm_indices = []
        norms = []
        normals = []  # final normals made getting norms with norm_indices

        vertex_colors = []
        current_color = None
        mtl_data = None
        mtl_dict = {}

        # parse the obj file to a Mesh
        for i, l in enumerate(lines):
            if l.startswith('v '):
                vert = [float(v) for v in l[2:].strip().split(' ')]
                vert[0] = -vert[0]
                verts.append(tuple(vert))

            elif l.startswith('vn '):
                n = l[3:].strip().split(' ')
                norms.append(tuple(float(e) for e in n))

            elif l.startswith('vt '):
                uv = l[3:].strip()
                uv = uv.split(' ')
                uvs.append(tuple(float(e) for e in uv))

            elif l.startswith('f '):
                l = l[2:]
                l = l.split(' ')

                try:
                    tri = tuple(
                        int(t.split('/')[0]) - 1 for t in l if t != '\n')
                except:
                    print_warning('error in obj file line:', i, ':', l)
                    return
                if len(tri) == 3:
                    tris.extend(tri)
                    if current_color:
                        vertex_colors.extend([current_color for i in range(3)])

                elif len(tri) == 4:
                    tris.extend(
                        (tri[0], tri[1], tri[2], tri[2], tri[3], tri[0]))
                    if current_color:
                        vertex_colors.extend([current_color for i in range(6)])

                else:  # ngon
                    for i in range(1, len(tri) - 1):
                        tris.extend((tri[i], tri[i + 1], tri[0]))
                    if current_color:
                        vertex_colors.extend(
                            [current_color for i in range(len(tri))])

                try:
                    uv = tuple(int(t.split('/')[1]) - 1 for t in l)
                    if len(uv) == 3:
                        uv_indices.extend(uv)
                    elif len(uv) == 4:
                        uv_indices.extend(
                            (uv[0], uv[1], uv[2], uv[2], uv[3], uv[0]))
                    else:  # ngon
                        for i in range(1, len(uv) - 1):
                            uv_indices.extend((uv[i], uv[i + 1], uv[0]))
                except:  # if no uvs
                    pass

                try:
                    n = tuple(int(t.split('/')[2]) - 1 for t in l)
                    if len(n) == 3:
                        norm_indices.extend(n)
                    elif len(uv) == 4:
                        norm_indices.extend(
                            (n[0], n[1], n[2], n[2], n[3], n[0]))
                    else:  # ngon
                        for i in range(1, len(n) - 1):
                            norm_indices.extend((n[i], n[i + 1], n[0]))
                except:  # if no normals
                    pass

            elif l.startswith('mtllib '):  # load mtl file
                mtl_file_name = Path(str(f).rstrip('.obj') + '.mtl')
                if mtl_file_name.exists():
                    with open(mtl_file_name) as mtl_file:
                        mtl_data = mtl_file.readlines()

                        for i in range(len(mtl_data) - 1):
                            if mtl_data[i].startswith('newmtl '):
                                material_name = mtl_data[i].strip()[
                                    7:]  # remove 'newmtl '
                                for j in range(i + 1,
                                               min(i + 8, len(mtl_data))):
                                    if mtl_data[j].startswith('newmtl'):
                                        break
                                    if mtl_data[j].startswith('Kd '):
                                        material_color = [
                                            float(e) for e in
                                            mtl_data[j].strip()[3:].split(' ')
                                        ]
                                        mtl_dict[
                                            material_name] = *material_color, 1

            elif l.startswith('usemtl ') and mtl_data:  # apply material color
                material_name = l[7:].strip()  # remove 'usemtl '
                if material_name in mtl_dict:
                    current_color = mtl_dict[material_name]

        if norms:  # make sure we have normals and not just normal indices (weird edge case).
            normals = [(-norms[nid][0], norms[nid][1], norms[nid][2])
                       for nid in norm_indices]

        if return_mesh:
            return Mesh(vertices=[verts[t] for t in tris],
                        normals=normals,
                        uvs=[uvs[uid] for uid in uv_indices],
                        colors=vertex_colors)

        meshstring = ''
        meshstring += 'Mesh('

        meshstring += '\nvertices='
        meshstring += str(tuple(verts[t] for t in tris))

        if vertex_colors:
            meshstring += '\ncolors='
            meshstring += str(tuple(col for col in vertex_colors))

        if uv_indices:
            meshstring += ', \nuvs='
            meshstring += str(tuple(uvs[uid] for uid in uv_indices))

        if normals:
            meshstring += ', \nnormals='
            meshstring += str(normals)

        meshstring += ''', \nmode='triangle')'''

        if not save_to_file:
            return meshstring

        outfilepath = outpath / (os.path.splitext(f)[0] + '.ursinamesh')
        with open(outfilepath, 'w') as file:
            file.write(meshstring)

        if delete_obj:
            os.remove(filepath)

        print_info('saved ursinamesh to:', outfilepath)
Пример #2
0
class Raycaster(Entity):
    line_model = Mesh(vertices=[Vec3(0, 0, 0), Vec3(0, 0, 1)], mode='line')
    _boxcast_box = Entity(model='cube',
                          origin_z=-.5,
                          collider='box',
                          color=color.white33,
                          enabled=False)

    def __init__(self):
        super().__init__(name='raycaster', eternal=True)
        self._picker = CollisionTraverser()  # Make a traverser
        self._pq = CollisionHandlerQueue()  # Make a handler
        self._pickerNode = CollisionNode('raycaster')
        self._pickerNode.set_into_collide_mask(0)
        self._pickerNP = self.attach_new_node(self._pickerNode)
        self._picker.addCollider(self._pickerNP, self._pq)

    def distance(self, a, b):
        return sqrt(sum((a - b)**2 for a, b in zip(a, b)))

    def raycast(self,
                origin,
                direction=(0, 0, 1),
                distance=inf,
                traverse_target=scene,
                ignore=list(),
                debug=False):
        self.position = origin
        self.look_at(self.position + direction)

        self._pickerNode.clearSolids()
        ray = CollisionRay()
        ray.setOrigin(Vec3(0, 0, 0))
        ray.setDirection(Vec3(0, 0, 1))

        self._pickerNode.addSolid(ray)

        if debug:
            temp = Entity(position=origin,
                          model=Raycaster.line_model,
                          scale=Vec3(1, 1, min(distance, 9999)),
                          add_to_scene_entities=False)
            temp.look_at(self.position + direction)
            destroy(temp, 1 / 30)

        self._picker.traverse(traverse_target)

        if self._pq.get_num_entries() == 0:
            self.hit = HitInfo(hit=False, distance=distance)
            return self.hit

        ignore += tuple([e for e in scene.entities if not e.collision])

        self._pq.sort_entries()
        self.entries = [  # filter out ignored entities
            e for e in self._pq.getEntries()
            if e.get_into_node_path().parent not in ignore and self.distance(
                self.world_position, Vec3(
                    *e.get_surface_point(render))) <= distance
        ]

        if len(self.entries) == 0:
            self.hit = HitInfo(hit=False, distance=distance)
            return self.hit

        self.collision = self.entries[0]
        nP = self.collision.get_into_node_path().parent
        point = Vec3(*self.collision.get_surface_point(nP))
        world_point = Vec3(*self.collision.get_surface_point(render))
        hit_dist = self.distance(self.world_position, world_point)

        self.hit = HitInfo(hit=True, distance=distance)
        for e in scene.entities:
            if e == nP:
                self.hit.entity = e

        nPs = [e.get_into_node_path().parent for e in self.entries]
        self.hit.entities = [e for e in scene.entities if e in nPs]

        self.hit.point = point
        self.hit.world_point = world_point
        self.hit.distance = hit_dist

        self.hit.normal = Vec3(*self.collision.get_surface_normal(
            self.collision.get_into_node_path().parent).normalized())
        self.hit.world_normal = Vec3(
            *self.collision.get_surface_normal(render).normalized())
        return self.hit

        self.hit = HitInfo(hit=False, distance=distance)
        return self.hit

    def boxcast(self,
                origin,
                direction=(0, 0, 1),
                distance=9999,
                thickness=(1, 1),
                traverse_target=scene,
                ignore=list(),
                debug=False):  # similar to raycast, but with width and height
        if isinstance(thickness, (int, float, complex)):
            thickness = (thickness, thickness)

        Raycaster._boxcast_box.enabled = True
        Raycaster._boxcast_box.collision = True
        Raycaster._boxcast_box.position = origin
        Raycaster._boxcast_box.scale = Vec3(abs(thickness[0]),
                                            abs(thickness[1]), abs(distance))
        Raycaster._boxcast_box.always_on_top = debug
        Raycaster._boxcast_box.visible = debug

        Raycaster._boxcast_box.look_at(origin + direction)
        hit_info = Raycaster._boxcast_box.intersects(
            traverse_target=traverse_target, ignore=ignore)
        if hit_info.world_point:
            hit_info.distance = ursinamath.distance(origin,
                                                    hit_info.world_point)
        else:
            hit_info.distance = distance

        if debug:
            Raycaster._boxcast_box.collision = False
            Raycaster._boxcast_box.scale_z = hit_info.distance
            invoke(setattr, Raycaster._boxcast_box, 'enabled', False, delay=.2)
        else:
            Raycaster._boxcast_box.enabled = False

        return hit_info
Пример #3
0
def obj_to_ursinamesh(path=application.compressed_models_folder,
                      outpath=application.compressed_models_folder,
                      name='*',
                      return_mesh=True,
                      save_to_file=False,
                      delete_obj=False):

    if name.endswith('.obj'):
        name = name[:-4]

    for f in path.glob(f'**/{name}.obj'):
        filepath = path / (os.path.splitext(f)[0] + '.obj')
        print('read obj at:', filepath)

        with open(filepath, 'r') as file:
            lines = file.readlines()

        verts = list()
        tris = list()

        uv_indices = list()
        uvs = list()
        norm_indices = list()
        norms = list()

        # parse the obj file to a Mesh
        for i, l in enumerate(lines):
            if l.startswith('v '):
                vert = [float(v) for v in l[2:].strip().split(' ')]
                vert[0] = -vert[0]
                verts.append(tuple(vert))

            elif l.startswith('vn '):
                n = l[3:].strip().split(' ')
                norms.append(tuple([float(e) for e in n]))

            elif l.startswith('vt '):
                uv = l[3:].strip()
                uv = uv.split(' ')
                uvs.append(tuple([float(e) for e in uv]))

            elif l.startswith('f '):
                l = l[2:]
                l = l.split(' ')

                try:
                    tri = tuple(
                        [int(t.split('/')[0]) - 1 for t in l if t != '\n'])
                except:
                    print('error in obj file line:', i, ':', l)
                    return
                if len(tri) == 3:
                    tris.extend(tri)
                elif len(tri) == 4:
                    tris.extend(
                        (tri[0], tri[1], tri[2], tri[2], tri[3], tri[0]))
                else:  # ngon
                    for i in range(1, len(tri) - 1):
                        tris.extend((tri[i], tri[i + 1], tri[0]))

                try:
                    uv = tuple([int(t.split('/')[1]) - 1 for t in l])
                    if len(uv) == 3:
                        uv_indices.extend(uv)
                    elif len(uv) == 4:
                        uv_indices.extend(
                            (uv[0], uv[1], uv[2], uv[2], uv[3], uv[0]))
                    else:  # ngon
                        for i in range(1, len(uv) - 1):
                            uv_indices.extend((uv[i], uv[i + 1], uv[0]))
                except:  # if no uvs
                    pass

                try:
                    n = tuple([int(t.split('/')[2]) - 1 for t in l])
                    if len(n) == 3:
                        norm_indices.extend(n)
                    elif len(uv) == 4:
                        norm_indices.extend(
                            (n[0], n[1], n[2], n[2], n[3], n[0]))
                    else:  # ngon
                        for i in range(1, len(n) - 1):
                            norm_indices.extend((n[i], n[i + 1], n[0]))
                except:  # if no normals
                    pass

        if return_mesh:
            return Mesh(vertices=[verts[t] for t in tris],
                        normals=[norms[nid] for nid in norm_indices],
                        uvs=[uvs[uid] for uid in uv_indices])

        meshstring = ''
        meshstring += 'Mesh('

        meshstring += '\nvertices='
        meshstring += str(tuple([verts[t] for t in tris]))

        if uv_indices:
            meshstring += ', \nuvs='
            meshstring += str(tuple([uvs[uid] for uid in uv_indices]))

        if norm_indices:
            meshstring += ', \nnormals='
            meshstring += str(tuple([norms[nid] for nid in norm_indices]))

        meshstring += ''', \nmode='triangle')'''

        if not save_to_file:
            return meshstring

        outfilepath = outpath / (os.path.splitext(f)[0] + '.ursinamesh')
        with open(outfilepath, 'w') as file:
            file.write(meshstring)

        if delete_obj:
            os.remove(filepath)

        print('saved ursinamesh to:', outfilepath)