Exemple #1
0
    def move(obj, d):
        # type: (Pivot, Point3D) -> Pivot

        res = []
        for sg in obj.content:
            assert isinstance(sg, SceneGeometry)
            sg = deepcopy(sg)
            g = sg.geometry  # type: Geometry
            for i, v in enumerate(g.verts):
                g.verts[i] = d + v

            res.append(sg)

        return Pivot(*res)
Exemple #2
0
def WavefrontObj(fn, special=False, exclude=()):
    # type: (str, bool) -> Pivot
    """
    Загружает WavefrontObj
    Каждый usemtl в obj порождает отдельный объект SceneGeometry
    раскрашенный/текстуированный/прозрачный в соответствиями с настройками

    Некоторые объекты (например, набор вертексов) шарятся между разными геометриями.

    При рендеринге это не важно, но при манипуляции с объектами (изменении) это
    необходимо учитывать.

    :param fn: Имя файла todo: ЮНИКОД НЕ ПОДДЕРЖИВАЕТСЯ!!!
    :param special: Если None, то загружаются все поверхности; если False, то загружаются
        все поверхности, за исключением специальный, чбе имя начинаяется на __; если
        True, то загружаются только специальные поверхности.
    :param exclude: список поверхностей, которые не надо загружать

    """

    fd, _ = os.path.split(fn)

    objname = None
    verts = []
    tverts = []
    nverts = []

    parts = []  # type: List[Geometry]
    surfaces = []

    mtls = None

    material_opacue = Material(transparent=True)

    cur_o = None
    cur_s = None

    with open(fn) as f:

        for ln, l in enumerate(f):
            a = l.rstrip().split(' ')

            k = a[0]

            if k == 'o':
                assert not objname
                objname = a[1]

            elif k == 'mtllib':
                mtls = ParseMtl(fd + '/' + a[1])

            elif k == 'v':
                verts.append(map(lambda x: float(x) * 1000.0, a[1:4]))
            elif k == 'vt':
                u = float(a[1])
                v = float(a[2])
                tverts.append((u, 1.0 - v))
            elif k == 'vn':
                nverts.append(map(float, a[1:4]))

            elif k == 'usemtl':
                parts.append(Geometry(verts=verts, ))
                surfaces.append(a[1])
            elif k == 's':

                cur_s = a[1]

            elif k == 'f':
                assert len(parts)
                cur_o = parts[len(parts) - 1]  # type: Geometry

                face = []
                nface = []
                tface = []

                oface = a[1:]
                for vert in oface:
                    vert = vert.split('/')
                    while len(vert) < 3:
                        vert.append('')
                    vert, tvert, nvert = [
                        int(vert) - 1 if vert else -1 for vert in vert
                    ]
                    face.append(vert)
                    if tvert != -1: tface.append(tvert)
                    if nvert != -1: nface.append(nvert)

                if nface: assert len(nface) == len(face)
                if tface: assert len(tface) == len(face)

                # print face, nface, tface

                if cur_o.faces is None:
                    cur_o.faces = []
                cur_o.faces.append(face)

                if nface and cur_s != 'off':
                    if cur_o.nfaces is None:
                        assert nverts
                        cur_o.nverts = nverts
                        cur_o.nfaces = []
                    cur_o.nfaces.append(nface)

                if tface:
                    if cur_o.tfaces is None:
                        assert tverts
                        cur_o.tverts = tverts
                        cur_o.tfaces = []
                    cur_o.tfaces.append(tface)

        geometries = []

        textures = {}
        # for mat_name, params in mtls.items():
        #     if 'map_Kd' in params:
        #         textures[mat_name] = Texture(params['map_Kd']).Name(mat_name)

        for p, surface in zip(parts, surfaces):
            if surface in exclude: continue
            if special is not None:
                if not special and surface.startswith('__'): continue
                if special and not surface.startswith('__'): continue

            o = SceneGeometry(p.calcFNorms())  # type: SceneGeometry

            o.Color(mtls[surface]['Kd'])

            a = mtls[surface]['Ka']
            if a[0] > 0.0:
                mat = Material(emission=a[0])
                o.Material(mat)

            d = mtls[surface]['d']
            if d < 1.0:
                o.Alpha(d).Material(material_opacue)

            t = mtls[surface].get('map_Kd')
            if t: o.Texture(Texture(t))

            geometries.append(o)

    return Pivot(*geometries)
