+ if (m_bounds.Intersects(Rect(-1,-1,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x - 1, m_bounds.y - 1, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, -1, -1), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(-1,0,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x - 1, m_bounds.y, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, -1, 0), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(-1,1,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x - 1, m_bounds.y + 1, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, -1, 1), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(0,-1,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x, m_bounds.y - 1, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 0, -1), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(0,1,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x, m_bounds.y + 1, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 0, 1), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(1,-1,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x + 1, m_bounds.y - 1, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 1, -1), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(1,0,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x + 1, m_bounds.y, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 1, 0), remaining_depth - 1);
+ }
+ if (m_bounds.Intersects(Rect(1,1,1,1)))
+ {
+ m_bounds = Rect(m_bounds.x + 1, m_bounds.y + 1, m_bounds.w, m_bounds.h);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 1, 1), remaining_depth - 1);
+ }
+ m_bounds = old_bounds;
+ m_bounds_dirty = true;
+
+#if 0
+ m_bounds = TransformToQuadChild(old_bounds, QTC_TOP_LEFT);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].top_left, remaining_depth-1);
+ m_bounds = TransformToQuadChild(old_bounds, QTC_TOP_RIGHT);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].top_right, remaining_depth-1);
+ m_bounds = TransformToQuadChild(old_bounds, QTC_BOTTOM_LEFT);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].bottom_left, remaining_depth-1);
+ m_bounds = TransformToQuadChild(old_bounds, QTC_BOTTOM_RIGHT);
+ m_bounds_dirty = true;
+ RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].bottom_right, remaining_depth-1);
+ m_bounds = old_bounds;
+ m_bounds_dirty = true;
+#endif
+}
+#endif
+
+void View::RenderRange(int width, int height, unsigned first_obj, unsigned last_obj)
+{
+ glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 43, -1, "View::RenderRange()");
+ if (m_render_dirty) // document has changed
+ PrepareRender();
+
+ if (m_buffer_dirty || m_bounds_dirty || !m_lazy_rendering) // object bounds have changed
+ {
+ if (m_use_gpu_rendering)
+ UpdateObjBoundsVBO(first_obj, last_obj);
+ }
+
+ if (m_use_gpu_transform)
+ {
+ #ifdef TRANSFORM_OBJECTS_NOT_VIEW
+ //Debug("Transform objects, not view");
+ GLfloat glbounds[] = {0.0f, 0.0f, 1.0f, 1.0f,
+ 0.0f, 0.0f, float(width), float(height)};
+ #else
+ GLfloat glbounds[] = {static_cast<GLfloat>(Float(m_bounds.x)), static_cast<GLfloat>(Float(m_bounds.y)), static_cast<GLfloat>(Float(m_bounds.w)), static_cast<GLfloat>(Float(m_bounds.h)),
+ 0.0, 0.0, static_cast<GLfloat>(width), static_cast<GLfloat>(height)};
+ #endif
+ m_bounds_ubo.Upload(sizeof(float)*8, glbounds);
+ }
+ else
+ {
+ GLfloat glbounds[] = {0.0f, 0.0f, 1.0f, 1.0f,
+ 0.0f, 0.0f, float(width), float(height)};
+ m_bounds_ubo.Upload(sizeof(float)*8, glbounds);
+ }
+ m_bounds_dirty = false;
+
+
+ // Render using GPU
+ if (m_use_gpu_rendering)
+ {
+ if (m_colour.a < 1.0f)
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ m_objbounds_vbo.Bind();
+ m_bounds_ubo.Bind();
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ for (unsigned i = 0; i < m_object_renderers.size(); ++i)
+ {
+ m_object_renderers[i]->RenderUsingGPU(first_obj, last_obj);
+ }
+
+ glDisableVertexAttribArray(0);
+ if (m_colour.a < 1.0f)
+ {
+ glDisable(GL_BLEND);
+ }
+ }
+ else // Rasterise on CPU then blit texture to GPU
+ {
+
+ for (unsigned i = 0; i < m_object_renderers.size(); ++i)
+ {
+ m_object_renderers[i]->RenderUsingCPU(m_document.m_objects, *this, {m_cpu_rendering_pixels, width, height}, first_obj, last_obj);
+ }
+ }
+ glPopDebugGroup();
+}
+
+void View::UpdateObjBoundsVBO(unsigned first_obj, unsigned last_obj)
+{
+ //m_objbounds_vbo.Invalidate();
+ m_objbounds_vbo.SetType(GraphicsBuffer::BufferTypeVertex);
+ m_objbounds_vbo.SetName("Object Bounds VBO");
+
+ #ifndef TRANSFORM_OBJECTS_NOT_VIEW
+ if (m_use_gpu_transform)
+ {
+ m_objbounds_vbo.SetUsage(GraphicsBuffer::BufferUsageStaticDraw);
+ }
+ else
+ #endif //TRANSFORM_OBJECTS_NOT_VIEW
+ {
+ m_objbounds_vbo.SetUsage(GraphicsBuffer::BufferUsageDynamicCopy);
+ }
+ m_objbounds_vbo.Resize(m_document.ObjectCount()*sizeof(GPUObjBounds));
+
+ BufferBuilder<GPUObjBounds> obj_bounds_builder(m_objbounds_vbo.MapRange(first_obj*sizeof(GPUObjBounds), (last_obj-first_obj)*sizeof(GPUObjBounds), false, true, true), m_objbounds_vbo.GetSize());
+
+ for (unsigned id = first_obj; id < last_obj; ++id)
+ {
+ Rect obj_bounds;
+ if (m_use_gpu_transform)
+ {
+ obj_bounds = m_document.m_objects.bounds[id];
+ }
+ else
+ {
+ obj_bounds = TransformToViewCoords(m_document.m_objects.bounds[id]);
+ }
+ GPUObjBounds gpu_bounds = {
+ (float)Float(obj_bounds.x),
+ (float)Float(obj_bounds.y),
+ (float)Float(obj_bounds.x + obj_bounds.w),
+ (float)Float(obj_bounds.y + obj_bounds.h)
+ };
+
+ obj_bounds_builder.Add(gpu_bounds);
+
+ }
+ m_objbounds_vbo.UnMap();
+}
+/**
+ * Prepare the document for rendering
+ * Will be called on View::Render if m_render_dirty is set
+ * (Called at least once, on the first Render)
+ */
+void View::PrepareRender()
+{
+ Debug("Recreate buffers with %u objects", m_document.ObjectCount());
+ // Prepare bounds vbo
+ if (UsingGPURendering())
+ {
+ m_bounds_ubo.Invalidate();
+ m_bounds_ubo.SetType(GraphicsBuffer::BufferTypeUniform);
+ m_bounds_ubo.SetUsage(GraphicsBuffer::BufferUsageStreamDraw);
+ m_bounds_ubo.SetName("m_bounds_ubo: Screen bounds.");
+ }
+
+ // Instead of having each ObjectRenderer go through the whole document
+ // we initialise them, go through the document once adding to the appropriate Renderers
+ // and then finalise them
+ // This will totally be efficient if we have like, a lot of distinct ObjectTypes. Which could totally happen. You never know.
+
+ // Prepare the buffers
+ for (unsigned i = 0; i < m_object_renderers.size(); ++i)
+ {
+ m_object_renderers[i]->PrepareBuffers(m_document.ObjectCount());
+ }
+
+ // Add objects from Document to buffers