Рубрики
Без рубрики

Блендер на кислоте

Blenderonacid Release V4 скоро появится и будет включать полный исходный код Minimario для образования … Tagged с блендером, Python.

Blenderonacid Release V4 скоро появится и будет включать полный исходный код Minimario для образовательных целей. В этом посте я расскажу, как работает большая часть этой демонстрации, вы узнаете о: Tpython, JavaScript, ODE, BPY и межпроцессной связи.

Blender имеет свой собственный встроенный текстовый редактор, а формат файла .blend может содержать текстовые объекты, на которые можно ссылаться другие объекты. Выше всего демонстрируется исходный код, наряду с сгенерированными 3D -данными. Чтобы узнать, как что -то работает, это помогает все это в одном файле .blend.

Лично мне не нравится писать код в текстовом редакторе Blender, и вместо этого предпочитаю полностью управляемый данными стиль, где я генерирую единый файл .blend из другого скрипта Python, этот скрипт генератора устанавливает сцену, создает текстовые объекты и ссылки их все вместе.

import bpy, os, sys, math

thispath = os.path.split(__file__)[0]

world = bpy.data.worlds[0]
world['threejs_canvas'] = None
script = bpy.data.texts.new(name='minimario_main')
world.threejs_script = script

src = open(os.path.join(thispath,'minimario/minimario_escapes_main.py'),'rb').read().decode('utf-8')
script.from_string( src )

js = bpy.data.texts.new(name='minimario_escapes_js')
src = open(os.path.join(thispath,'minimario/minimario_escapes_js.py'),'rb').read().decode('utf-8')
js.from_string( src )
script.include0 = js

shared = bpy.data.texts.new(name='minimario_shared')
src = open(os.path.join(thispath,'minimario/minimario_shared.py'),'rb').read().decode('utf-8')
shared.from_string( src )
script.include1 = shared

draw = bpy.data.texts.new(name='minimario_draw')
src = open(os.path.join(thispath,'minimario/minimario_draw.py'),'rb').read().decode('utf-8')
draw.from_string( src )
script.include2 = draw


blender_start = bpy.data.texts.new(name='minimario_startup')
world.blender_startup_script = blender_start
blender_start.include0 = shared

src = open(os.path.join(thispath,'minimario/minimario_startup.py'),'rb').read().decode('utf-8')
blender_start.from_string( src )

onupdate = '''
import math

M = bpy.data.objects['mario_collision']
MR = bpy.data.objects['marioroot']

if M.location[0] + 0.1 < M['px']:
    if MR.rotation_euler.z < math.radians( 180 ):
        MR.rotation_euler.z += 0.5
else:
    MR.rotation_euler.z *= 0.75

M['px'] = M.location[0]

if M.location.x > 9 and M.location.z > 8 and M.location.z < 13:
    bname = 'brick-%s' % int(M.location.x)
    if bname not in bpy.data.objects:
        bname = 'brick-%s' % int(M.location.x+0.5)
    if bname not in bpy.data.objects:
        bname = 'brick-%s' % int(M.location.x-0.5)
    if bname in bpy.data.objects:
        brick = bpy.data.objects[ bname ]
        if 'broken' not in brick.keys():
            print('breaking new brick')
            brick['broken'] = True
            bpy.ops.ode.cell_fracture(object_name=bname)
'''

blender_onupdate = bpy.data.texts.new(name='minimario_onupdate')
world.blender_script = blender_onupdate
blender_onupdate.from_string(onupdate)


Выше вы можете видеть, что в основном этот скрипт просто считывает другие сценарии Python из подразделения и создает текстовые объекты. Некоторый код является встроенным, сценарий onupdate, он называется каждым кадром в блендере.

Выше Марио начинается в окне Emscripten | SDL, которое он прыгает в отладчик Emscripten, переходя от SDL, втянутых пикселей в искусство CSS ASCII, когда он покидает окно веб -браузера и входит в блендер, он превращается в 3D ASCII искусство. Затем он ломает несколько блоков, используя физику ODE и мой сценарий настраиваемого перелома ячеек. Наконец он возвращается в окно браузера и сбивает окно SDL снизу и прыгает обратно в окно SDL.

CSS Mario просто нарисован под окном SDL, его положение синхронизируется из TPYTHON с этим вызовом функции.

set_ascii_mario( B.getPosition()[0]-60, -float(B.getPosition()[1]+320), state["direction"] )

Переменная B это тело ODE, используемое для моделирования его физики. Определение set_ascii_mario ниже.

def set_ascii_mario(x,y, direction):
    bump *= 0.75
    canvas.style.top = (75 - bump) + 'px'
    if direction == 1:
        marioR.hidden = true
        mariodiv.hidden = false
        mariodiv.style.left = (x + canvas.offsetLeft) + 'px'
        mariodiv.style.top  = y + 'px'
    else:
        mariodiv.hidden = true
        marioR.hidden = false
        marioR.style.left = (x + canvas.offsetLeft) + 'px'
        marioR.style.top  = y + 'px'

