When writing add-ons for Blender, you often want to use images here. I think there is. If you use OpenGL (bgl) provided by Blender, you can display it, so I will try it.
There are two ways to read the image, so I will write each one.
bpy.data.images.load ()
gl_texture_test.py
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Blender2.77a
import bpy
import bgl
image_file_path = "C:/Works/blender_rogo.png "
class GL_Texture():
def __init__(self, file_path):
self.image = None
self.width = 0
self.height = 0
self.load_image(file_path)
def load_image(self, file_path):
try:
self.image = bpy.data.images.load(file_path)
except Exception as e:
print(e)
if self.image:
self.width, self.height = self.image.size
self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
def remove(self):
if self.image:
try:
self.image.user_clear()
self.image.gl_free()
#self.image.buffers_free()
bpy.data.images.remove(self.image)
except Exception as e:
print(e)
def bind(self):
if self.image.bindcode[0]:
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.image.bindcode[0])
else:
self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
print("reload gl texture")
class GL_Texture_test_Operator(bpy.types.Operator):
bl_idname = "view3d.gl_texture_test_operator"
bl_label = "View3D GL_Texture draw test"
_handle_draw = None
is_enabled = False
_my_texture = None
@staticmethod
def draw_callback_px(self, context):
bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
bgl.glEnable(bgl.GL_BLEND)
bgl.glEnable(bgl.GL_TEXTURE_2D)
tex = GL_Texture_test_Operator._my_texture
w = tex.width
h =tex.height
tex.bind()
bgl.glBegin(bgl.GL_QUADS)
bgl.glTexCoord2f(0.0, 0.0)
bgl.glVertex2f(0.0, 0.0)
bgl.glTexCoord2f(1.0, 0.0)
bgl.glVertex2f(0.0+w, 0.0)
bgl.glTexCoord2f(1.0, 1.0)
bgl.glVertex2f(0.0+w, 0.0+h)
bgl.glTexCoord2f(0.0, 1.0)
bgl.glVertex2f(0.0, 0.0+h)
bgl.glEnd()
bgl.glDisable(bgl.GL_TEXTURE_2D)
bgl.glDisable(bgl.GL_BLEND)
@staticmethod
def handle_add(self, context):
GL_Texture_test_Operator._handle_draw = bpy.types.SpaceView3D.draw_handler_add(
self.draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
@staticmethod
def handle_remove():
if GL_Texture_test_Operator._handle_draw is not None:
bpy.types.SpaceView3D.draw_handler_remove(GL_Texture_test_Operator._handle_draw, 'WINDOW')
GL_Texture_test_Operator._handle_draw = None
GL_Texture_test_Operator.is_enabled = False
@classmethod
def poll(cls, context):
return context.area.type == 'VIEW_3D'
def modal(self, context, event):
if context.area:
context.area.tag_redraw()
return {'PASS_THROUGH'}
def invoke(self, context, event):
if context.area.type == 'VIEW_3D':
if GL_Texture_test_Operator.is_enabled:
self.cancel(context)
return {'FINISHED'}
else:
GL_Texture_test_Operator._my_texture = GL_Texture(image_file_path)
GL_Texture_test_Operator.handle_add(self, context)
GL_Texture_test_Operator.is_enabled = True
context.area.tag_redraw()
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "View3D not found, cannot run operator")
return {'CANCELLED'}
def cancel(self, context):
GL_Texture_test_Operator.handle_remove()
if GL_Texture_test_Operator._my_texture is not None:
GL_Texture_test_Operator._my_texture.remove()
GL_Texture_test_Operator._my_texture = None
class GL_Texture_test_panel(bpy.types.Panel):
bl_label = "GL Texture test"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self, context):
layout = self.layout
if GL_Texture_test_Operator.is_enabled:
layout.operator("view3d.gl_texture_test_operator", "Stop", icon="PAUSE")
else:
layout.operator("view3d.gl_texture_test_operator", "Start", icon="PLAY")
def register():
bpy.utils.register_class(GL_Texture_test_Operator)
bpy.utils.register_class(GL_Texture_test_panel)
def unregister():
bpy.utils.unregister_class(GL_Texture_test_panel)
bpy.utils.unregister_class(GL_Texture_test_Operator)
if __name__ == "__main__":
register()
Copy and paste it into Blender's text editor and run it. For image_file_path at the top, enter the path of the image you actually use.
I drew the Blender logo appropriately. Use this for the image to be displayed. If you don't have a suitable image, please use it.
When you execute it, a button will be displayed in the property panel first, so press it.
Then
The path check of the image file to be used is omitted. You should check it when you actually use it.
self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
I think that the image data is sent to the memory of OpenGL.
I think the two arguments on the right are the storage method when scaling.
self.image.bindcode[0]
The texture ID is included here after gl_load ().
It wasn't a list before, but for some reason it has changed to a list.
I don't know what data is contained except 0.
def bind(self):
if self.image.bindcode[0]:
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.image.bindcode[0])
else:
self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
print("reload gl texture")
As for this part, the texture memory is automatically released a few minutes after it is executed, so the texture is loaded again. Blender seems to be freeing memory on its own.
The drawing part is the same as OpenGL.
――It's easy anyway. --A wide variety of readable file formats
--Since the image file is loaded in Blender, you can see the loaded image like this during execution. (Although it is erased at the end)
--The texture memory is released for some reason after a few minutes of execution, so you need to gl_load ()
each time.
TimeOut
in OpenGL of the System tab of the user setting.
It seems that if you set it to 0, it will not be released, but since it is also possible to tamper with it from the script side, if it disappears after all, let's gl_load ()
.Rewrite the GL_Texture class from the sample above and execute it.
GL_Texture class
class GL_Texture():
def __init__(self, file_path):
self.texture_id = 0
self.width = 0
self.height = 0
self.load_8bit_bitmap(file_path)
def load_8bit_bitmap(self, file_path):
f = None
bitmap_data = None
gl_buffer = None
try:
f = open(file_path, 'rb')
file_header = f.read(14)
info_header = f.read(40)
self.width = struct.unpack("<i", info_header[4:8])[0]
self.height = struct.unpack("<i", info_header[8:12])[0]
palette = []
for i in range(256):
rgbr = f.read(4)
palette.append(struct.unpack("BBBB", rgbr)[0:3])
gl_buffer = bgl.Buffer(bgl.GL_BYTE, self.width*self.height*4)
for i in range(self.width*self.height):
data = f.read(1)
index = struct.unpack("B", data)[0]
bgr = palette[index]
gl_buffer[i*4] = bgr[2]
gl_buffer[i*4+1] = bgr[1]
gl_buffer[i*4+2] = bgr[0]
if index == 0:
gl_buffer[i*4+3] = 0
else:
gl_buffer[i*4+3] = 255
f.close()
except Exception as e:
if f is not None:
f.close()
print(e)
return False
# set opengl
textures = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenTextures(1, textures)
self.texture_id = textures[0]
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture_id)
bgl.glPixelStorei(bgl.GL_UNPACK_ALIGNMENT, 4)
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_RGBA,
self.width, self.height, 0, bgl.GL_RGBA,
bgl.GL_UNSIGNED_BYTE, gl_buffer)
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST)
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST)
def remove(self):
if self.texture_id:
textures = bgl.Buffer(bgl.GL_INT, 1)
textures[0] = self.texture_id
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
bgl.glDeleteTextures(1, textures)
self.texture_id = 0
def bind(self):
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture_id)
Caution. Only 8-bit format bitmap files can be loaded. Index 0 is transparent. It seems that you cannot upload the bitmap file, so please prepare it yourself.
It is a flow of opening a file, expanding it in a buffer, and registering it in OpenGL.
In bgl, if you want to pass a buffer as an argument, you need to use the Buffer class. I think that's the difference from ordinary OpenGL.
Since the file check process is omitted, please check various things when actually using it.
--Since it is registered in OpenGL directly without passing through the Image class of Blende, Blender does not release the memory without permission.
--Loading is a little slow. --You need to write as many load parts as there are formats you want to support.
Personally, I recommend using bpy.data.images.load ()
because it's easier and easier. It loads fast.
The image file is also loaded in Blender during execution, but even if you save it as it is, it will not be saved if the number of users is 0, so you do not have to worry too much.
Recommended Posts