Please, I would like to know the following:
My target is to have a color picking for the OpenGL shapes in the viewport using unique custom color for each generated object (If there is any other way please guide me) In the example the script (I assembled some scripts online to do this test) draws a simple 3d cube with shader color values(0.7585456, 0.5684187, 0, 1) - Line 41 but when I try to pick the color back using the color_read method I get these values [0.7529412508010864, 0.5647059082984924, 0.0, 1.0] which only matches the first two floats number but no exactly the long float which we passed as the shader color earlier.
Here is the code example
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
indices = (
(0, 1), (0, 2), (1, 3), (2, 3),
(4, 5), (4, 6), (5, 7), (6, 7),
(0, 4), (1, 5), (2, 6), (3, 7))
coords = (
(-1, -1, -1), (+1, -1, -1),
(-1, +1, -1), (+1, +1, -1),
(-1, -1, +1), (+1, -1, +1),
(-1, +1, +1), (+1, +1, +1))
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
# Draw function which copies data from the 3D View
def draw(self, context):
gpu.state.line_width_set(5)
# draw the geo data into the opengl shader
batch = batch_for_shader(
shader=shader,
type='LINES',
content={
"pos": coords,
},
indices=indices
)
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
shader.bind()
# here I set the shader color to a particular float values and later I was to picking this exact color from read_color method
shader.uniform_float("color", (0.7585456, 0.5684187, 0, 1))
batch.draw(shader)
gpu.state.depth_mask_set(False)
# Modal operator for controlled redrawing of the image object
# NOTE: This code is only for a more conveniant testing of the draw function
# If you want to stop the test, press 'ESC'
class ModalFramebufferCopy(bpy.types.Operator):
bl_idname = "view3d.modal_framebuffer_copy"
bl_label = "Draw 3D View Framebuffer"
# modal operator for controlled redraw of the image
def modal(self, context, event):
# stop the execution of this example code if 'ESC' is pressed
if event.type in {'ESC'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW')
return {'CANCELLED'}
elif event.type == 'LEFTMOUSE':
self.framebuffer = gpu.state.active_framebuffer_get()
self.framebuffer.clear()
self.framebuffer.bind()
# get information on current viewport
self.viewport_info = gpu.state.viewport_get()
self.width = self.viewport_info[2]
self.height = self.viewport_info[3]
# obtain pixels from the framebuffer
self.pixelBuffer = self.framebuffer.read_color(event.mouse_x, event.mouse_y, 1, 1, 4, 0, 'FLOAT')
# here I print the returned values from the color picking but it doesn't match the shader input colors
print (self.pixelBuffer.to_list()[0])
self.modal_redraw = True
else:
# set draw variable to update:
# This is here to prevent excessive redrawing
self.modal_redraw = True
return {'PASS_THROUGH'}
def invoke(self, context, event):
self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw, (self, context), 'WINDOW', 'POST_VIEW') # this draws the grid alone (without objects)
# updates the viewport after an new options is selected
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def register():
bpy.utils.register_class(ModalFramebufferCopy)
def unregister():
bpy.utils.unregister_class(ModalFramebufferCopy)
if __name__ == "__main__":
register()
# Invoke modal operator for the example code
bpy.ops.view3d.modal_framebuffer_copy('INVOKE_DEFAULT')
Please, notice that once you run the script and move the cursor into position in the viewport it will print the color data info. Also, I'm using Blender 3.4
Thanks in advance
[Additional info from comments:]
I'm drawing custom shapes in the viewport and connect them to an empty object as a holder of them and I need to select the empty object once I select any of these custom shapes. like what happens when you select a wireframe obj it detects which object you selected then select it in the outliner.