Fix T64528: error in RenderEngine API docs example
[blender.git] / doc / python_api / examples / bpy.types.RenderEngine.py
1 """
2 Simple Render Engine
3 ++++++++++++++++++++
4 """
5
6 import bpy
7 import bgl
8
9
10 class CustomRenderEngine(bpy.types.RenderEngine):
11     # These three members are used by blender to set up the
12     # RenderEngine; define its internal name, visible name and capabilities.
13     bl_idname = "CUSTOM"
14     bl_label = "Custom"
15     bl_use_preview = True
16
17     # Init is called whenever a new render engine instance is created. Multiple
18     # instances may exist at the same time, for example for a viewport and final
19     # render.
20     def __init__(self):
21         self.scene_data = None
22         self.draw_data = None
23
24     # When the render engine instance is destroy, this is called. Clean up any
25     # render engine data here, for example stopping running render threads.
26     def __del__(self):
27         pass
28
29     # This is the method called by Blender for both final renders (F12) and
30     # small preview for materials, world and lights.
31     def render(self, depsgraph):
32         scene = depsgraph.scene
33         scale = scene.render.resolution_percentage / 100.0
34         self.size_x = int(scene.render.resolution_x * scale)
35         self.size_y = int(scene.render.resolution_y * scale)
36
37         # Fill the render result with a flat color. The framebuffer is
38         # defined as a list of pixels, each pixel itself being a list of
39         # R,G,B,A values.
40         if self.is_preview:
41             color = [0.1, 0.2, 0.1, 1.0]
42         else:
43             color = [0.2, 0.1, 0.1, 1.0]
44
45         pixel_count = self.size_x * self.size_y
46         rect = [color] * pixel_count
47
48         # Here we write the pixel values to the RenderResult
49         result = self.begin_result(0, 0, self.size_x, self.size_y)
50         layer = result.layers[0].passes["Combined"]
51         layer.rect = rect
52         self.end_result(result)
53
54     # For viewport renders, this method gets called once at the start and
55     # whenever the scene or 3D viewport changes. This method is where data
56     # should be read from Blender in the same thread. Typically a render
57     # thread will be started to do the work while keeping Blender responsive.
58     def view_update(self, context, depsgraph):
59         region = context.region
60         view3d = context.space_data
61         scene = depsgraph.scene
62
63         # Get viewport dimensions
64         dimensions = region.width, region.height
65
66         if not self.scene_data:
67             # First time initialization
68             self.scene_data = []
69             first_time = True
70
71             # Loop over all datablocks used in the scene.
72             for datablock in depsgraph.ids:
73                 pass
74         else:
75             first_time = False
76
77             # Test which datablocks changed
78             for update in depsgraph.updates:
79                 print("Datablock updated: ", update.id.name)
80
81             # Test if any material was added, removed or changed.
82             if depsgraph.id_type_updated('MATERIAL'):
83                 print("Materials updated")
84
85         # Loop over all object instances in the scene.
86         if first_time or depsgraph.id_type_updated('OBJECT'):
87             for instance in depsgraph.object_instances:
88                 pass
89
90     # For viewport renders, this method is called whenever Blender redraws
91     # the 3D viewport. The renderer is expected to quickly draw the render
92     # with OpenGL, and not perform other expensive work.
93     # Blender will draw overlays for selection and editing on top of the
94     # rendered image automatically.
95     def view_draw(self, context, depsgraph):
96         region = context.region
97         scene = depsgraph.scene
98
99         # Get viewport dimensions
100         dimensions = region.width, region.height
101
102         # Bind shader that converts from scene linear to display space,
103         bgl.glEnable(bgl.GL_BLEND)
104         bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA);
105         self.bind_display_space_shader(scene)
106
107         if not self.draw_data or self.draw_data.dimensions != dimensions:
108             self.draw_data = CustomDrawData(dimensions)
109
110         self.draw_data.draw()
111
112         self.unbind_display_space_shader()
113         bgl.glDisable(bgl.GL_BLEND)
114
115
116 class CustomDrawData:
117     def __init__(self, dimensions):
118         # Generate dummy float image buffer
119         self.dimensions = dimensions
120         width, height = dimensions
121
122         pixels = [0.1, 0.2, 0.1, 1.0] * width * height
123         pixels = bgl.Buffer(bgl.GL_FLOAT, width * height * 4, pixels)
124
125         # Generate texture
126         self.texture = bgl.Buffer(bgl.GL_INT, 1)
127         bgl.glGenTextures(1, self.texture)
128         bgl.glActiveTexture(bgl.GL_TEXTURE0)
129         bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0])
130         bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_RGBA16F, width, height, 0, bgl.GL_RGBA, bgl.GL_FLOAT, pixels)
131         bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR)
132         bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR)
133         bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
134
135         # Bind shader that converts from scene linear to display space,
136         # use the scene's color management settings.
137         shader_program = bgl.Buffer(bgl.GL_INT, 1)
138         bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, shader_program);
139
140         # Generate vertex array
141         self.vertex_array = bgl.Buffer(bgl.GL_INT, 1)
142         bgl.glGenVertexArrays(1, self.vertex_array)
143         bgl.glBindVertexArray(self.vertex_array[0])
144
145         texturecoord_location = bgl.glGetAttribLocation(shader_program[0], "texCoord");
146         position_location = bgl.glGetAttribLocation(shader_program[0], "pos");
147
148         bgl.glEnableVertexAttribArray(texturecoord_location);
149         bgl.glEnableVertexAttribArray(position_location);
150
151         # Generate geometry buffers for drawing textured quad
152         position = [0.0, 0.0, width, 0.0, width, height, 0.0, height]
153         position = bgl.Buffer(bgl.GL_FLOAT, len(position), position)
154         texcoord = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]
155         texcoord = bgl.Buffer(bgl.GL_FLOAT, len(texcoord), texcoord)
156
157         self.vertex_buffer = bgl.Buffer(bgl.GL_INT, 2)
158
159         bgl.glGenBuffers(2, self.vertex_buffer)
160         bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vertex_buffer[0])
161         bgl.glBufferData(bgl.GL_ARRAY_BUFFER, 32, position, bgl.GL_STATIC_DRAW)
162         bgl.glVertexAttribPointer(position_location, 2, bgl.GL_FLOAT, bgl.GL_FALSE, 0, None)
163
164         bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vertex_buffer[1])
165         bgl.glBufferData(bgl.GL_ARRAY_BUFFER, 32, texcoord, bgl.GL_STATIC_DRAW)
166         bgl.glVertexAttribPointer(texturecoord_location, 2, bgl.GL_FLOAT, bgl.GL_FALSE, 0, None)
167
168         bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, 0)
169         bgl.glBindVertexArray(0)
170
171     def __del__(self):
172         bgl.glDeleteBuffers(2, self.vertex_buffer)
173         bgl.glDeleteVertexArrays(1, self.vertex_array)
174         bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
175         bgl.glDeleteTextures(1, self.texture)
176
177     def draw(self):
178         bgl.glActiveTexture(bgl.GL_TEXTURE0)
179         bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0])
180         bgl.glBindVertexArray(self.vertex_array[0])
181         bgl.glDrawArrays(bgl.GL_TRIANGLE_FAN, 0, 4);
182         bgl.glBindVertexArray(0)
183         bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
184
185
186 # RenderEngines also need to tell UI Panels that they are compatible with.
187 # We recommend to enable all panels marked as BLENDER_RENDER, and then
188 # exclude any panels that are replaced by custom panels registered by the
189 # render engine, or that are not supported.
190 def get_panels():
191     exclude_panels = {
192         'VIEWLAYER_PT_filter',
193         'VIEWLAYER_PT_layer_passes',
194     }
195
196     panels = []
197     for panel in bpy.types.Panel.__subclasses__():
198         if hasattr(panel, 'COMPAT_ENGINES') and 'BLENDER_RENDER' in panel.COMPAT_ENGINES:
199             if panel.__name__ not in exclude_panels:
200                 panels.append(panel)
201
202     return panels
203
204 def register():
205     # Register the RenderEngine
206     bpy.utils.register_class(CustomRenderEngine)
207
208     for panel in get_panels():
209         panel.COMPAT_ENGINES.add('CUSTOM')
210
211 def unregister():
212     bpy.utils.unregister_class(CustomRenderEngine)
213
214     for panel in get_panels():
215         if 'CUSTOM' in panel.COMPAT_ENGINES:
216             panel.COMPAT_ENGINES.remove('CUSTOM')
217
218
219 if __name__ == "__main__":
220     register()