mieki256's diary



2017/09/28(木) [n年前の日記]

#1 [pi3d][python][raspberrypi] pi3dで独自モデルを描画その2

pi3d で独自モデルを描画する実験をしていたけれど。単にグルグル回してるだけではなんだか物足りないよなと思えてきたので、少しソレっぽい描画を、と。

とりあえず、Raspberry Pi Zero W 上で動かしてみたり。



結構滑らかに動いてくれた。もっとも、簡単な背景モデル x 2 + スプライト x 1枚を描画してるだけだから…。おそらく弾を撃ったらそれだけで処理落ちしそう。

ソース。 :

ソースとモデルデータは以下。CC0ってことで。

_draw_model2.py
_airplane_01_64x64.png
_models.zip

モデルデータは、解凍して、models/*.obj な場所になるように置く。

u"""
pi3d draw model(obj + mtl) sample.

モデルデータ(.obj + .mtl)を描画してみるサンプルその2。
街っぽいモデルデータを読み込んで動かしつつ、
手前のスプライトをWASDキーで移動。
Press ESC key to exit.

- Windows10 x64 + Python 2.7.12 32bit + pi3d 2.21
- Raspberry Pi Zero W + raspbian stretch + pi3d 2.21

Author : mieki256
License : CC0
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import math
import pi3d
import pygame


class KeyboardStatus(object):
    u"""Keyboard status."""

    # referenc :
    # pi3d/event/Keys.py
    # https://www.pygame.org/docs/ref/key.html
    keys_list = {
        "KEY_ESC": pygame.K_ESCAPE,
        "KEY_A": pygame.K_a, "KEY_B": pygame.K_b, "KEY_C": pygame.K_c,
        "KEY_D": pygame.K_d, "KEY_E": pygame.K_e, "KEY_F": pygame.K_f,
        "KEY_G": pygame.K_g, "KEY_H": pygame.K_h, "KEY_I": pygame.K_i,
        "KEY_J": pygame.K_j, "KEY_K": pygame.K_k, "KEY_L": pygame.K_l,
        "KEY_M": pygame.K_m, "KEY_N": pygame.K_n, "KEY_O": pygame.K_o,
        "KEY_P": pygame.K_p, "KEY_Q": pygame.K_q, "KEY_R": pygame.K_r,
        "KEY_S": pygame.K_s, "KEY_T": pygame.K_t, "KEY_U": pygame.K_u,
        "KEY_V": pygame.K_v, "KEY_W": pygame.K_w, "KEY_X": pygame.K_x,
        "KEY_Y": pygame.K_y, "KEY_Z": pygame.K_z,
        "KEY_0": pygame.K_0, "KEY_1": pygame.K_1, "KEY_2": pygame.K_2,
        "KEY_3": pygame.K_3, "KEY_4": pygame.K_4, "KEY_5": pygame.K_5,
        "KEY_6": pygame.K_6, "KEY_7": pygame.K_7, "KEY_8": pygame.K_8,
        "KEY_9": pygame.K_9,
        "KEY_UP": pygame.K_UP, "KEY_DOWN": pygame.K_DOWN,
        "KEY_LEFT": pygame.K_LEFT, "KEY_RIGHT": pygame.K_RIGHT,
        "KEY_SPACE": pygame.K_SPACE
    }

    class PygameKeyStatus(object):
        u"""Keyboard status with pygame."""

        def __init__(self):
            """Init."""
            pygame.init()

        def update(self):
            """Update."""
            pygame.event.pump()
            self.keys = pygame.key.get_pressed()
            pygame.event.clear()

        def key_pressed(self, key_str):
            """Get key press state.

            @param key_str key kind string
            @return True or False

            """
            return self.keys[KeyboardStatus.keys_list[key_str]]

        def close(self):
            """Close."""
            pygame.quit()

    class Pi3dInputKeyState(object):
        """Keyboard status width pi3d.InputEvents."""

        def __init__(self):
            """Init."""
            self.inputs = pi3d.InputEvents()

        def update(self):
            """Update."""
            self.inputs.do_input_events()

        def key_pressed(self, key_str):
            """Get key press state.

            @param key_str key kind string
            @return True or False

            """
            return (self.inputs.key_state(key_str) != 0)

        def close(self):
            """Close."""
            pass

    def __init__(self):
        """Init."""
        self.keyboard = pi3d.Keyboard()
        if pi3d.USE_PYGAME:
            self.state = KeyboardStatus.PygameKeyStatus()
        else:
            self.state = KeyboardStatus.Pi3dInputKeyState()

    def update(self):
        """Update."""
        self.pikey = self.keyboard.read()
        self.state.update()

    def key_pressed(self, key_str):
        """Key press check.

        @param key_str key kind
        @return True or False

        """
        return self.state.key_pressed(key_str)

    def esckey_pressed(self):
        """ESC key press check.

        @return True or False

        """
        return (self.pikey == 27 or self.key_pressed("KEY_ESC"))

    def close(self):
        u"""Close."""
        self.keyboard.close()
        self.state.close()


display = pi3d.Display.create(frames_per_second=60)

# ライトを設定. position, color, ambientを指定
light = pi3d.Light(lightpos=(10.0, -10.0, -5.0),
                   lightcol=(1.0, 1.0, 1.0),
                   lightamb=(0.3, 0.3, 0.3),
                   is_point=False)

# シェーダを生成
shader = pi3d.Shader("uv_light")
shader_flat = pi3d.Shader("uv_flat")

# カメラ生成
camera_bg = pi3d.Camera(at=(0, 0, 0), eye=(0.0, 15.0, -4.0))
camera_flat = pi3d.Camera(is_3d=True)

# 背景モデルデータを読み込み
model_path = "models/tiny_city.obj"
my_model0 = pi3d.Model(camera=camera_bg, light=light, file_string=model_path)
my_model1 = pi3d.Model(camera=camera_bg, light=light, file_string=model_path)

# 背景モデルのシェーダを設定
my_model0.set_shader(shader)
my_model1.set_shader(shader)

# 背景モデル位置を初期化
bg_x, bg_y, bg_z = 0.0, 0.0, 20.0
my_model0.position(bg_x, bg_y, bg_z)
my_model1.position(bg_x, bg_y, bg_z - 20.0)

# 背景モデルを少し回転
# my_model0.rotateIncZ(10)
# my_model1.rotateIncZ(10)

# 手前に重ねるスプライトを生成
x, y, z = 0.0, 0.0, 1.0
tex = pi3d.Texture("airplane_01_64x64.png")
spr = pi3d.ImageSprite(tex, shader_flat, w=0.1, h=0.1, camera=camera_flat)
display.add_sprites(spr)
spr.position(x, y, z)

inputs = KeyboardStatus()

# スプライトが画面外に移動しないように境界値を算出
y_limit = 0.45
x_limit = y_limit * float(display.width) / float(display.height)
y_limit -= 0.05
x_limit -= 0.05

# メインループ
while display.loop_running():

    inputs.update()

    camera_bg.reset()
    camera_flat.reset()

    # BGモデルを描画
    my_model0.draw()
    my_model1.draw()

    # BGモデルを移動
    my_model0.position(bg_x, bg_y, bg_z)
    my_model1.position(bg_x, bg_y, bg_z - 20.0)
    bg_z -= 0.3
    if bg_z <= 0.0:
        bg_z += 20.0

    # 手前のスプライトの座標をキー入力で変更
    spd = 0.01
    ang = -1
    if inputs.key_pressed("KEY_A") or inputs.key_pressed("KEY_LEFT"):
        ang = 180
    elif inputs.key_pressed("KEY_D") or inputs.key_pressed("KEY_RIGHT"):
        ang = 0
    if inputs.key_pressed("KEY_W") or inputs.key_pressed("KEY_UP"):
        if ang == 0:
            ang = 45
        elif ang == 180:
            ang = 180 - 45
        else:
            ang = 90
    elif inputs.key_pressed("KEY_S") or inputs.key_pressed("KEY_DOWN"):
        if ang == 0:
            ang = 360 - 45
        elif ang == 180:
            ang = 180 + 45
        else:
            ang = 270
    if ang >= 0:
        rad = math.radians(ang)
        x += spd * math.cos(rad)
        y += spd * math.sin(rad)

    # 画面外に移動しないように座標を補正
    if y >= y_limit:
        y = y_limit
    elif y <= -y_limit:
        y = -y_limit
    if x >= x_limit:
        x = x_limit
    elif x <= -x_limit:
        x = -x_limit

    # スプライトの座標を設定
    spr.position(x, y, z)

    if inputs.esckey_pressed():
        inputs.close()
        display.destroy()
        break

キー入力処理で、ちょっと長くなっちゃってるけど…。そこだけ別ファイルに分けたほうがいいのだろうけど。ていうかコレ、gistに置くべきだったか…。

少し説明。 :

スプライトをWASDキー or カーソルキーで動かせるようにしてみたけれど、画面外に出ていかないように補正するあたりで悩んだり。

色々試してみたけれど、透視変換で描画する場合、ウインドウの縦幅 = 1.0 として扱われる雰囲気。まあ、z値によって1.0より大きい範囲になるけど…。なので、y座標の範囲は、0.5 〜 0.0 〜 -0.5 ぐらいの値を目安にしつつ少し調整、みたいな。

x座標の範囲は…。display.width と display.height でウインドウの横幅・縦幅をドット単位で取得できるので、縦幅を1.0とした際の比率を求めて、それを使ってx座標の範囲も算出、みたいな。

以上、1 日分です。

過去ログ表示

Prev - 2017/09 - Next
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project