diff --git a/src/c3nav/mapdata/render/engines/base.py b/src/c3nav/mapdata/render/engines/base.py index 077bef5d..9a798eea 100644 --- a/src/c3nav/mapdata/render/engines/base.py +++ b/src/c3nav/mapdata/render/engines/base.py @@ -53,6 +53,10 @@ class RenderEngine(ABC): # render the image to png. pass + @staticmethod + def hex_to_rgb(hexcolor): + return tuple(int(hexcolor[i:i + 2], 16)/255 for i in range(1, 6, 2)) + def clip_altitudes(self, new_geometry, new_altitude=None): # register new geometry with an altitude # a geometry with no altitude will reset the altitude information of its area as if nothing was ever there diff --git a/src/c3nav/mapdata/render/engines/opengl.py b/src/c3nav/mapdata/render/engines/opengl.py index d6253454..4290f37e 100644 --- a/src/c3nav/mapdata/render/engines/opengl.py +++ b/src/c3nav/mapdata/render/engines/opengl.py @@ -1,10 +1,15 @@ import io +from collections import deque +from typing import Union import ModernGL import numpy as np from PIL import Image +from shapely.geometry import MultiPolygon, Polygon +from trimesh.creation import triangulate_polygon from c3nav.mapdata.render.engines.base import RenderEngine +from c3nav.mapdata.utils.geometry import assert_multipolygon class OpenGLEngine(RenderEngine): @@ -15,8 +20,7 @@ class OpenGLEngine(RenderEngine): self.ctx = ModernGL.create_standalone_context() self.color_rbo = self.ctx.renderbuffer((self.width, self.height)) - self.depth_rbo = self.ctx.depth_renderbuffer((self.width, self.height)) - self.fbo = self.ctx.framebuffer([self.color_rbo], self.depth_rbo) + self.fbo = self.ctx.framebuffer([self.color_rbo]) self.fbo.use() self.ctx.clear(*(i/255 for i in self.background_rgb)) @@ -42,12 +46,36 @@ class OpenGLEngine(RenderEngine): '''), ]) + scale_x = self.scale / self.width * 2 + scale_y = self.scale / self.height * 2 + + self.np_scale = np.array((scale_x, -scale_y)) + self.np_offset = np.array((-self.minx * scale_x - 1, self.maxy * scale_y - 1)) + + def _create_geometry(self, geometry: Union[Polygon, MultiPolygon], append=None): + triangles = deque() + + for i, polygon in enumerate(assert_multipolygon(geometry)): + vertices, faces = triangulate_polygon(polygon) + triangles.append(vertices[faces.flatten()]) + + vertices = np.vstack(triangles).astype(np.float32) + vertices = vertices * self.np_scale + self.np_offset + if append is not None: + append = np.array(append, dtype=np.float32).flatten() + vertices = np.hstack(( + vertices, + append.reshape(1, append.size).repeat(vertices.shape[0], 0) + )) + return vertices.flatten() + def _add_geometry(self, geometry, fill=None, stroke=None, altitude=None, height=None, shape_cache_key=None): - pass + if fill is not None: + self.vertices.append(self._create_geometry(geometry, self.hex_to_rgb(fill.color))) def get_png(self) -> bytes: if self.vertices: - vbo = self.ctx.buffer(np.hstack(self.vertices).tobytes()) + vbo = self.ctx.buffer(np.hstack(self.vertices).astype(np.float32).tobytes()) # We control the 'in_vert' and `in_color' variables vao = self.ctx.simple_vertex_array(self.prog, vbo, ['in_vert', 'in_color']) diff --git a/src/requirements/opengl.txt b/src/requirements/opengl.txt new file mode 100644 index 00000000..5531bc3a --- /dev/null +++ b/src/requirements/opengl.txt @@ -0,0 +1 @@ +ModernGL>=4.2,<4.3 diff --git a/src/requirements/production.txt b/src/requirements/production.txt index 75c4723e..64f89b3e 100644 --- a/src/requirements/production.txt +++ b/src/requirements/production.txt @@ -5,6 +5,7 @@ csscompressor djangorestframework>=3.6,<3.7 django-filter>=1.0,<1.1 shapely>=1.5,<1.6 +trimesh>=2.20,<2.21 rtree>=0.8,<0.9 celery>=4.0,<4.1 requests>=2.17,<2.18