566 lines
22 KiB
Python
566 lines
22 KiB
Python
from panda3d.core import (
|
|
ButtonHandle,
|
|
ButtonRegistry,
|
|
ColorAttrib,
|
|
ColorBlendAttrib,
|
|
CullFaceAttrib,
|
|
DepthTestAttrib,
|
|
Geom,
|
|
GeomNode,
|
|
GeomTriangles,
|
|
GeomVertexData,
|
|
GeomVertexArrayFormat,
|
|
GeomVertexFormat,
|
|
InternalName,
|
|
KeyboardButton,
|
|
MouseButton,
|
|
NodePath,
|
|
RenderState,
|
|
SamplerState,
|
|
Shader,
|
|
ScissorAttrib,
|
|
Texture,
|
|
TextureAttrib
|
|
)
|
|
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from direct.showbase.DirectObject import DirectObject
|
|
|
|
try:
|
|
from shaders import *
|
|
except ModuleNotFoundError:
|
|
from .shaders import *
|
|
|
|
from imgui_bundle import imgui
|
|
import ctypes
|
|
|
|
import pyperclip
|
|
import sys
|
|
|
|
__all__ = ['ImGuiBackend', 'ImGuiStyles']
|
|
|
|
KEYBOARD_BUTTON_TO_IMGUI_KEY = {
|
|
# Special keys
|
|
KeyboardButton.tab(): imgui.Key.tab.value,
|
|
KeyboardButton.left(): imgui.Key.left_arrow.value,
|
|
KeyboardButton.right(): imgui.Key.right_arrow.value,
|
|
KeyboardButton.up(): imgui.Key.up_arrow.value,
|
|
KeyboardButton.down(): imgui.Key.down_arrow.value,
|
|
KeyboardButton.enter(): imgui.Key.enter.value,
|
|
KeyboardButton.escape(): imgui.Key.escape.value,
|
|
KeyboardButton.backspace(): imgui.Key.backspace.value,
|
|
KeyboardButton.space(): imgui.Key.space.value,
|
|
KeyboardButton.lshift(): imgui.Key.left_shift.value,
|
|
KeyboardButton.rshift(): imgui.Key.right_shift.value,
|
|
KeyboardButton.shift(): imgui.Key.mod_shift.value,
|
|
KeyboardButton.control(): imgui.Key.mod_ctrl.value,
|
|
KeyboardButton.lcontrol(): imgui.Key.left_ctrl.value,
|
|
KeyboardButton.rcontrol(): imgui.Key.right_ctrl.value,
|
|
KeyboardButton.alt(): imgui.Key.mod_alt.value,
|
|
KeyboardButton.lalt(): imgui.Key.left_alt.value,
|
|
KeyboardButton.ralt(): imgui.Key.right_alt.value,
|
|
KeyboardButton._del(): imgui.Key.delete.value,
|
|
KeyboardButton.home(): imgui.Key.home.value,
|
|
KeyboardButton.end(): imgui.Key.end.value,
|
|
KeyboardButton.pageUp(): imgui.Key.page_up.value,
|
|
KeyboardButton.pageDown(): imgui.Key.page_down.value,
|
|
KeyboardButton.insert(): imgui.Key.insert.value,
|
|
KeyboardButton.capsLock(): imgui.Key.caps_lock.value,
|
|
KeyboardButton.numLock(): imgui.Key.num_lock.value,
|
|
KeyboardButton.scrollLock(): imgui.Key.scroll_lock.value,
|
|
KeyboardButton.menu(): imgui.Key.menu.value,
|
|
KeyboardButton.lmeta(): imgui.Key.left_super.value,
|
|
KeyboardButton.rmeta(): imgui.Key.right_super.value,
|
|
KeyboardButton.meta(): imgui.Key.mod_super.value,
|
|
|
|
# Function keys (F1 to F16)
|
|
KeyboardButton.f1(): imgui.Key.f1.value,
|
|
KeyboardButton.f2(): imgui.Key.f2.value,
|
|
KeyboardButton.f3(): imgui.Key.f3.value,
|
|
KeyboardButton.f4(): imgui.Key.f4.value,
|
|
KeyboardButton.f5(): imgui.Key.f5.value,
|
|
KeyboardButton.f6(): imgui.Key.f6.value,
|
|
KeyboardButton.f7(): imgui.Key.f7.value,
|
|
KeyboardButton.f8(): imgui.Key.f8.value,
|
|
KeyboardButton.f9(): imgui.Key.f9.value,
|
|
KeyboardButton.f10(): imgui.Key.f10.value,
|
|
KeyboardButton.f11(): imgui.Key.f11.value,
|
|
KeyboardButton.f12(): imgui.Key.f12.value,
|
|
KeyboardButton.f13(): imgui.Key.f13.value,
|
|
KeyboardButton.f14(): imgui.Key.f14.value,
|
|
KeyboardButton.f15(): imgui.Key.f15.value,
|
|
KeyboardButton.f16(): imgui.Key.f16.value,
|
|
|
|
# Numeric keys (0-9) on the main keyboard
|
|
KeyboardButton.asciiKey("0"): imgui.Key._0.value,
|
|
KeyboardButton.asciiKey("1"): imgui.Key._1.value,
|
|
KeyboardButton.asciiKey("2"): imgui.Key._2.value,
|
|
KeyboardButton.asciiKey("3"): imgui.Key._3.value,
|
|
KeyboardButton.asciiKey("4"): imgui.Key._4.value,
|
|
KeyboardButton.asciiKey("5"): imgui.Key._5.value,
|
|
KeyboardButton.asciiKey("6"): imgui.Key._6.value,
|
|
KeyboardButton.asciiKey("7"): imgui.Key._7.value,
|
|
KeyboardButton.asciiKey("8"): imgui.Key._8.value,
|
|
KeyboardButton.asciiKey("9"): imgui.Key._9.value,
|
|
|
|
# Alphabetic keys
|
|
KeyboardButton.asciiKey("a"): imgui.Key.a.value,
|
|
KeyboardButton.asciiKey("b"): imgui.Key.b.value,
|
|
KeyboardButton.asciiKey("c"): imgui.Key.c.value,
|
|
KeyboardButton.asciiKey("d"): imgui.Key.d.value,
|
|
KeyboardButton.asciiKey("e"): imgui.Key.e.value,
|
|
KeyboardButton.asciiKey("f"): imgui.Key.f.value,
|
|
KeyboardButton.asciiKey("g"): imgui.Key.g.value,
|
|
KeyboardButton.asciiKey("h"): imgui.Key.h.value,
|
|
KeyboardButton.asciiKey("i"): imgui.Key.i.value,
|
|
KeyboardButton.asciiKey("j"): imgui.Key.j.value,
|
|
KeyboardButton.asciiKey("k"): imgui.Key.k.value,
|
|
KeyboardButton.asciiKey("l"): imgui.Key.l.value,
|
|
KeyboardButton.asciiKey("m"): imgui.Key.m.value,
|
|
KeyboardButton.asciiKey("n"): imgui.Key.n.value,
|
|
KeyboardButton.asciiKey("o"): imgui.Key.o.value,
|
|
KeyboardButton.asciiKey("p"): imgui.Key.p.value,
|
|
KeyboardButton.asciiKey("q"): imgui.Key.q.value,
|
|
KeyboardButton.asciiKey("r"): imgui.Key.r.value,
|
|
KeyboardButton.asciiKey("s"): imgui.Key.s.value,
|
|
KeyboardButton.asciiKey("t"): imgui.Key.t.value,
|
|
KeyboardButton.asciiKey("u"): imgui.Key.u.value,
|
|
KeyboardButton.asciiKey("v"): imgui.Key.v.value,
|
|
KeyboardButton.asciiKey("w"): imgui.Key.w.value,
|
|
KeyboardButton.asciiKey("x"): imgui.Key.x.value,
|
|
KeyboardButton.asciiKey("y"): imgui.Key.y.value,
|
|
KeyboardButton.asciiKey("z"): imgui.Key.z.value,
|
|
|
|
# Punctuation keys
|
|
KeyboardButton.asciiKey("!"): imgui.Key._1.value,
|
|
KeyboardButton.asciiKey("@"): imgui.Key._2.value,
|
|
KeyboardButton.asciiKey("#"): imgui.Key._3.value,
|
|
KeyboardButton.asciiKey("$"): imgui.Key._4.value,
|
|
KeyboardButton.asciiKey("%"): imgui.Key._5.value,
|
|
KeyboardButton.asciiKey("^"): imgui.Key._6.value,
|
|
KeyboardButton.asciiKey("&"): imgui.Key._7.value,
|
|
KeyboardButton.asciiKey("*"): imgui.Key._8.value,
|
|
KeyboardButton.asciiKey("("): imgui.Key._9.value,
|
|
KeyboardButton.asciiKey(")"): imgui.Key._0.value,
|
|
KeyboardButton.asciiKey("-"): imgui.Key.minus.value,
|
|
KeyboardButton.asciiKey("="): imgui.Key.equal.value,
|
|
KeyboardButton.asciiKey("["): imgui.Key.left_bracket.value,
|
|
KeyboardButton.asciiKey("]"): imgui.Key.right_bracket.value,
|
|
KeyboardButton.asciiKey("\\"): imgui.Key.backslash.value,
|
|
KeyboardButton.asciiKey(";"): imgui.Key.semicolon.value,
|
|
KeyboardButton.asciiKey("'"): imgui.Key.apostrophe.value,
|
|
KeyboardButton.asciiKey(","): imgui.Key.comma.value,
|
|
KeyboardButton.asciiKey("."): imgui.Key.period.value,
|
|
KeyboardButton.asciiKey("/"): imgui.Key.slash.value,
|
|
|
|
# Special characters
|
|
KeyboardButton.asciiKey("`"): imgui.Key.grave_accent.value,
|
|
KeyboardButton.asciiKey("~"): imgui.Key.grave_accent.value,
|
|
KeyboardButton.asciiKey("_"): imgui.Key.minus.value,
|
|
KeyboardButton.asciiKey("+"): imgui.Key.equal.value,
|
|
KeyboardButton.asciiKey("<"): imgui.Key.comma.value,
|
|
KeyboardButton.asciiKey(">"): imgui.Key.period.value,
|
|
KeyboardButton.asciiKey("?"): imgui.Key.slash.value,
|
|
|
|
# Additional modifier keys
|
|
KeyboardButton.printScreen(): imgui.Key.print_screen.value,
|
|
KeyboardButton.pause(): imgui.Key.pause.value,
|
|
}
|
|
|
|
class GeomList:
|
|
def __init__(self, vdata: GeomVertexData):
|
|
self.vdata: GeomVertexData = vdata # vertex data shared among the below GeomNodes
|
|
self.nodepaths: list[NodePath] = []
|
|
|
|
class ImGuiBackend(DirectObject):
|
|
"""
|
|
The main Dear ImGui backend code for Panda3D.
|
|
(Based on https://github.com/bluekyu/panda3d_imgui; ported to
|
|
Python using imgui-bundle and updated to support Dear ImGui version 1.92.3)
|
|
"""
|
|
|
|
def __init__(self, window = None, parent = None, style = 'dark'):
|
|
DirectObject.__init__(self)
|
|
self.notify = DirectNotifyGlobal.directNotify.newCategory("imgui")
|
|
if not window:
|
|
window = base.win
|
|
if not parent:
|
|
parent = base.pixel2d
|
|
|
|
self.window = window
|
|
self.root = parent.attachNewNode('imgui-root', 1000)
|
|
self.context = imgui.create_context()
|
|
self.io = imgui.get_io()
|
|
|
|
# Add backend flag for rendering textures
|
|
self.io.backend_flags |= imgui.BackendFlags_.renderer_has_textures.value
|
|
|
|
self.vformat = None
|
|
self.textureCounter = 0
|
|
self.textures: dict[int, Texture] = {}
|
|
self.geomData: list[GeomList] = []
|
|
|
|
self.__setupStyle(style)
|
|
self.__setupGeom()
|
|
self.__setupShader()
|
|
self.__setupFront()
|
|
self.__setupEvent()
|
|
self.__windowEvent()
|
|
self.__setupButton()
|
|
self.__setupRender()
|
|
|
|
# Set Clipboard functions
|
|
imgui.get_platform_io().platform_set_clipboard_text_fn = self.__setClipboardText
|
|
imgui.get_platform_io().platform_get_clipboard_text_fn = self.__getClipboardText
|
|
|
|
def toggle(self):
|
|
if self.root.isHidden():
|
|
self.root.show()
|
|
else:
|
|
self.root.hide()
|
|
|
|
def hide(self):
|
|
self.root.hide()
|
|
|
|
def show(self):
|
|
self.root.show()
|
|
|
|
def isMouseCaptured(self) -> bool:
|
|
# This returns True if the mouse is over
|
|
# a Imgui window or any other reason.
|
|
return self.io.want_capture_mouse
|
|
|
|
def isKeyboardCaptured(self) -> bool:
|
|
# This returns True when a Imgui text
|
|
# input widget is focused or any other reason.
|
|
return self.io.want_capture_keyboard
|
|
|
|
def __setupStyle(self, style):
|
|
match style:
|
|
case 'dark':
|
|
imgui.style_colors_dark()
|
|
case 'classic':
|
|
imgui.style_colors_classic()
|
|
case 'light':
|
|
imgui.style_colors_light()
|
|
case _:
|
|
self.notify.warning(f"Unknown style: \"{style}\"")
|
|
|
|
def __onButton(self, keyName: str, down: bool):
|
|
# Panda3D adds the prefix of the modifier keys to the key name
|
|
# if they are held down, so we have to strip them out.
|
|
if keyName.startswith(('control-', 'alt-', 'shift-', 'shift-control-', 'shift-alt-', 'shift-control-alt-',
|
|
'meta-', 'control-meta-', 'alt-meta-', 'control-alt-meta-', 'shift-meta-',
|
|
'shift-control-meta', 'shift-alt-meta-', 'shift-control-alt-meta-')):
|
|
keyName = keyName.split('-')[-1]
|
|
if keyName == '':
|
|
# must be minus.
|
|
keyName = '-'
|
|
|
|
button = ButtonRegistry.ptr().getButton(keyName)
|
|
if button == ButtonHandle.none():
|
|
return
|
|
if MouseButton.isMouseButton(button):
|
|
if button == MouseButton.one():
|
|
self.io.add_mouse_button_event(0, down)
|
|
elif button == MouseButton.three():
|
|
# NOTE: In Panda3D, MouseButton.three() is the right mouse button
|
|
# while MouseButton.two() is the middle (scroll wheel) button
|
|
self.io.add_mouse_button_event(1, down)
|
|
elif button == MouseButton.two():
|
|
self.io.add_mouse_button_event(2, down)
|
|
elif button == MouseButton.four():
|
|
self.io.add_mouse_button_event(3, down)
|
|
elif button == MouseButton.five():
|
|
self.io.add_mouse_button_event(4, down)
|
|
elif button == MouseButton.wheelUp() and down:
|
|
self.io.add_mouse_wheel_event(0, .5)
|
|
elif button == MouseButton.wheelDown() and down:
|
|
self.io.add_mouse_wheel_event(0, -.5)
|
|
elif button == MouseButton.wheelLeft() and down:
|
|
self.io.add_mouse_wheel_event(.5, 0)
|
|
elif button == MouseButton.wheelRight() and down:
|
|
self.io.add_mouse_wheel_event(-.5, 0)
|
|
else:
|
|
imguiKey = KEYBOARD_BUTTON_TO_IMGUI_KEY.get(button, imgui.Key.none.value)
|
|
self.io.add_key_event(imguiKey, down)
|
|
|
|
def __onKeystroke(self, keyName):
|
|
# NOTE: Panda3D for some reason doesn't recognize if
|
|
# the caps lock is on for macOS. You would have to
|
|
# hold down shift to input capital letters.
|
|
# Windows/Linux handles this just fine.
|
|
button = ButtonRegistry.ptr().getButton(keyName)
|
|
if keyName == ' ':
|
|
# There is no space button on the ButtonRegistry.
|
|
button = KeyboardButton.space()
|
|
if button.hasAsciiEquivalent():
|
|
self.io.add_input_character(ord(button.getAsciiEquivalent()))
|
|
|
|
def __setupGeom(self):
|
|
self.notify.debug("__setupGeom")
|
|
arrayFormat = GeomVertexArrayFormat(
|
|
InternalName.getVertex(), 4, Geom.NT_stdfloat, Geom.C_point,
|
|
InternalName.getColor(), 1, Geom.NT_packed_dabc, Geom.C_color
|
|
)
|
|
|
|
self.vformat = GeomVertexFormat.registerFormat(GeomVertexFormat(arrayFormat))
|
|
|
|
self.root.setState(RenderState.make(
|
|
ColorAttrib.makeVertex(),
|
|
ColorBlendAttrib.make(ColorBlendAttrib.M_add, ColorBlendAttrib.O_incoming_alpha, ColorBlendAttrib.O_one_minus_incoming_alpha),
|
|
DepthTestAttrib.make(DepthTestAttrib.M_none),
|
|
CullFaceAttrib.make(CullFaceAttrib.M_cull_none)
|
|
))
|
|
|
|
def __setupShader(self):
|
|
self.notify.debug("__setupShader")
|
|
shader = Shader.make(
|
|
Shader.SL_GLSL,
|
|
VERT_SHADER,
|
|
FRAG_SHADER,
|
|
)
|
|
|
|
self.root.setShader(shader)
|
|
|
|
def __setupFront(self):
|
|
self.notify.debug("__setupFront")
|
|
self.io.fonts.add_font_default()
|
|
|
|
def __setupEvent(self):
|
|
self.notify.debug("__setupEvent")
|
|
self.accept("window-event", self.__windowEvent)
|
|
|
|
def __windowEvent(self, _ = None):
|
|
if self.window:
|
|
self.io.display_size = (self.window.getXSize(), self.window.getYSize())
|
|
|
|
def __setupButton(self):
|
|
self.notify.debug("__setupButton")
|
|
base.buttonThrowers[0].node().setButtonDownEvent('buttonDown')
|
|
base.buttonThrowers[0].node().setButtonUpEvent('buttonUp')
|
|
base.buttonThrowers[0].node().setKeystrokeEvent('keystroke')
|
|
|
|
def __buttonDown(keyName):
|
|
self.__onButton(keyName, True)
|
|
|
|
def __buttonUp(keyName):
|
|
self.__onButton(keyName, False)
|
|
|
|
def __keyStroke(keyName):
|
|
self.__onKeystroke(keyName)
|
|
|
|
def __handleOobe():
|
|
if base.bboard.get('oobeEnabled'):
|
|
self.ignore('buttonDown')
|
|
self.ignore('buttonUp')
|
|
self.accept('oobe-down', __buttonDown)
|
|
self.accept('oobe-up', __buttonUp)
|
|
else:
|
|
self.ignore('oobe-down')
|
|
self.ignore('oobe-up')
|
|
|
|
# Set the names to the button events back.
|
|
base.buttonThrowers[0].node().setButtonDownEvent('buttonDown')
|
|
base.buttonThrowers[0].node().setButtonUpEvent('buttonUp')
|
|
self.accept('buttonDown', __buttonDown)
|
|
self.accept('buttonUp', __buttonUp)
|
|
|
|
self.accept('buttonDown', __buttonDown)
|
|
self.accept('buttonUp', __buttonUp)
|
|
self.accept('keystroke', __keyStroke)
|
|
|
|
self.accept(base.bboard.getEvent('oobeEnabled'), __handleOobe)
|
|
|
|
# self.addTask(__handleOobe, appendTask=True)
|
|
|
|
def __setupRender(self):
|
|
self.notify.debug("__setupRender")
|
|
# NOTE: igLoop (frame rendering) has 50 sort.
|
|
base.taskMgr.add(self.__newFrame, "imgui-new-frame", 0)
|
|
base.taskMgr.add(self.__renderFrame, "imgui-render-frame", 40)
|
|
|
|
def __newFrame(self, task):
|
|
if self.root.isHidden():
|
|
return task.cont
|
|
|
|
self.io.delta_time = base.clock.getDt()
|
|
if self.window:
|
|
mouse = self.window.getPointer(0)
|
|
if mouse.getInWindow():
|
|
if self.io.want_set_mouse_pos:
|
|
self.window.movePointer(0, self.io.mouse_pos.x, self.io.mouse_pos.y)
|
|
else:
|
|
self.io.mouse_pos = mouse.getX(), mouse.getY()
|
|
else:
|
|
self.io.mouse_pos = (-imgui.FLT_MAX, -imgui.FLT_MAX)
|
|
|
|
imgui.new_frame()
|
|
base.messenger.send("imgui-new-frame")
|
|
|
|
return task.cont
|
|
|
|
def __renderFrame(self, task):
|
|
if self.root.isHidden():
|
|
return task.cont
|
|
|
|
imgui.render()
|
|
|
|
fbWidth = self.io.display_size.x * self.io.display_framebuffer_scale.x
|
|
fbHeight = self.io.display_size.y * self.io.display_framebuffer_scale.y
|
|
|
|
self.__updateTextures()
|
|
|
|
drawData = imgui.get_draw_data()
|
|
|
|
for child in self.root.children:
|
|
child.detachNode()
|
|
|
|
for i, cmdList in enumerate(drawData.cmd_lists):
|
|
if i > len(self.geomData) - 1:
|
|
self.geomData.append(GeomList(GeomVertexData(f"imgui-vertex-{i}", self.vformat, Geom.UH_stream)))
|
|
|
|
geomList = self.geomData[i]
|
|
vertexHandle = geomList.vdata.modifyArrayHandle(0)
|
|
if vertexHandle.getNumRows() < cmdList.vtx_buffer.size():
|
|
vertexHandle.uncleanSetNumRows(cmdList.vtx_buffer.size())
|
|
vertexHandle.setData(ctypes.string_at(cmdList.vtx_buffer.data_address(), cmdList.vtx_buffer.size() * imgui.VERTEX_SIZE))
|
|
|
|
indexBuffer = cmdList.idx_buffer.data_address()
|
|
for k, drawCmd, in enumerate(cmdList.cmd_buffer):
|
|
if k > len(geomList.nodepaths) - 1:
|
|
geomList.nodepaths.append(self.__createGeomnode(geomList.vdata))
|
|
|
|
np = geomList.nodepaths[k]
|
|
np.reparentTo(self.root)
|
|
|
|
node = np.node()
|
|
indexHandle = node.modifyGeom(0).modifyPrimitive(0).modifyVertices(drawCmd.elem_count).modifyHandle()
|
|
if indexHandle.getNumRows() < drawCmd.elem_count:
|
|
indexHandle.uncleanSetNumRows(drawCmd.elem_count)
|
|
|
|
indexHandle.setData(ctypes.string_at(indexBuffer, drawCmd.elem_count * imgui.INDEX_SIZE))
|
|
indexBuffer += (drawCmd.elem_count * imgui.INDEX_SIZE)
|
|
|
|
# FIXME: This works, but it breaks Modal windows as the white
|
|
# covers the window (but still usable) instead of the background.
|
|
# (Not sure if this is to blame or the shader...)
|
|
state = RenderState.make(ScissorAttrib.make(
|
|
drawCmd.clip_rect.x / fbWidth,
|
|
drawCmd.clip_rect.z / fbWidth,
|
|
1 - drawCmd.clip_rect.w / fbHeight,
|
|
1 - drawCmd.clip_rect.y / fbHeight
|
|
))
|
|
|
|
if drawCmd.tex_ref.get_tex_id():
|
|
texture = self.textures[drawCmd.tex_ref.get_tex_id()]
|
|
state = state.addAttrib(TextureAttrib.make(texture))
|
|
|
|
node.setGeomState(0, state)
|
|
|
|
return task.cont
|
|
|
|
def loadTexture(self, texture: Texture | str) -> imgui.ImTextureRef:
|
|
if type(texture) == str:
|
|
texture = base.loader.loadTexture(texture)
|
|
self.notify.debug(f"loadTexture ({self.textureCounter + 1})")
|
|
|
|
self.textureCounter += 1
|
|
self.textures[self.textureCounter] = texture
|
|
|
|
return imgui.ImTextureRef(self.textureCounter)
|
|
|
|
def removeTexture(self, tex: imgui.ImTextureRef, unload = True):
|
|
texture = self.textures.get(tex.get_tex_id(), None)
|
|
if texture:
|
|
self.notify.debug(f"removeTexture ({tex.get_tex_id()})")
|
|
|
|
if unload:
|
|
texture.clear()
|
|
base.loader.unloadTexture(texture)
|
|
del texture
|
|
del self.textures[tex.get_tex_id()]
|
|
|
|
def __updateTextures(self):
|
|
for tex in imgui.get_platform_io().textures:
|
|
if tex.status != imgui.ImTextureStatus.ok:
|
|
self.__updateTexture(tex)
|
|
|
|
def __updateTexture(self, tex: imgui.ImTextureData):
|
|
def __setPixelsToTexture(texture: Texture):
|
|
# The easiest way to update a Panda texture
|
|
# is to recreate the ram image and rewrite
|
|
# all the pixels.
|
|
|
|
# This clears the RamImage if one exists already.
|
|
textureData = texture.makeRamImage()
|
|
|
|
pixels = tex.get_pixels_array()
|
|
textureData.setData(pixels.tobytes())
|
|
|
|
match tex.status:
|
|
case imgui.ImTextureStatus.want_create:
|
|
self.notify.debug(f"__updateTexture ({self.textureCounter + 1}): create ({tex.width}x{tex.height})")
|
|
assert tex.tex_id == 0
|
|
assert tex.backend_user_data is None
|
|
assert tex.format == imgui.ImTextureFormat.rgba32
|
|
|
|
texture = Texture()
|
|
texture.setName(f"imgui-texture-{tex.unique_id}")
|
|
texture.setup2dTexture(tex.width, tex.height, Texture.T_unsigned_byte, Texture.F_rgba32)
|
|
texture.setMinfilter(SamplerState.FT_linear)
|
|
texture.setMagfilter(SamplerState.FT_linear)
|
|
|
|
__setPixelsToTexture(texture)
|
|
|
|
self.textureCounter += 1
|
|
self.textures[self.textureCounter] = texture
|
|
|
|
tex.tex_id = self.textureCounter
|
|
tex.status = imgui.ImTextureStatus.ok
|
|
case imgui.ImTextureStatus.want_updates:
|
|
self.notify.debug(f"__updateTexture ({tex.tex_id}): update ({tex.width}x{tex.height})")
|
|
|
|
__setPixelsToTexture(self.textures[tex.tex_id])
|
|
|
|
tex.status = imgui.ImTextureStatus.ok
|
|
case imgui.ImTextureStatus.want_destroy:
|
|
texture = self.textures.get(tex.tex_id, None)
|
|
if texture:
|
|
texture.clear()
|
|
del texture
|
|
del self.textures[tex.tex_id]
|
|
|
|
tex.tex_id = 0
|
|
tex.status = imgui.ImTextureStatus.destroyed
|
|
|
|
@staticmethod
|
|
def __createGeomnode(vdata: GeomVertexData) -> NodePath:
|
|
prim = GeomTriangles(Geom.UH_stream)
|
|
if imgui.INDEX_SIZE == 2:
|
|
prim.setIndexType(Geom.NT_uint16)
|
|
else:
|
|
prim.setIndexType(Geom.NT_uint32)
|
|
prim.closePrimitive()
|
|
|
|
geom = Geom(vdata)
|
|
geom.addPrimitive(prim)
|
|
|
|
node = GeomNode("imgui-geom")
|
|
node.addGeom(geom, RenderState.makeEmpty())
|
|
|
|
return NodePath(node)
|
|
|
|
def cleanup(self):
|
|
if self.context:
|
|
imgui.destroy_context()
|
|
self.context = None
|
|
|
|
@staticmethod
|
|
def __setClipboardText(_, str: str):
|
|
pyperclip.copy(str)
|
|
|
|
@staticmethod
|
|
def __getClipboardText(_):
|
|
return pyperclip.paste()
|