简体   繁体   English

Pyglet。 如何动态更改顶点的图片(动画)。 OpenGL的

[英]Pyglet. How to change picture(animate) for vertices dynamically. OpenGL

Environment: 环境:

Python: 3.6.6 Python:3.6.6
pyglet version: 1.3.2 pyglet版本:1.3.2

Code base: 代码库:

abstract_model.py abstract_model.py

import pyglet


def get_texture_group(file, order_group_index):
    texture = pyglet.image.load(file).texture
    order_group = pyglet.graphics.OrderedGroup(order_group_index)
    return pyglet.graphics.TextureGroup(texture, order_group)


class AbstractModel(object):

    def _create_as_vertex(self):
        v_x = self.cell_data.get("x") * 32
        v_y = self.cell_data.get("y") * -1 * 32

        texture_group = self.map_type_iamge.get(self.cell_data.get("t"))
        x_offset = self.x_offset * self.scale

        x, y, z = v_x + x_offset, v_y, self.z
        x_ = (texture_group.texture.width * self.scale + x_offset + v_x)
        y_ = (texture_group.texture.height * self.scale + v_y)

        tex_coords = ('t2f', (0, 0, 1, 0, 1, 1, 0, 1))

        self.vertices = self.batch.add(
            4, pyglet.gl.GL_QUADS,
            texture_group,
            ('v3f', (x, y, z,
                     x_, y, z,
                     x_, y_, z,
                     x, y_, z)),
            tex_coords)

    def _animate(self, dt):
        # lets assume that I have list of pyglet.graphics.TextureGroup
        # and they should somehow be drawn one after other
        print("I need change image. dt=", dt, self)
        pyglet.clock.schedule_once(self._animate, 1)

ground3d.py ground3d.py

import os
import pyglet

import settings
from models import abstract_model


GROUND_DIR = os.path.join(settings.STATIC_DIR, "ground")

order_group_index = 0

map_type_iamge = {
    1: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "w1.png"), order_group_index),
    2: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "t1.png"), order_group_index),
    1001: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "t1_direction.png"), order_group_index),
}


class Ground3D(abstract_model.AbstractModel):

    def __init__(self, cell_data, batch):

        self.batch = batch
        self.cell_data = cell_data
        self.map_type_iamge = map_type_iamge
        self.scale = 1
        self.x_offset = 0
        self.z = 0
        self.entity = None

        self._create_as_vertex()
        pyglet.clock.schedule_once(self._animate, 1)

Explanation: 说明:

I have models(just flat rect for an example) which should be placed on 3 dimensions. 我有模型(只是平直的例子),它应放在3个维度上。 And these models should be animated, like picture_1, after second picture_2, ... etc. 这些模型应该是动画的,比如picture_1,在第二张图片之后,......等等。
As I understood from my previous question using pyglet.sprite.Sprite() in 3D batch is not a good idea. 正如我从上一个问题中所理解的那样,在3D批处理中使用pyglet.sprite.Sprite()并不是一个好主意。

Question: 题:

How I can change pictures(using TextureGroup or any other approaches) on self.vertices ? 我如何在self.vertices上更改图片(使用TextureGroup或任何其他方法)?

Or which arroach/classes I use use to implement it. 或者我使用哪些arroach /类来实现它。 I can't find any examples for such (as for my simple vision) usual case as animation for some flat models in 3 dimensions. 我找不到任何这样的例子(对于我的简单视觉)通常情况作为三维平面模型的动画。

There are many example about rotating/moving/resizing of vertices , but how to build a correct question(is animation aspect) for getting answer in google - I don't know. 关于vertices旋转/移动/调整大小有很多例子,但是如何建立一个正确的问题(动画方面)来获得谷歌的答案 - 我不知道。

PS : If you, reader , have any usefull links on this subject(for pyglet or just for OpenGL) I would be very appreciated you share this link(s) in comment. PS :如果你, 读者 ,对这个主题有任何有用的链接(对于pyglet或仅用于OpenGL),我将非常感谢你在评论中分享这个链接。

Texture coordinates. 纹理坐标。

You should have a single texture atlas for all frames of all different things that are animated ever. 对于所有动画的所有帧,你应该有一个纹理图集。

Preferably, everything should have same animation speed and same amount of frames all the time. 优选地,一切都应该具有相同的动画速度和相同的帧数。

Let's say there's two sprites that have 2 frames for entire animation, and they are stored in 64x64 texture atlas. 假设有两个精灵,整个动画有2帧,它们存储在64x64纹理图谱中。 (EDIT: sorry for ambiguity, 64x64 PIXELS, just because it could imply that we have 64x64 tile atlas, same everywhere else where I mention this) (编辑:对不起歧义,64x64 PIXELS,只是因为它可能意味着我们有64x64平铺图集,在我提到的其他地方也一样)

Now, you need to have a global timer with global value which indicates current animation frame , not game frame. 现在,您需要一个具有全局值的全局计时器,它指示当前动画帧 ,而不是游戏帧。 It should be independent of framerate. 它应该与帧速率无关。