Выше вы, вероятно, ожидали увидеть JavaScript вместо Python. В то время как чистый JavaScript может быть смешан с вашим сценарием Python, работающим в скомпилированном интерпретаторе Tpython WASM, предпочтительнее, чтобы вы писали свой код JS в стиле Python и позволили Tpython Toolchain перенести его в JS, это потому, что тогда Функция JS будет автоматически подвергаться вашему сценарию Tpython для легкого вызова.

На этом этапе вам может быть интересно, как позиция Марио синхронизирована с блендером. Это делается с использованием стандартного Xmlhttprequest.

def sendmario():
    var msg = [{"name":"mario_collision", "pos":[X,-Z,Y]}]
    var req = new XMLHttpRequest()
    req.addEventListener("load", sendmario)
    req.open("GET", "http://localhost:8080/update?" + JSON.stringify(msg))
    req.send()
setTimeout(sendmario, 100)

Наверху угоняет встроенный сервер Blenderonacid, обычно используемый для синхронизации трех объектов, но в этом случае мы не используем три. Вместо этого мы создали mario_collision объект заранее в сценарии запуска блендера, который является родителем 3D ASCII Mario. Ниже приведен сценарий запуска блендера.

import bpy, math, _bpy

ob = bpy.data.objects['Cube']
ob.name = 'mario_collision'
ob.display_type = "WIRE"
ob.ode_size[0] = 2
ob.ode_size[1] = 2
ob.ode_size[2] = 2
ob.ode_kinematic = True
_bpy.ode_add_object( ob )
ob['px'] = 0.0

root = bpy.data.objects.new(name='marioroot', object_data=None)
root.scale *= 0.12
bpy.context.collection.objects.link(root)
root.parent = ob


cam = bpy.data.objects['Camera']
cam.location = [10, -30, 5]
cam.rotation_euler = [math.radians(90), 0, 0]


def make_mario( asciiart, collection, root ):
    z = 16
    for ln in asciiart:
        x = -16
        for c in ln:
            x += 2
            if c == '_':
                continue
            pix = bpy.data.objects.new(name='pixel', object_data=bpy.data.meshes['Cube'].copy())
            if c not in bpy.data.materials:
                mat = bpy.data.materials.new(name=c)
                mat.use_nodes = False
                if c == '?':
                    r,g,b = MarioPal['%']
                else:
                    r,g,b = MarioPal[c]
                mat.diffuse_color = [r/255,g/255,b/255,1.0]
            else:
                mat = bpy.data.materials[c]
            pix.data.materials[0]=mat
            collection.objects.link(pix)
            pix.location = [x,0,z]
            pix.scale.y = 2
            pix.modifiers.new(name='bevel', type="BEVEL")
            pix.parent=root

        z -= 2


col = bpy.data.collections.new(name='mariopixels')
bpy.context.collection.children.link(col)
make_mario( Mario, col, root )

def make_bricks():
    mat = bpy.data.materials.new(name='brick')
    mat.use_nodes = False
    mat.diffuse_color = [0.57, 0.15, 0.14, 1.0]
    x = 10
    z = 10
    for i in range(4):
        brick = bpy.data.objects.new(name='brick-%s' %int(x), object_data=bpy.data.meshes['Cube'].copy())
        bpy.context.collection.objects.link(brick)
        brick.location = [x, 0, z]
        brick.data.materials[0] = mat
        brick.ode_size[0] = 2
        brick.ode_size[1] = 2
        brick.ode_size[2] = 2
        brick.ode_kinematic = True
        _bpy.ode_add_object( brick )
        x += 2

make_bricks()

Выше вы можете видеть, что я настраиваю вторичное симуляцию физики ODE в Blender. Основное моделирование физики ODE фактически работает в веб -браузере, отправляя обновления позиции для mario_collision к этой физике симуляции здесь, в Блендере. Обратите внимание, что в блендере я делаю эти тела ODE_KINEMATIC Это означает, что они имеют только столкновение и отвечают на обновления позиций с пользовательского интерфейса или встроенного веб -сервера.

Наконец, давайте рассмотрим код, который запускает кирпичи, чтобы сломаться, это происходит в основном сценарии блендера OnuPdate.

M = bpy.data.objects['mario_collision']

if M.location.x > 9 and M.location.z > 8 and M.location.z < 13:
    bname = 'brick-%s' % int(M.location.x)
    if bname not in bpy.data.objects:
        bname = 'brick-%s' % int(M.location.x+0.5)
    if bname not in bpy.data.objects:
        bname = 'brick-%s' % int(M.location.x-0.5)
    if bname in bpy.data.objects:
        brick = bpy.data.objects[ bname ]
        if 'broken' not in brick.keys():
            print('breaking new brick')
            brick['broken'] = True
            bpy.ops.ode.cell_fracture(object_name=bname)

Почему я обходил систему обратного выявления Callision ODE Callision и пишу уродливый код, как выше, чтобы вызвать перелом ячейки? Потому что это просто, и показывать вам простой трюк для обнаружения столкновений, где вы окружаете позиции, чтобы целые числа и ищите объект по этому округлению. Этот чит работает хорошо, если ваши игровые объекты имеют примерно одинакового размера.

Оригинал: “https://dev.to/djraptor11/blender-on-acid-29fm”