Exemple #3
0
def ParseGeometry(obj):
    # type: (Pivot, int, int) -> List[Pivot]
    """
    Разбирает объект на непересекающиеся компоненты (в плоскости XY с точностью до
    обрамляющего прямоугольника)

    :param obj: Пивот с геометрией
    :return: Список объектов, каждый центрирован
    """

    res = []

    bl = BoxList()

    for sgi, sg in enumerate(obj.content):

        assert isinstance(sg, SceneGeometry)

        g = sg.geometry

        # каждой грани сопоставим бокс, в которой она лежит
        # по мере добавления новых граней боксы сливаются

        for vi, face in enumerate(g.faces):
            verts = [(v[0], v[1]) for v in [g.verts[i] for i in face]]
            bl.AddFace(verts, [sgi, vi])

    # сливаем боксы
    bl.Process()

    # разделяем геометрию

    res = []

    for box in bl.boxes:

        content = list(deepcopy(obj.content))  # type:List[SceneGeometry]
        # content = [deepcopy(sg) for sg in obj.content]

        # пометим все используемые грани
        for sg in content:
            sg.geometry.marks = [False] * len(sg.geometry.faces)

        for sgi, vi in box.ifaces:
            # noinspection PyUnresolvedReferences
            content[sgi].geometry.marks[vi] = True

        # уберем все неиспользуемые грани
        for i in range(len(content)):

            g = content[i].geometry  # type: Geometry
            g.fnorms = list(g.fnorms)
            vi = 0
            while vi < len(g.faces):
                # noinspection PyUnresolvedReferences
                if g.marks[vi]:
                    vi += 1
                else:
                    # noinspection PyUnresolvedReferences
                    g.marks.pop(vi)
                    g.faces.pop(vi)
                    if g.tfaces: g.tfaces.pop(vi)
                    if g.nfaces: g.nfaces.pop(vi)
                    if g.fnorms: g.fnorms.pop(vi)

            delattr(g, 'marks')
            # если ни одной грани не осталось удалим объект
            if not g.faces:
                content[i] = None
            else:
                # Сдвигаем вертексы чтобы центрировать объект, неиспользуемые вертексы
                # обнулим (т.е. поместим в центр, чтобы не удалять и не переписывать
                # индексы в гранях) чтобы корректно рассчитывались размеры объекта

                # Т.к. изначально объекты шарят один и тот же набор вертексов - копируем его

                g.verts = deepcopy(g.verts)

                marks = [False] * len(g.verts)
                for face in g.faces:
                    for vi in face:
                        marks[vi] = True

                for vi in range(len(g.verts)):

                    if marks[vi]:
                        g.verts[vi][0] -= box.center.x
                        g.verts[vi][1] -= box.center.y
                    else:
                        g.verts[vi][0] = g.verts[vi][1] = 0.0

        res.append(Pivot(*filter(lambda o: bool(o), content)))

    return res
Exemple #4
0
def SmartResize(obj, sx=None, sy=None, sz=None, change_uv=True, rcenter=False):
    # type: (Pivot, float, float, float, True) -> Pivot
    """
    Выполняет "умное" масштабирование с изменением текстурных координат, чтобы размер
    накладываемой текстуры оставался неизменным (маппинг предполагается планарным)
    Объект растягивается или сжимается так, что новые размеры совпадают с заданными,
    а пропорции относительно нулевых плоскостей сохраняются.

    todo: дикие глюки от того что списки вертексов шарятся! Очень внимательно надо!
    todo: всю геометрию дипкопить!

    Args:

        obj: Что резайзим
        sx, sy, sz: Новый размер по соотв. оси или None если размер неизменный
        change_uv: менять текстурные координаты так, чтобы текстура не искажалась, а тайлилась.
        rcenter:
            False - резайз выполняется относительно нулевых плоскостей
            True  - относительно центра объекта

    Returns:
        Pivot с масштабированным объектом
    """
    def move(obj, d):
        # type: (Pivot, Point3D) -> Pivot

        res = []
        for sg in obj.content:
            assert isinstance(sg, SceneGeometry)
            sg = deepcopy(sg)
            g = sg.geometry  # type: Geometry
            for i, v in enumerate(g.verts):
                g.verts[i] = d + v

            res.append(sg)

        return Pivot(*res)

    center = obj.gcenter

    if rcenter:
        obj = move(obj, -center)

    res = []

    b0, b1 = obj.gbounds

    old_size = b1 - b0

    sx = sx or old_size.x
    sy = sy or old_size.y
    sz = sz or old_size.z

    new_size = Point3D(sx, sy, sz)

    d_plus = XYZ(0.0, 0.0, 0.0)
    d_minus = XYZ(0.0, 0.0, 0.0)

    for i in [0, 1, 2]:
        if b1[i] > TOL:
            ratio = b1[i] / old_size[i] if b0[i] < -TOL else 1.0

            d_plus[i] = (new_size[i] - old_size[i]) * ratio
            d_minus[i] = -(new_size[i] - old_size[i]) * (1.0 - ratio)

        elif b0[i] < -TOL:
            ratio = -b0[i] / old_size[i] if b1[i] > TOL else 1.0

            d_plus[i] = (new_size[i] - old_size[i]) * (1.0 - ratio)
            d_minus[i] = -(new_size[i] - old_size[i]) * ratio

    for sg in obj.content:
        assert isinstance(sg, SceneGeometry)
        sg = deepcopy(sg)
        g = sg.geometry

        # most difficult part - transform u,v correctly, we imply planar mapping
        if g.tfaces:

            # first, we need to remove tfaces, but leave tverts as 1-to-1 to verts
            # such conversion is not always correct, but mostly correct for planar map

            tverts = [[0, 0]] * len(g.verts)
            for f, tf in zip(g.faces, g.tfaces):
                for fi, tfi in zip(f, tf):
                    tverts[fi] = g.tverts[tfi]

            g.tfaces = None
            g.tverts = tverts

        # then we need to define du/sdx dv/sdx etc

        uv_by = XYZ(PointUV(0, 0), PointUV(0, 0), PointUV(0, 0))

        if g.tverts:

            assert len(g.tverts) == len(g.verts)

            for f in g.faces:
                for i in range(1, len(f)):
                    d = Point3D(*g.verts[f[i]]) - Point3D(*g.verts[f[i - 1]])
                    duv = PointUV(*g.tverts[f[i]]) - PointUV(*g.tverts[f[i -
                                                                         1]])

                    uv_by = Point3D(
                        duv / d.x if abs(d.x) > TOL else uv_by.x,
                        duv / d.y if abs(d.y) > TOL else uv_by.y,
                        duv / d.z if abs(d.z) > TOL else uv_by.z,
                    )

            # print uv_by.x, uv_by.y, uv_by.z

        def modify_v(v):
            return Point3D(*[
                v[i] + (d_plus[i]
                        if v[i] > TOL else d_minus[i] if v[i] < -TOL else 0.0)
                for i in [0, 1, 2]
            ])

        verts = []
        tverts = []
        for i in range(len(g.verts)):
            v0 = g.verts[i]
            v = modify_v(v0)
            verts.append(v)

            if g.tverts:
                if change_uv:
                    dxyz = v - v0
                    uv = sum([uv_by[_] * dxyz[_]
                              for _ in [0, 1, 2]], PointUV(0, 0)) + g.tverts[i]
                    tverts.append(tuple(uv))
                else:
                    tverts.append(g.tverts[i])

        g.verts = map(list, verts)
        g.tverts = tverts or None

        res.append(sg)

    obj = Pivot(*res)

    if rcenter:
        obj = move(obj, center * new_size / old_size)

    return obj