Said value should be updated every once in a while at your desired speed like this: 所述值应该每隔一段时间以您想要的速度更新,如下所示:

current_frame = (current_frame + 1) % animation_length

Since we have 2 frames in this example, it will turn out like this: 由于我们在此示例中有2个帧,因此结果如下:

# init
animation_length = 2
current_frame = 0

# updates:
current_frame = (0 + 1) % 2 # 1 % 2 -> 1
current_frame = (1 + 1) % 2 # 2 % 2 -> 0
...

Now, you need to update UV's of all your sprites only when the frame changes. 现在,只有在帧更改时才需要更新所有精灵的UV。

UV's start from left right and go from 0 to 1 (as far as I remember, for the sake of this example, they do, shhh). UV从左到右开始从0到1(据我记得,为了这个例子,他们这样做,嘘)。

Since we have 2 frames each, we can calculate "tiles" in the UV coordinates like this: 由于我们每个都有2个帧,我们可以像这样计算UV坐标中的“tile”:

tile_width = 1.0 / frames # 2 frames each, width will be 0.5
tile_height = 1.0 / sprites # 2 sprites each, height will be 0.5 too, perfect

Now, on first frame, you generate your UV's like normal, you just take vertical ID or something, and use tile_height * sprite_id to get current V coordinate, and your U is calculated like tile_width * current_frame . 现在,在第一帧,你生成你的UV正常,你只需要垂直ID或其他东西,并使用tile_height * sprite_id来获得当前的V坐标,你的U计算像tile_width * current_frame

This assumes that you already have sprite batching so what you do is go over every sprite on update, and basically just recalculate new UV's with new frame, meaning all sprites change their frame to the next one, yay! 这假设你已经有sprite批处理所以你所做的就是在更新时查看每个sprite,基本上只需用新帧重新计算新的UV,这意味着所有的精灵都会将它们的帧更改为下一个!

If you want to have systems that are independent of eachother, say, very slow animations for some, and faster for others, you'll need different sprite batches or proper calculation on from where to where you need to update UV's in vertex buffer array. 如果你想拥有一个独立于彼此的系统,比如说某些系统的动画非常慢,而对其他系统来说更快,那么你需要不同的精灵批量或正确计算你需要在哪里更新顶点缓冲区数组中的UV。 Everything else is exactly the same, except now current_frame won't be global but rather contained, preferebly in some list or separate object that manages timers for animations. 其他所有内容都完全相同,除非现在current_frame不是全局的,而是包含在内,优先包含在管理动画定时器的某个列表或单独对象中。

You don't need to change anything in your shaders, they just need right UV's for the frames and you're set. 您不需要在着色器中更改任何内容,只需要对框架使用正确的UV,就可以进行设置。

By the way, this is very basic, you could apply some logic yourself so you could instead have 16x16 grid of 32x32 pixels in your texture, each line of sprites having 4 different animations, these could be either sprite's states (attack, run, etc), but how you do it is entirely on you, most importantly, get it to work. 顺便说一句,这是非常基本的,你可以自己应用一些逻辑,所以你可以在纹理中使用16x16网格的32x32像素,每行精灵有4种不同的动画,这些可以是精灵的状态(攻击,运行等) ),但你如何做到完全取决于你,最重要的是,让它发挥作用。 Goodluck. 祝好运。

But if you do it the way I said, then state will be another integer, and UV for state, assuming all states have exactly the same width, it would be like this: 但是,如果按照我说的方式进行,那么状态将是另一个整数,UV为状态,假设所有状态具有完全相同的宽度,它将是这样的:

state_width = 1 / states
tile_width = 1 / (states * frames_per_state)
U = state_width * current_state + tile_width * current_frame

Now, one issue arises, player could start his animation at the last attack frame. 现在,出现了一个问题,玩家可以在最后一个攻击框架开始他的动画。

It's normal, entities with actions should all have individual timers, what I described above, is for tons of sprites that are just background, like grass. 这是正常的,有行动的实体应该都有个别的计时器,我上面描述的,是大量的精灵,只是背景,如草。 Now when you divided it up, you could have a proper way to reset current frame to 0 when new state is assigned. 现在,当您将其分割时,您可以通过适当的方式在分配新状态时当前帧重置为0。

If your entities are objects, you could write proper methods that recalculate UV's every time you rebuild the sprite batch using those sprites, and then timers itselves could be contained in objects. 如果你的实体是对象,你可以编写适当的方法,每次使用这些精灵重建精灵批处理时重新计算UV,然后定时器本身就可以包含在对象中。

We need to draw something? 我们需要画一些东西吗? Check animation state, has it changed, no? 检查动画状态,改变了吗,没有? Send UV's that were calculated before, otherwise, wait a bit, we need to recalculate, only then add those to VBO, and well, render your thing, in the end, it will appear as if you have animations, even though really, it's just a simple, but great UV manipulation. 发送之前计算过的UV,否则,稍等一下,我们需要重新计算,然后再将它们添加到VBO,好吧,渲染你的东西,最后,它会显得好像你有动画,即使真的,它是只是一个简单但很棒的UV操作。

Goodluck. 祝好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM