diff --git a/repos/gems/include/nano3d/cube_shape.h b/repos/gems/include/nano3d/cube_shape.h new file mode 100644 index 000000000..3c1c19da0 --- /dev/null +++ b/repos/gems/include/nano3d/cube_shape.h @@ -0,0 +1,69 @@ +/* + * \brief Cube 3D object + * \author Norman Feske + * \date 2015-06-19 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__NANO3D__CUBE_SHAPE_H_ +#define _INCLUDE__NANO3D__CUBE_SHAPE_H_ + +#include + +namespace Nano3d { class Cube_shape; } + + +class Nano3d::Cube_shape +{ + private: + + enum { NUM_VERTICES = 8, NUM_FACES = 6 }; + + typedef Nano3d::Vertex_array Vertex_array; + + Vertex_array _vertices; + + enum { VERTICES_PER_FACE = 4 }; + + typedef unsigned Face[VERTICES_PER_FACE]; + + Face _faces[NUM_FACES] { { 0, 1, 3, 2 }, + { 6, 7, 5, 4 }, + { 1, 0, 4, 5 }, + { 3, 1, 5, 7 }, + { 2, 3, 7, 6 }, + { 0, 2, 6, 4 } }; + + public: + + Cube_shape(int size) + { + for (unsigned i = 0; i < NUM_VERTICES; i++) + _vertices[i] = Nano3d::Vertex((i&1) ? size : -size, + (i&2) ? size : -size, + (i&4) ? size : -size); + } + + Vertex_array const &vertex_array() const { return _vertices; } + + /** + * Call functor 'fn' for each face of the object + * + * The functor is called with an array of 'unsigned' vertex indices + * and the number of indices as arguments. + */ + template + void for_each_face(FN const &fn) const + { + for (unsigned i = 0; i < NUM_FACES; i++) + fn(_faces[i], VERTICES_PER_FACE); + } +}; + +#endif /* _INCLUDE__NANO3D__CUBE_SHAPE_H_ */ diff --git a/repos/gems/include/nano3d/dodecahedron_shape.h b/repos/gems/include/nano3d/dodecahedron_shape.h new file mode 100644 index 000000000..c5af09ea8 --- /dev/null +++ b/repos/gems/include/nano3d/dodecahedron_shape.h @@ -0,0 +1,231 @@ +/* + * \brief Dodecahedron 3D object + * \author Norman Feske + * \date 2015-06-19 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__NANO3D__DODECAHEDRON_SHAPE_H_ +#define _INCLUDE__NANO3D__DODECAHEDRON_SHAPE_H_ + +#include + +namespace Nano3d { class Dodecahedron_shape; } + + +class Nano3d::Dodecahedron_shape +{ + private: + + enum { NUM_VERTICES = 20, NUM_EDGES = 30, NUM_FACES = 12 }; + + struct Edge + { + unsigned left_face, right_face; + + unsigned vertex[2]; + + Edge() : left_face(0), right_face(0), vertex { 0, 0 } { } + + Edge(unsigned vertex_0, unsigned vertex_1, + unsigned left_face, unsigned right_face) + : + left_face(left_face), right_face(right_face), + vertex { vertex_0, vertex_1 } + { } + }; + + class Face + { + public: + + enum { NUM_EDGES = 5 }; + + private: + + int _edges[NUM_EDGES]; + + public: + + Face() : _edges{} { } + + template + Face(EDGE_INDICES... edge_indices) + : + _edges { edge_indices... } + { } + + static constexpr unsigned num_edges() { return NUM_EDGES; } + + int edge(unsigned i) const { return _edges[i]; } + }; + + typedef Nano3d::Vertex_array Vertex_array; + + Vertex_array _vertices; + + Edge _edges[NUM_EDGES]; + Face _faces[NUM_FACES]; + + /* ratio of edge length to radius, as 16.16 fixpoint number */ + enum { A_TO_R = 46769 }; + + /* angle between two edges, scaled to 0..1024 range */ + enum { DIHEDRAL_ANGLE = 332 }; + + public: + + /** + * \param r radius of the surrounding sphere + */ + Dodecahedron_shape(int r) + { + /* + * Vertices + */ + + /* + * There are four level, each with 5 vertices. + * + * y0 and y1 are the y positions of the first and second level. + * r0 and r1 are the radius of first and second levels. + * The third and fourth levels are symetric to the first levels. + */ + int const y0 = -(r * 52078) >> 16; /* r*0.7947 */ + int const y1 = -(r * 11030) >> 16; + int const r0 = (r * 39780) >> 16; /* r*0.607 */ + int const r1 = (r * 63910) >> 16; + + enum { ANGLE_STEP = 1024 / 5 }; + enum { ANGLE_HALF_STEP = 1024 / 10 }; + + int j = 0; /* index into '_vertices' array */ + + /* level 1 */ + for (int i = 0; i < 5; i++) { + int const a = i*ANGLE_STEP; + _vertices[j++] = Vertex((r0*sin_frac16(a)) >> 16, y0, + (r0*cos_frac16(a)) >> 16); + } + + /* level 2 */ + for (int i = 0; i < 5; i++) { + int const a = i*ANGLE_STEP; + _vertices[j++] = Vertex((r1*sin_frac16(a)) >> 16, y1, + (r1*cos_frac16(a)) >> 16); + } + + /* level 3 */ + for (int i = 0; i < 5; i++) { + int const a = i*ANGLE_STEP + ANGLE_HALF_STEP; + _vertices[j++] = Vertex((r1*sin_frac16(a)) >> 16, -y1, + (r1*cos_frac16(a)) >> 16); + } + + /* level 4 */ + for (int i = 0; i < 5; i++) { + int const a = i*ANGLE_STEP + ANGLE_HALF_STEP; + _vertices[j++] = Vertex((r0*sin_frac16(a)) >> 16, -y0, + (r0*cos_frac16(a)) >> 16); + } + + /* + * Edges + */ + + j = 0; /* index into '_edges' array */ + + /* level 1 */ + for (int i = 0; i < 5; i++) + _edges[j++] = Edge(i, (i+1)%5, i + 1, 0); + + /* level 1 to level 2 */ + for (int i = 0; i < 5; i++) + _edges[j++] = Edge(i, i + 5, 1 + (i + 4)%5, 1 + i); + + /* level 2 to level 3 */ + for (int i = 0; i < 5; i++) + _edges[j++] = Edge(i+5, i + 10, 1 + 5 + (i + 4)%5, 1 + i); + + /* level 3 to level 2 */ + for (int i = 0; i < 5; i++) + _edges[j++] = Edge(i + 10, (i + 1)%5 + 5, 1 + 5 + i, 1 + i); + + /* level 3 to level 4 */ + for (int i = 0; i < 5; i++) + _edges[j++] = Edge(i + 10, i + 15, 1 + 5 + (i + 4)%5, 1 + 5 + i); + + /* level 4 */ + for (int i = 0; i < 5; i++) + _edges[j++] = Edge(i + 15, (i + 1)%5 + 15, 11, 1 + 5 + i); + + /* + * Faces + */ + + j = 0; /* index into '_faces' array */ + _faces[j++] = Face(0, 1, 2, 3, 4); + + for (int i = 0; i < 5; i++) + _faces[j++] = Face(i, i + 5, i + 10, i + 15, 5 + (1 + i)%5); + + for (int i = 0; i < 5; i++) + _faces[j++] = Face(i+20, i + 25, (i + 1)%5 + 20, 10 + (i + 1)%5, 15 + i); + + _faces[j++] = Face(29, 28, 27, 26, 25); + } + + Vertex_array const &vertex_array() const { return _vertices; } + + /** + * Call functor 'fn' for each face of the object + * + * The functor is called with an array of 'unsigned' vertex indices + * and the number of indices as arguments. + */ + template + void for_each_face(FN const &fn) const + { + for (unsigned i = 0; i < NUM_FACES; i++) { + + Face const face = _faces[i]; + + /* + * Asssemble array of vertex indices for the current face. + */ + unsigned vertex_indices[Face::num_edges()]; + + bool skip_face = false; + + for (unsigned j = 0; j < Face::num_edges(); j++) { + + Edge const edge = _edges[face.edge(j)]; + + int vertex_idx = -1; + + if (edge.left_face == i) + vertex_idx = edge.vertex[1]; + + if (edge.right_face == i) + vertex_idx = edge.vertex[0]; + + if (vertex_idx == -1) + skip_face = true; + + vertex_indices[j] = vertex_idx; + } + + /* call functor with the information about the face vertices */ + if (!skip_face) + fn(vertex_indices, Face::num_edges()); + } + } +}; + +#endif /* _INCLUDE__NANO3D__DODECAHEDRON_SHAPE_H_ */ diff --git a/repos/gems/include/nano3d/scene.h b/repos/gems/include/nano3d/scene.h new file mode 100644 index 000000000..f476a5285 --- /dev/null +++ b/repos/gems/include/nano3d/scene.h @@ -0,0 +1,313 @@ +/* + * \brief Simple framework for rendering an animated scene + * \author Norman Feske + * \date 2015-06-22 + * + * The 'Scene' class template contains the code for setting up a nitpicker + * view with a triple-buffer for rendering tearing-free animations. + * A derrived class implements the to-be-displayed content in the virtual + * 'render' method. + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__NANO3D__SCENE_H_ +#define _INCLUDE__NANO3D__SCENE_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +namespace Nano3d { + + struct Input_handler; + template class Scene; +} + + +struct Nano3d::Input_handler +{ + virtual void handle_input(Input::Event const [], unsigned num_events) = 0; +}; + + +template +class Nano3d::Scene +{ + public: + + class Unsupported_color_depth { }; + + typedef Genode::Pixel_alpha8 Pixel_alpha8; + + virtual void render(Genode::Surface &pixel_surface, + Genode::Surface &alpha_surface) = 0; + + private: + + Genode::Signal_receiver &_sig_rec; + + /** + * Position and size of nitpicker view + */ + Nitpicker::Point const _pos; + Nitpicker::Area const _size; + + Nitpicker::Connection _nitpicker; + + struct Mapped_framebuffer + { + enum { NUM_BUFFERS = 3 }; + + static Framebuffer::Session & + _init_framebuffer(Nitpicker::Connection &nitpicker, + Nitpicker::Area const size) + { + Framebuffer::Mode::Format const format = nitpicker.mode().format(); + if (format != Framebuffer::Mode::RGB565) { + PERR("framebuffer mode %d is not supported\n", format); + throw Unsupported_color_depth(); + } + + /* + * Dimension the virtual framebuffer 3 times as high as the + * visible view because it contains the visible buffer, the + * front buffer, and the back buffer. + */ + bool const use_alpha = true; + unsigned const height = size.h()*NUM_BUFFERS; + nitpicker.buffer(Framebuffer::Mode(size.w(), height, format), + use_alpha); + + return *nitpicker.framebuffer(); + } + + Framebuffer::Session &framebuffer; + + Framebuffer::Mode const mode = framebuffer.mode(); + + /** + * Return visible size + */ + Nitpicker::Area size() const + { + return Nitpicker::Area(mode.width(), mode.height()/NUM_BUFFERS); + } + + Genode::Attached_dataspace ds { framebuffer.dataspace() }; + + PT *pixel_base(unsigned i) + { + return (PT *)(ds.local_addr() + i*size().count()); + } + + Pixel_alpha8 *alpha_base(unsigned i) + { + Pixel_alpha8 * const alpha_base = + (Pixel_alpha8 *)(ds.local_addr() + NUM_BUFFERS*size().count()); + + return alpha_base + i*size().count(); + } + + /** + * Set or clear the input mask for the virtual framebuffer + */ + void input_mask(bool input_enabled) + { + /* + * The input-mask buffer follows the alpha buffer. Hence, we + * can obtain the base address by requesting the base of + * the (non-exiting) alpha buffer (using NUM_BUFFERS as index) + * beyond the actual alpha buffers. + */ + Genode::memset(alpha_base(NUM_BUFFERS), input_enabled, + NUM_BUFFERS*size().count()); + } + + Mapped_framebuffer(Nitpicker::Connection &nitpicker, Nitpicker::Area size) + : + framebuffer(_init_framebuffer(nitpicker, size)) + { } + + } _framebuffer { _nitpicker, _size }; + + Nitpicker::Session::View_handle _view_handle = _nitpicker.create_view(); + + typedef Genode::Surface Pixel_surface; + typedef Genode::Surface Alpha_surface; + + struct Surface + { + Pixel_surface pixel; + Alpha_surface alpha; + + Surface(PT *pixel_base, Genode::Pixel_alpha8 *alpha_base, + Genode::Surface_base::Area size) + : + pixel(pixel_base, size), alpha(alpha_base, size) + { } + + Genode::Surface_base::Area size() const { return pixel.size(); } + + template + void _clear(Genode::Surface &surface) + { + Genode::size_t n = (surface.size().count()*sizeof(T))/sizeof(long); + for (long *dst = (long *)surface.addr(); n--; dst++) + *dst = 0; + } + + void clear() + { + _clear(pixel); + _clear(alpha); + } + }; + + Surface _surface_0 { _framebuffer.pixel_base(0), _framebuffer.alpha_base(0), + _framebuffer.size() }; + Surface _surface_1 { _framebuffer.pixel_base(1), _framebuffer.alpha_base(1), + _framebuffer.size() }; + Surface _surface_2 { _framebuffer.pixel_base(2), _framebuffer.alpha_base(2), + _framebuffer.size() }; + + Surface *_surface_visible = &_surface_0; + Surface *_surface_front = &_surface_1; + Surface *_surface_back = &_surface_2; + + bool _do_sync = false; + + Timer::Connection _timer; + + Genode::Attached_dataspace _input_ds { _nitpicker.input()->dataspace() }; + + Input_handler *_input_handler = nullptr; + + void _handle_input(unsigned) + { + if (!_input_handler) + return; + + while (int num = _nitpicker.input()->flush()) { + + auto const *ev_buf = _input_ds.local_addr(); + + if (_input_handler) + _input_handler->handle_input(ev_buf, num); + } + } + + Genode::Signal_dispatcher _input_dispatcher { + _sig_rec, *this, &Scene::_handle_input }; + + void _swap_back_and_front_surfaces() + { + Surface *tmp = _surface_back; + _surface_back = _surface_front; + _surface_front = tmp; + } + + void _swap_visible_and_front_surfaces() + { + Surface *tmp = _surface_visible; + _surface_visible = _surface_front; + _surface_front = tmp; + } + + void _handle_period(unsigned) + { + if (_do_sync) + return; + + _surface_back->clear(); + + render(_surface_back->pixel, _surface_back->alpha); + + _swap_back_and_front_surfaces(); + + /* swap front and back buffers on next sync */ + _do_sync = true; + } + + Genode::Signal_dispatcher _periodic_dispatcher { + _sig_rec, *this, &Scene::_handle_period }; + + void _handle_sync(unsigned) + { + /* rendering of scene is not complete, yet */ + if (!_do_sync) + return; + + _swap_visible_and_front_surfaces(); + _swap_back_and_front_surfaces(); + + int const h = _framebuffer.size().h(); + + int const buf_y = (_surface_visible == &_surface_0) ? 0 + : (_surface_visible == &_surface_1) ? -h + : -2*h; + + Nitpicker::Point const offset(0, buf_y); + _nitpicker.enqueue(_view_handle, offset); + _nitpicker.execute(); + + _do_sync = false; + } + + Genode::Signal_dispatcher _sync_dispatcher { + _sig_rec, *this, &Scene::_handle_sync }; + + typedef Nitpicker::Session::Command Command; + + public: + + Scene(Genode::Signal_receiver &sig_rec, unsigned update_rate_ms, + Nitpicker::Point pos, Nitpicker::Area size) + : + _sig_rec(sig_rec), _pos(pos), _size(size) + { + Nitpicker::Rect rect(_pos, _size); + _nitpicker.enqueue(_view_handle, rect); + _nitpicker.enqueue(_view_handle); + _nitpicker.execute(); + + _nitpicker.input()->sigh(_input_dispatcher); + + _timer.sigh(_periodic_dispatcher); + _timer.trigger_periodic(1000*update_rate_ms); + + _framebuffer.framebuffer.sync_sigh(_sync_dispatcher); + } + + static void dispatch_signals_loop(Genode::Signal_receiver &sig_rec) + { + while (1) { + + Genode::Signal signal = sig_rec.wait_for_signal(); + + Genode::Signal_dispatcher_base *dispatcher = + static_cast(signal.context()); + + dispatcher->dispatch(signal.num()); + } + } + + unsigned long elapsed_ms() const { return _timer.elapsed_ms(); } + + void input_handler(Input_handler *input_handler) + { + _framebuffer.input_mask(input_handler ? true : false); + _input_handler = input_handler; + } +}; + +#endif /* _INCLUDE__NANO3D__SCENE_H_ */ diff --git a/repos/gems/include/nano3d/sincos_frac16.h b/repos/gems/include/nano3d/sincos_frac16.h new file mode 100644 index 000000000..3993fd7b9 --- /dev/null +++ b/repos/gems/include/nano3d/sincos_frac16.h @@ -0,0 +1,120 @@ +/* + * \brief Table of sine and cosine values in 16.16 fractional format + * \author Norman Feske + * \date 2015-06-19 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__NANO3D__SINCOS_FRAC16_H_ +#define _INCLUDE__NANO3D__SINCOS_FRAC16_H_ + +namespace Nano3d { class Sincos_frac16; }; + + +class Nano3d::Sincos_frac16 +{ + public: + + enum { STEPS = 1024 }; + + private: + + int _table[STEPS + STEPS/4]; + + public: + + inline Sincos_frac16(); + + int sin(int angle) const { return _table[angle & (STEPS - 1)]; } + int cos(int angle) const { return sin(angle + STEPS/4); } +}; + + +Nano3d::Sincos_frac16::Sincos_frac16() +{ + int const cos_mid = 0x7fff; + int const cos_low = 0x310b; /* cos(360/1024) = 0x7fff6216 */ + int const sin_mid = 0x00c9; + int const sin_low = 0x07c4; /* sin(360/1024) = 0x00c90f87 */ + + int x_mid = 0x7fff; + int x_low = 0x7fff; /* x = 1.0 */ + int y_mid = 0; + int y_low = 0; /* y = 0.0 */ + + int nx_high, ny_high; + int nx_mid, nx_low; + int ny_mid, ny_low; + + for (unsigned i = 0; i < (STEPS >> 2); i++) { + + /* store current sine value */ + _table[i] = y_mid << 1; + _table[(STEPS >> 1) - i - 1] = y_mid << 1; + _table[i + (STEPS >> 1)] = -y_mid << 1; + _table[STEPS - i - 1] = -y_mid << 1; + + /* rotate sin/cos values */ + + /* x' = x*cos - y*sin */ + + nx_low = x_low*cos_low + - y_low*sin_low; + + nx_mid = x_low*cos_mid + x_mid*cos_low + - y_low*sin_mid - y_mid*sin_low; + nx_mid += (nx_low >> 14); + + nx_high = x_mid*cos_mid + - y_mid*sin_mid + + (nx_mid >> 15); + + nx_high = nx_high << 1; + + /* y' = y*cos + x*sin */ + + ny_low = y_low*cos_low + + x_low*sin_low; + + ny_mid = y_low*cos_mid + y_mid*cos_low + + x_low*sin_mid + x_mid*sin_low + + (ny_low >> 14); + + ny_high = y_mid*cos_mid + + x_mid*sin_mid + + (ny_mid >> 15); + + ny_high = ny_high << 1; + + /* use new sin/cos values for next iteration, preserve sign */ + x_low = (nx_high & 0x80000000) ? (nx_high | (~0 << 16)) : (nx_high & 0xffff); + x_low = x_low >> 1; + x_mid = nx_high >> 16; + + y_low = (ny_high & 0x80000000) ? (ny_high | (~0 << 16)) : (ny_high & 0xffff); + y_low = y_low >> 1; + y_mid = ny_high >> 16; + } +} + + +namespace Nano3d { + + static Sincos_frac16 const &sincos_frac16() + { + static Sincos_frac16 inst; + return inst; + } + + + static inline int sin_frac16(int angle) { return sincos_frac16().sin(angle); } + static inline int cos_frac16(int angle) { return sincos_frac16().cos(angle); } +}; + +#endif /* _INCLUDE__NANO3D__SINCOS_FRAC16_H_ */ diff --git a/repos/gems/include/nano3d/sqrt.h b/repos/gems/include/nano3d/sqrt.h new file mode 100644 index 000000000..8b2eb94ec --- /dev/null +++ b/repos/gems/include/nano3d/sqrt.h @@ -0,0 +1,41 @@ +/* + * \brief Square root of integer values + * \date 2010-09-27 + * \author Norman Feske + */ + +/* + * Copyright (C) 2010-2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__NANO3D__SQRT_H_ +#define _INCLUDE__NANO3D__SQRT_H__ + +namespace Nano3d { + + /** + * Calculate square root of an integer value + */ + template + T sqrt(T value) + { + /* + * Calculate square root using nested intervalls. The range of values + * is log(x) with x being the maximum value of type T. We narrow the + * result bit by bit starting with the most significant bit. + */ + T result = 0; + for (T i = sizeof(T)*8 / 2; i > 0; i--) { + T const bit = i - 1; + T const test = result + (1 << bit); + if (test*test <= value) + result = test; + } + return result; + } +} + +#endif /* _INCLUDE__NANO3D__SQRT_H__ */ diff --git a/repos/gems/include/nano3d/vertex_array.h b/repos/gems/include/nano3d/vertex_array.h new file mode 100644 index 000000000..350a7bc23 --- /dev/null +++ b/repos/gems/include/nano3d/vertex_array.h @@ -0,0 +1,161 @@ +/* + * \brief Vertex array + * \author Norman Feske + * \date 2010-09-27 + */ + +/* + * Copyright (C) 2010-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__NANO3D__VERTEX_ARRAY_H_ +#define _INCLUDE__NANO3D__VERTEX_ARRAY_H_ + +#include + +namespace Nano3d { + + template class Vec2; + template class Vec3; + + typedef Vec3 Vertex; + + template class Vertex_array; +} + + +template +class Nano3d::Vec2 +{ + private: + + T _x, _y; + + public: + + Vec2() : _x(0), _y(0) { } + + Vec2(T x, T y) : _x(x), _y(y) { } + + T &x() { return _x; } + T &y() { return _y; } + + T x() const { return _x; } + T y() const { return _y; } + + void rotate(int sina, int cosa) + { + int x = _x*cosa - _y*sina; + int y = _x*sina + _y*cosa; + + _x = x >> 16; + _y = y >> 16; + } +}; + + +template +class Nano3d::Vec3 +{ + private: + + T _x, _y, _z; + + public: + + Vec3() : _x(0), _y(0), _z(0) { } + + Vec3(T x, T y, T z) : _x(x), _y(y), _z(z) { } + + T &x() { return _x; } + T &y() { return _y; } + T &z() { return _z; } + + T x() const { return _x; } + T y() const { return _y; } + T z() const { return _z; } +}; + + +template +class Nano3d::Vertex_array +{ + protected: + + Vertex _buf[MAX_VERTICES]; + + void _rotate(int & (Vec3::*x)(), int & (Vec3::*y)(), int angle) + { + int sina = sin_frac16(angle); + int cosa = cos_frac16(angle); + + Vertex *vertex = &_buf[0]; + + for (unsigned i = 0; i < MAX_VERTICES; i++, vertex++) { + + Vec2 p((vertex->*x)(), (vertex->*y)()); + + p.rotate(sina, cosa); + + (vertex->*x)() = p.x(); + (vertex->*y)() = p.y(); + } + } + + public: + + /** + * Constructor + */ + Vertex_array() { } + + Vertex &operator [] (unsigned index) { return _buf[index]; } + + /** + * Rotate vertices around x, y, and z axis + */ + void rotate_x(int angle) { _rotate(&Vertex::y, &Vertex::z, angle); } + void rotate_y(int angle) { _rotate(&Vertex::x, &Vertex::z, angle); } + void rotate_z(int angle) { _rotate(&Vertex::x, &Vertex::y, angle); } + + /** + * Apply central projection to vertices + * + * FIXME: Add proper documentation of the parameters. + * + * \param z_shift Recommended value is 1600 + * \param distance Recommended value is screen height + */ + void project(int z_shift, int distance) + { + Vertex *vertex = &_buf[0]; + for (unsigned i = 0; i < MAX_VERTICES; i++, vertex++) { + + int z = (vertex->z() >> 5) + z_shift - 1; + + /* avoid division by zero */ + if (z == 0) z += 1; + + vertex->x() = ((vertex->x() >> 5) * distance) / z; + vertex->y() = ((vertex->y() >> 5) * distance) / z; + } + } + + /** + * Translate vertices + */ + void translate(int dx, int dy, int dz) + { + Vertex *vertex = &_buf[0]; + for (unsigned i = 0; i < MAX_VERTICES; i++, vertex++) { + vertex->x() += dx; + vertex->y() += dy; + vertex->z() += dz; + } + } +}; + +#endif /* _INCLUDE__NANO3D__VERTEX_ARRAY_H_ */ diff --git a/repos/gems/include/polygon_gfx/clipping.h b/repos/gems/include/polygon_gfx/clipping.h new file mode 100644 index 000000000..008a62ae7 --- /dev/null +++ b/repos/gems/include/polygon_gfx/clipping.h @@ -0,0 +1,216 @@ +/* + * \brief Polygon clipping + * \date 2015-06-19 + * \author Norman Feske + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__CLIPPING_H_ +#define _INCLUDE__POLYGON_GFX__CLIPPING_H_ + +namespace Polygon { + + struct Point_base; + + inline int intersect_ratio(int, int, int); + + template struct Clipper_vertical; + template struct Clipper_horizontal; + + struct Clipper_min; + struct Clipper_max; + + template struct Clipper; + + template struct Clipper_2d; +} + + +/** + * Common base class of polygon points + */ +struct Polygon::Point_base : Genode::Point<> +{ + /* + * Number of attributes to interpolate along the polygon edges. This value + * must be defined by each derived class. + */ + enum { NUM_EDGE_ATTRIBUTES = 1 }; + + /** + * Constructors + */ + Point_base() { } + Point_base(int x, int y): Genode::Point<>(x, y) { } + + /** + * Return edge attribute by ID + */ + inline int edge_attr(int id) const { return x(); } + + /** + * Assign value to edge attribute with specified ID + */ + inline void edge_attr(int id, int value) { *this = Point_base(value, y()); } +}; + + +/** + * Calculate ratio of range intersection + * + * \param v_start Start of range + * \param v_end End of range + * \param v_cut Range cut (should be in interval v_start...v_end) + * \return Ratio of intersection as 16.16 fixpoint + * + * The input arguments 'v_start', 'v_end', 'v_cut' must use only + * the lower 16 bits of the int type. + */ +inline int Polygon::intersect_ratio(int v_start, int v_end, int v_cut) +{ + int dv = v_end - v_start, + dv_cut = v_cut - v_start; + + return dv ? (dv_cut<<16)/dv : 0; +} + + +/** + * Support for vertical clipping boundary + */ +template +struct Polygon::Clipper_vertical +{ + /** + * Select clipping-sensitive attribute from polygon point + */ + static int clip_value(POINT p) { return p.x(); } + + /** + * Calculate intersection point + */ + static POINT clip(POINT p1, POINT p2, int clip) + { + /* + * Enforce unique x order of points to apply rounding errors + * consistently also when edge points are specified in reverse. + * Typically the same edge is used in reverse direction by + * each neighboured polygon. + */ + if (clip_value(p1) > clip_value(p2)) { POINT tmp = p1; p1 = p2; p2 = tmp; } + + /* calculate ratio of the intersection of edge and clipping boundary */ + int ratio = intersect_ratio(p1.x(), p2.x(), clip); + + /* calculate y value at intersection point */ + POINT result; + *(Point_base *)&result = Point_base(clip, p1.y() + ((ratio*(p2.y() - p1.y()))>>16)); + + /* calculate intersection values for edge attributes other than x */ + for (int i = 1; i < POINT::NUM_EDGE_ATTRIBUTES; i++) { + int v1 = p1.edge_attr(i), + v2 = p2.edge_attr(i); + result.edge_attr(i, v1 + ((ratio*(v2 - v1))>>16)); + } + return result; + } +}; + + +/** + * Support for horizontal clipping boundary + */ +template +struct Polygon::Clipper_horizontal +{ + /** + * Select clipping-sensitive attribute from polygon point + */ + static int clip_value(POINT p) { return p.y(); } + + /** + * Calculate intersection point + */ + static POINT clip(POINT p1, POINT p2, int clip) + { + if (clip_value(p1) > clip_value(p2)) { POINT tmp = p1; p1 = p2; p2 = tmp; } + + /* calculate ratio of the intersection of edge and clipping boundary */ + int ratio = intersect_ratio(clip_value(p1), clip_value(p2), clip); + + /* calculate y value at intersection point */ + POINT result; + *(Point_base *)&result = Point_base(p1.x() + ((ratio*(p2.x() - p1.x()))>>16), clip); + + /* calculate intersection values for edge attributes other than x */ + for (int i = 1; i < POINT::NUM_EDGE_ATTRIBUTES; i++) { + int v1 = p1.edge_attr(i), + v2 = p2.edge_attr(i); + result.edge_attr(i, v1 + ((ratio*(v2 - v1))>>16)); + } + return result; + } +}; + + +/** + * Support for clipping against a lower boundary + */ +struct Polygon::Clipper_min +{ + static bool inside(int value, int boundary) { return value >= boundary; } +}; + + +/** + * Support for clipping against a higher boundary + */ +struct Polygon::Clipper_max +{ + static bool inside(int value, int boundary) { return value <= boundary; } +}; + + +/** + * One-dimensional clipping + * + * This template allows for the aggregation of the policies defined above to + * build specialized 1D-clipping functions for upper/lower vertical/horizontal + * clipping boundaries and for polygon points with different attributes. + */ +template +struct Polygon::Clipper : CLIPPER_DIRECTION, CLIPPER_MINMAX +{ + /** + * Check whether point is inside the clipping area or not + */ + static bool inside(POINT p, int clip) + { + return CLIPPER_MINMAX::inside(CLIPPER_DIRECTION::clip_value(p), clip); + } +}; + + +/** + * Create clipping helpers + * + * This class is used as a compound containing all rules to + * clip a polygon against a 2d region such as the clipping + * region of a Canvas. + */ +template +struct Polygon::Clipper_2d +{ + typedef Clipper, Clipper_min, POINT> Top; + typedef Clipper, Clipper_max, POINT> Bottom; + typedef Clipper, Clipper_min, POINT> Left; + typedef Clipper, Clipper_max, POINT> Right; +}; + +#endif /* _INCLUDE__POLYGON_GFX__CLIPPING_H_ */ diff --git a/repos/gems/include/polygon_gfx/interpolate_rgb565.h b/repos/gems/include/polygon_gfx/interpolate_rgb565.h new file mode 100644 index 000000000..6b64d471d --- /dev/null +++ b/repos/gems/include/polygon_gfx/interpolate_rgb565.h @@ -0,0 +1,76 @@ +/* + * \brief RGB565-optimized interpolation functions for polygon painting + * \date 2015-06-19 + * \author Norman Feske + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__INTERPOLATE_RGB565_H_ +#define _INCLUDE__POLYGON_GFX__INTERPOLATE_RGB565_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Polygon { + + using Genode::Pixel_rgb565; + + template <> + inline void interpolate_rgba(Color, Color, Pixel_rgb565 *, + unsigned char *, unsigned, int, int); +} + + +/** + * Specialization that employs dithering + */ +template <> +inline void Polygon::interpolate_rgba(Color start, Color end, Pixel_rgb565 *dst, + unsigned char *dst_alpha, + unsigned num_values, int x, int y) +{ + /* sanity check */ + if (num_values <= 0) return; + + /* use 16.16 fixpoint values for the calculation */ + int r_ascent = ((end.r - start.r)<<16) / (int)num_values, + g_ascent = ((end.g - start.g)<<16) / (int)num_values, + b_ascent = ((end.b - start.b)<<16) / (int)num_values, + a_ascent = ((end.a - start.a)<<16) / (int)num_values; + + /* set start values for color components */ + int r = start.r<<16, + g = start.g<<16, + b = start.b<<16, + a = start.a<<16; + + for ( ; num_values--; dst++, dst_alpha++, x++) { + + int const dither_value = Genode::Dither_matrix::value(x, y) << 12; + + /* combine current color value with existing pixel via alpha blending */ + *dst = Pixel_rgb565::mix(*dst, + Pixel_rgb565((r + dither_value) >> 16, + (g + dither_value) >> 16, + (b + dither_value) >> 16), + (a + dither_value) >> 16); + + *dst_alpha += ((255 - *dst_alpha)*(a + dither_value)) >> (16 + 8); + + /* increment color-component values by ascent */ + r += r_ascent; + g += g_ascent; + b += b_ascent; + a += a_ascent; + } +} + +#endif /* _INCLUDE__POLYGON_GFX__INTERPOLATE_RGB565_H_ */ diff --git a/repos/gems/include/polygon_gfx/interpolate_rgba.h b/repos/gems/include/polygon_gfx/interpolate_rgba.h new file mode 100644 index 000000000..7801befe7 --- /dev/null +++ b/repos/gems/include/polygon_gfx/interpolate_rgba.h @@ -0,0 +1,70 @@ +/* + * \brief Interpolation functions for polygon painting + * \date 2015-06-19 + * \author Norman Feske + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__INTERPOLATE_RGBA_H_ +#define _INCLUDE__POLYGON_GFX__INTERPOLATE_RGBA_H_ + +/* Genode includes */ +#include + +namespace Polygon { + + using Genode::Color; + + template + static inline void interpolate_rgba(Color, Color, PT *, unsigned char *, + unsigned, int, int); +} + + +/** + * Interpolate color values + */ +template +static inline void Polygon::interpolate_rgba(Polygon::Color start, + Polygon::Color end, + PT *dst, + unsigned char *dst_alpha, + unsigned num_values, + int x, int y) +{ + /* sanity check */ + if (num_values == 0) return; + + /* use 16.16 fixpoint values for the calculation */ + int const r_ascent = ((end.r - start.r)<<16) / (int)num_values, + g_ascent = ((end.g - start.g)<<16) / (int)num_values, + b_ascent = ((end.b - start.b)<<16) / (int)num_values, + a_ascent = ((end.a - start.a)<<16) / (int)num_values; + + /* set start values for color components */ + int r = start.r<<16, + g = start.g<<16, + b = start.b<<16, + a = start.a<<16; + + for ( ; num_values--; dst++, dst_alpha++) { + + /* combine current color value with existing pixel via alpha blending */ + *dst = PT::mix(*dst, PT(r>>16, g>>16, b>>16), a>>16); + *dst_alpha += ((255 - *dst_alpha)*a) >> (16 + 8); + + /* increment color-component values by ascent */ + r += r_ascent; + g += g_ascent; + b += b_ascent; + a += a_ascent; + } +} + +#endif /* _INCLUDE__POLYGON_GFX__INTERPOLATE_RGBA_H_ */ diff --git a/repos/gems/include/polygon_gfx/polygon_painter_base.h b/repos/gems/include/polygon_gfx/polygon_painter_base.h new file mode 100644 index 000000000..bcb9df68f --- /dev/null +++ b/repos/gems/include/polygon_gfx/polygon_painter_base.h @@ -0,0 +1,259 @@ +/* + * \brief Common base of polygon painters + * \author Norman Feske + * \date 2015-06-19 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__POLYGON_PAINTER_BASE_H_ +#define _INCLUDE__POLYGON_GFX__POLYGON_PAINTER_BASE_H_ + +/* Genode includes */ +#include +#include +#include +#include + +namespace Polygon { class Painter_base; } + + +class Polygon::Painter_base +{ + private: + + /** + * Interpolate linearly between start value and end value + */ + static inline void _interpolate(int start, int end, int *dst, + unsigned num_values) + { + /* sanity check */ + if (num_values == 0) return; + + int const ascent = ((end - start)<<16)/(int)num_values; + + for (int curr = start<<16; num_values--; curr += ascent) + *dst++ = curr>>16; + } + + /** + * Clip polygon against boundary + * + * \param src Array of unclipped source-polygon points + * \param src_num_points Number of source-polygon points + * \param dst Destination buffer for clipped polygon points + * \param clip Clipping boundary + * \return Number of resulting polygon points + */ + template + static inline int _clip_1d(POINT const *src, unsigned src_num_points, + POINT *dst, int clip) + { + /* + * Walk along the polygon edges. Each time when crossing the + * clipping border, a new polygon point is created at the + * intersection point. All polygon points outside the clipping area + * are discarded. + */ + unsigned dst_num_points = 0; + for (unsigned i = 0; i < src_num_points; i++) { + + POINT curr = *src++; + POINT next = *src; + + bool curr_inside = CLIPPER::inside(curr, clip); + bool next_inside = CLIPPER::inside(next, clip); + + /* add current point to resulting polygon if inside clipping area */ + if (curr_inside) + dst[dst_num_points++] = curr; + + /* add point of intersection when walking outside of the clipping area */ + if (curr_inside && !next_inside) + dst[dst_num_points++] = CLIPPER::clip(curr, next, clip); + + /* add point of intersection when walking inside to the clipping area */ + if (!curr_inside && next_inside) + dst[dst_num_points++] = CLIPPER::clip(curr, next, clip); + } + + /* store coordinates of the first point also at the end of the polygon */ + dst[dst_num_points] = dst[0]; + + return dst_num_points; + } + + public: + + typedef Genode::Rect<> Rect; + typedef Genode::Area<> Area; + + /** + * Buffers used for storing interpolated attribute values along a left + * or right polygon edges. + * + * The edge buffers are partitioned into subsequent sub buffers with a + * size corresponding to the maximum y range of the polygon, which is + * the surface height. The different sub buffers are used to hold the + * interpolated edge values for the different polygon-point attributes. + * + * \param N number of attributes + */ + template + class Edge_buffers + { + private: + + Genode::Allocator &_alloc; + + unsigned const _edge_len; + + Genode::size_t _edges_size() { return N*2*_edge_len*sizeof(int); } + + int * const _edges = (int *)_alloc.alloc(_edges_size()); + + public: + + Edge_buffers(Genode::Allocator &alloc, unsigned edge_len) + : + _alloc(alloc), _edge_len(edge_len) + { } + + ~Edge_buffers() { _alloc.free(_edges, _edges_size()); } + + /** + * Return size of a single edge buffer + */ + unsigned edge_len() const { return _edge_len; } + + /** + * Return left edge buffer for Nth attribute + */ + int *left(unsigned n) { return _edges + n*2*_edge_len; } + + /** + * Return right edge buffer for Nth attribute + */ + int *right(unsigned n) { return _edges + (n*2 + 1)*_edge_len; } + }; + + /** + * Calculate maximum number of points needed for clipped polygon + * + * When clipping the polygon with 'num_points' points against + * the canvas boundaries, the resulting polygon will have a + * maximum of 'num_points' + 4 points - in the worst case one + * additional point per canvas edge. To ease the further + * processing, we add the coordinates of the first point as an + * additional point to the clipped polygon. + */ + static unsigned max_points_clipped(unsigned num_points) + { + return num_points + 4 + 1; + } + + /** + * Clip polygon against clipping rectangle + * + * \param src_points Array of unclipped polygon points + * \param num_points Number of unclipped polygon points + * \param dst_points Buffer for storing the clipped polygon + * \return Number of points of resulting polygon + * + * During the clipping computation, the destination buffer 'dst_points' + * is used to store two polygons. Therefore, the buffer must be + * dimensioned at 2*'max_points_clipped()'. The end result of the + * computation is stored at the beginning of 'dst_points'. + */ + template + static int clip_polygon(POINT const *src_points, unsigned num_points, + POINT *dst_points, Rect clip) + { + POINT *c0 = dst_points, + *c1 = dst_points + max_points_clipped(num_points); + + for (unsigned i = 0; i < num_points; i++) + c0[i] = src_points[i]; + + /* last point is connected to the first point */ + c0[num_points] = c0[0]; + + /* clip against top, left, bottom, and right clipping boundaries */ + typedef Clipper_2d Clipper; + num_points = _clip_1d (c0, num_points, c1, clip.y1()); + num_points = _clip_1d (c1, num_points, c0, clip.x1()); + num_points = _clip_1d(c0, num_points, c1, clip.y2()); + num_points = _clip_1d (c1, num_points, c0, clip.x2()); + + return num_points; + } + + + /** + * Determine bounding box of the specified polygon points + * + * \height maximum bounds + */ + template + static Rect bounding_box(POINT const points[], int num_points, Area area) + { + int x_min = area.w() - 1, x_max = 0; + int y_min = area.h() - 1, y_max = 0; + + for (int i = 0; i < num_points; i++) { + x_min = Genode::min(x_min, points[i].x()); + x_max = Genode::max(x_max, points[i].x()); + y_min = Genode::min(y_min, points[i].y()); + y_max = Genode::max(y_max, points[i].y()); + } + + return Rect(Point_base(x_min, y_min), Point_base(x_max, y_max)); + } + + + /** + * Calculate edge buffers for a polygon + * + * \param N number of edge attributes + */ + template + void fill_edge_buffers(Edge_buffers &edges, + POINT points[], unsigned num_points) + { + /* for each edge attribute */ + for (unsigned i = 0; i < N; i++) { + + int * const l_edge = edges.left(i); + int * const r_edge = edges.right(i); + + for (unsigned j = 0; j < num_points; j++) { + + POINT const p1 = points[j]; + POINT const p2 = points[j + 1]; + + /* request attribute values to interpolate */ + int const p1_attr = p1.edge_attr(i); + int const p2_attr = p2.edge_attr(i); + + /* horizontal edge */ + if (p1.y() == p2.y()); + + /* right edge */ + else if (p1.y() < p2.y()) + _interpolate(p1_attr, p2_attr, r_edge + p1.y(), p2.y() - p1.y()); + + /* left edge */ + else + _interpolate(p2_attr, p1_attr, l_edge + p2.y(), p1.y() - p2.y()); + } + } + } +}; + +#endif /* _INCLUDE__POLYGON_GFX__POLYGON_PAINTER_BASE_H_ */ diff --git a/repos/gems/include/polygon_gfx/shaded_polygon_painter.h b/repos/gems/include/polygon_gfx/shaded_polygon_painter.h new file mode 100644 index 000000000..5aea747f8 --- /dev/null +++ b/repos/gems/include/polygon_gfx/shaded_polygon_painter.h @@ -0,0 +1,145 @@ +/* + * \brief Functor for painting shaded polygons + * \author Norman Feske + * \date 2015-06-19 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__SHADED_POLYGON_PAINTER_H_ +#define _INCLUDE__POLYGON_GFX__SHADED_POLYGON_PAINTER_H_ + +#include +#include +#include + +namespace Polygon { class Shaded_painter; } + + +class Polygon::Shaded_painter : public Polygon::Painter_base +{ + private: + + /** + * Edge attribute IDs + */ + enum Edge_attr { ATTR_X, ATTR_R, ATTR_G, ATTR_B, ATTR_A, NUM_ATTR }; + + Edge_buffers _edges; + + public: + + /** + * Polygon point used for RGBA-shaded polygons + */ + struct Point : Point_base + { + Color color; + + Point() { } + Point(int x, int y, Color color) : Point_base(x, y), color(color) { } + + enum { NUM_EDGE_ATTRIBUTES = NUM_ATTR }; + + inline int edge_attr(int id) const + { + switch (id) { + default: + case ATTR_X: return Point_base::edge_attr(id); + case ATTR_R: return color.r; + case ATTR_G: return color.g; + case ATTR_B: return color.b; + case ATTR_A: return color.a; + } + } + + inline void edge_attr(int id, int value) + { + switch (id) { + case ATTR_X: Point_base::edge_attr(id, value); return; + case ATTR_R: color.r = value; return; + case ATTR_G: color.g = value; return; + case ATTR_B: color.b = value; return; + case ATTR_A: color.a = value; return; + } + } + }; + + /** + * Constructor + * + * \param alloc allocator used for allocating edge buffers + * \param max_height maximum size of polygon to draw, used to dimension + * the edge buffers + */ + Shaded_painter(Genode::Allocator &alloc, unsigned max_height) + : + _edges(alloc, max_height) + { } + + /** + * Draw polygon with linearly interpolated color + * + * \param points Array of polygon points + * \param num_points Number of polygon points + * + * The pixel surface and the alpha surface must have the same + * dimensions. + */ + template + void paint(Genode::Surface &pixel_surface, + Genode::Surface &alpha_surface, + Point const points[], unsigned num_points) + { + Point clipped[2*max_points_clipped(num_points)]; + num_points = clip_polygon(points, num_points, clipped, + pixel_surface.clip()); + + Rect const bbox = bounding_box(clipped, num_points, pixel_surface.size()); + + fill_edge_buffers(_edges, clipped, num_points); + + int * const x_l_edge = _edges.left (ATTR_X); + int * const x_r_edge = _edges.right(ATTR_X); + int * const r_l_edge = _edges.left (ATTR_R); + int * const r_r_edge = _edges.right(ATTR_R); + int * const g_l_edge = _edges.left (ATTR_G); + int * const g_r_edge = _edges.right(ATTR_G); + int * const b_l_edge = _edges.left (ATTR_B); + int * const b_r_edge = _edges.right(ATTR_B); + int * const a_l_edge = _edges.left (ATTR_A); + int * const a_r_edge = _edges.right(ATTR_A); + + /* calculate begin of first destination scanline */ + unsigned const dst_w = pixel_surface.size().w(); + PT *dst_pixel = pixel_surface.addr() + dst_w*bbox.y1(); + AT *dst_alpha = alpha_surface.addr() + dst_w*bbox.y1(); + + for (int y = bbox.y1(); y < bbox.y2(); y++) { + + /* read left and right color values from corresponding edge buffers */ + Color l_color = Color(r_l_edge[y], g_l_edge[y], b_l_edge[y], a_l_edge[y]); + Color r_color = Color(r_r_edge[y], g_r_edge[y], b_r_edge[y], a_r_edge[y]); + + int const x_l = x_l_edge[y]; + int const x_r = x_r_edge[y]; + + if (x_l < x_r) + interpolate_rgba(l_color, r_color, dst_pixel + x_l, + (unsigned char *)dst_alpha + x_l, + x_r - x_l, x_l, y); + + dst_pixel += dst_w; + dst_alpha += dst_w; + } + + pixel_surface.flush_pixels(bbox); + } +}; + +#endif /* _INCLUDE__POLYGON_GFX__SHADED_POLYGON_PAINTER_H_ */ diff --git a/repos/gems/include/polygon_gfx/textured_polygon_painter.h b/repos/gems/include/polygon_gfx/textured_polygon_painter.h new file mode 100644 index 000000000..ba7fefc8c --- /dev/null +++ b/repos/gems/include/polygon_gfx/textured_polygon_painter.h @@ -0,0 +1,146 @@ +/* + * \brief Functor for painting textured polygons + * \author Norman Feske + * \date 2015-06-29 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__TEXTURED_POLYGON_PAINTER_H_ +#define _INCLUDE__POLYGON_GFX__TEXTURED_POLYGON_PAINTER_H_ + +#include +#include +#include +#include + +namespace Polygon { class Textured_painter; } + + +class Polygon::Textured_painter : public Polygon::Painter_base +{ + private: + + /** + * Edge attribute IDs + */ + enum Edge_attr { ATTR_X, ATTR_U, ATTR_V, NUM_ATTR }; + + Edge_buffers _edges; + + public: + + /** + * Polygon point used for textured polygons + */ + struct Point : Point_base + { + int u = 0, v = 0; + + Point() { } + Point(int x, int y, int u, int v) : Point_base(x, y), u(u), v(v) { } + + enum { NUM_EDGE_ATTRIBUTES = NUM_ATTR }; + + inline int edge_attr(int id) const + { + switch (id) { + default: + case ATTR_X: return Point_base::edge_attr(id); + case ATTR_U: return u; + case ATTR_V: return v; + } + } + + inline void edge_attr(int id, int value) + { + switch (id) { + case ATTR_X: Point_base::edge_attr(id, value); return; + case ATTR_U: u = value; return; + case ATTR_V: v = value; return; + } + } + }; + + /** + * Constructor + * + * \param alloc allocator used for allocating edge buffers + * \param max_height maximum size of polygon to draw, used to dimension + * the edge buffers + */ + Textured_painter(Genode::Allocator &alloc, unsigned max_height) + : + _edges(alloc, max_height) + { } + + /** + * Draw textured polygon + * + * \param points Array of polygon points + * \param num_points Number of polygon points + * + * The pixel surface and the alpha surface must have the same + * dimensions. + */ + template + void paint(Genode::Surface &pixel_surface, + Genode::Surface &alpha_surface, + Point const points[], unsigned num_points, + Genode::Texture const &texture) + { + Point clipped[2*max_points_clipped(num_points)]; + num_points = clip_polygon(points, num_points, clipped, + pixel_surface.clip()); + + Rect const bbox = bounding_box(clipped, num_points, pixel_surface.size()); + + fill_edge_buffers(_edges, clipped, num_points); + + int * const x_l_edge = _edges.left (ATTR_X); + int * const x_r_edge = _edges.right(ATTR_X); + int * const u_l_edge = _edges.left (ATTR_U); + int * const u_r_edge = _edges.right(ATTR_U); + int * const v_l_edge = _edges.left (ATTR_V); + int * const v_r_edge = _edges.right(ATTR_V); + + unsigned const src_w = texture.size().w(); + PT const *src_pixel = texture.pixel(); + unsigned char const *src_alpha = texture.alpha(); + + /* calculate begin of destination scanline */ + unsigned const dst_w = pixel_surface.size().w(); + PT *dst_pixel = pixel_surface.addr() + dst_w*bbox.y1(); + AT *dst_alpha = alpha_surface.addr() + dst_w*bbox.y1(); + + for (int y = bbox.y1(); y < bbox.y2(); y++) { + + /* + * Read left and right texture coordinates (u,v) from + * corresponding edge buffers. + */ + Genode::Point<> const l_texpos(u_l_edge[y], v_l_edge[y]); + Genode::Point<> const r_texpos(u_r_edge[y], v_r_edge[y]); + + int const x_l = x_l_edge[y]; + int const x_r = x_r_edge[y]; + + if (x_l < x_r) + texturize_rgba(l_texpos, r_texpos, + dst_pixel + x_l, (unsigned char *)dst_alpha + x_l, + x_r - x_l, src_pixel, src_alpha, src_w); + + dst_pixel += dst_w; + dst_alpha += dst_w; + } + + pixel_surface.flush_pixels(bbox); + } +}; + +#endif /* _INCLUDE__POLYGON_GFX__TEXTURED_POLYGON_PAINTER_H_ */ diff --git a/repos/gems/include/polygon_gfx/texturize_rgba.h b/repos/gems/include/polygon_gfx/texturize_rgba.h new file mode 100644 index 000000000..e89f9e533 --- /dev/null +++ b/repos/gems/include/polygon_gfx/texturize_rgba.h @@ -0,0 +1,69 @@ +/* + * \brief Texturizing function for polygon painting + * \date 2015-06-29 + * \author Norman Feske + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__POLYGON_GFX__TEXTURIZE_RGBA_H_ +#define _INCLUDE__POLYGON_GFX__TEXTURIZE_RGBA_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Polygon { + + template + static inline void texturize_rgba(Genode::Point<>, Genode::Point<>, PT *, + unsigned char *, unsigned, PT const *, + unsigned char const *, unsigned); +} + + +/** + * Texturize scanline + */ +template +static inline void Polygon::texturize_rgba(Genode::Point<> start, Genode::Point<> end, + PT *dst, unsigned char *dst_alpha, + unsigned num_values, + PT const *texture_base, + unsigned char const *alpha_base, + unsigned texture_width) +{ + /* sanity check */ + if (num_values <= 0) return; + + /* use 16.16 fixpoint values for the calculation */ + int tx_ascent = ((end.x() - start.x())<<16)/(int)num_values, + ty_ascent = ((end.y() - start.y())<<16)/(int)num_values; + + /* set start values for color components */ + int tx = start.x()<<16, + ty = start.y()<<16; + + for ( ; num_values--; dst++, dst_alpha++) { + + /* blend pixel from texture with destination point on surface */ + unsigned long src_offset = (ty>>16)*texture_width + (tx>>16); + + int const a = alpha_base[src_offset]; + + *dst = texture_base[src_offset]; + *dst_alpha += ((255 - *dst_alpha)*a) >> 8; + + /* walk through texture */ + tx += tx_ascent; + ty += ty_ascent; + } +} + +#endif /* _INCLUDE__POLYGON_GFX__TEXTURIZE_RGBA_H_ */ diff --git a/repos/gems/run/nano3d.run b/repos/gems/run/nano3d.run new file mode 100644 index 000000000..ef4fcd47c --- /dev/null +++ b/repos/gems/run/nano3d.run @@ -0,0 +1,149 @@ +# +# Build +# +if {![have_spec linux]} { + puts "Runs on Linux only" + exit 0 +} + +set build_components { + core init + drivers/timer + server/nitpicker server/dynamic_rom app/nano3d + drivers/framebuffer + app/backdrop +} + +lappend_if [have_spec usb] build_components drivers/usb + +build $build_components + +create_boot_directory + +# +# Generate config +# + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# copy backdrop PNG images to bin directory +foreach file { genode_logo.png grid.png } { + file copy -force [genode_dir]/repos/gems/src/app/backdrop/$file bin/ } + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init + timer + nitpicker dynamic_rom nano3d + backdrop + ld.lib.so libpng.lib.so libc.lib.so libm.lib.so zlib.lib.so + genode_logo.png grid.png +} + +# platform-specific modules +lappend_if [have_spec linux] boot_modules fb_sdl + +build_boot_image $boot_modules + +run_genode_until forever diff --git a/repos/gems/src/app/nano3d/main.cc b/repos/gems/src/app/nano3d/main.cc new file mode 100644 index 000000000..bfc8fa0d7 --- /dev/null +++ b/repos/gems/src/app/nano3d/main.cc @@ -0,0 +1,235 @@ +/* + * \brief Animated cube + * \author Norman Feske + * \date 2015-06-26 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include + + +template +class Scene : public Nano3d::Scene +{ + public: + + enum Shape { SHAPE_DODECAHEDRON, SHAPE_CUBE }; + enum Painter { PAINTER_SHADED, PAINTER_TEXTURED }; + + private: + + Nitpicker::Area const _size; + + struct Radial_texture + { + enum { W = 128, H = 128 }; + + unsigned char alpha[H][W]; + PT pixel[H][W]; + Genode::Surface_base::Area size { W, H }; + Genode::Texture texture { &pixel[0][0], &alpha[0][0], size }; + + Radial_texture() + { + int const r_max = W/2 + 5; + + for (unsigned y = 0; y < H; y++) { + for (unsigned x = 0; x < W; x++) { + + int const dx = x - W/2; + int const dy = y - H/2; + + int const radius = Nano3d::sqrt(dx*dx + dy*dy); + + alpha[y][x] = 250 - (radius*250)/r_max; + + if ((x&4) ^ (y&4)) + alpha[y][x] = 0; + + int const r = (x*200)/W; + int const g = (y*200)/H; + int const b = (x*128)/W + (y*128)/H; + + pixel[y][x] = PT(r, g, b); + } + } + } + }; + + Radial_texture _texture; + + Shape _shape = SHAPE_DODECAHEDRON; + Painter _painter = PAINTER_TEXTURED; + + void _handle_config(unsigned) + { + Genode::config()->reload(); + + try { + _shape = SHAPE_DODECAHEDRON; + if (Genode::config()->xml_node().attribute("shape").has_value("cube")) + _shape = SHAPE_CUBE; + } catch (...) { } + + try { + _painter = PAINTER_TEXTURED; + if (Genode::config()->xml_node().attribute("painter").has_value("shaded")) + _painter = PAINTER_SHADED; + } catch (...) { } + } + + Genode::Signal_dispatcher _config_dispatcher; + + public: + + Scene(Genode::Signal_receiver &sig_rec, unsigned update_rate_ms, + Nitpicker::Point pos, Nitpicker::Area size) + : + Nano3d::Scene(sig_rec, update_rate_ms, pos, size), _size(size), + _config_dispatcher(sig_rec, *this, &Scene::_handle_config) + { + Genode::config()->sigh(_config_dispatcher); + _handle_config(0); + } + + private: + + Polygon::Shaded_painter _shaded_painter { + *Genode::env()->heap(), _size.h() }; + Polygon::Textured_painter _textured_painter { + *Genode::env()->heap(), _size.h() }; + + Nano3d::Cube_shape const _cube { 7000 }; + Nano3d::Dodecahedron_shape const _dodecahedron { 10000 }; + + template + void _render_shape(Genode::Surface &pixel, + Genode::Surface &alpha, + SHAPE const &shape, unsigned frame, + bool backward_facing) + { + typedef Genode::Color Color; + + auto vertices = shape.vertex_array(); + + vertices.rotate_x(frame*1); + vertices.rotate_y(frame*2); + vertices.rotate_z(frame*3); + vertices.project(1600, 800); + vertices.translate(200, 200, 0); + + if (_painter == PAINTER_TEXTURED) { + + typedef Polygon::Textured_painter::Point Textured_point; + + shape.for_each_face([&] (unsigned const vertex_indices[], + unsigned num_vertices) { + + Textured_point points[num_vertices]; + + int angle = -frame*4; + for (unsigned i = 0; i < num_vertices; i++) { + + Nano3d::Vertex const vertex = vertices[vertex_indices[i]]; + + Textured_point &point = + backward_facing ? points[num_vertices - 1 - i] + : points[i]; + + int const r = _texture.size.w()/2; + + int const u = r + (r*Nano3d::cos_frac16(angle) >> 16); + int const v = r + (r*Nano3d::sin_frac16(angle) >> 16); + + angle += Nano3d::Sincos_frac16::STEPS / num_vertices; + + point = Textured_point(vertex.x(), vertex.y(), u, v); + } + + _textured_painter.paint(pixel, alpha, points, num_vertices, + _texture.texture); + }); + } + + if (_painter == PAINTER_SHADED) { + + typedef Polygon::Shaded_painter::Point Shaded_point; + + shape.for_each_face([&] (unsigned const vertex_indices[], + unsigned num_vertices) { + + Shaded_point points[num_vertices]; + + for (unsigned i = 0; i < num_vertices; i++) { + + Nano3d::Vertex const v = vertices[vertex_indices[i]]; + + Shaded_point &point = + backward_facing ? points[num_vertices - 1 - i] + : points[i]; + + Color const color = + backward_facing ? Color(i*10, i*10, i*10, 230 - i*18) + : Color(240, 10*i, 0, 10 + i*35); + + point = Shaded_point(v.x(), v.y(), color); + } + + _shaded_painter.paint(pixel, alpha, + points, num_vertices); + }); + } + } + + public: + + /** + * Scene interface + */ + void render(Genode::Surface &pixel, + Genode::Surface &alpha) override + { + unsigned const frame = (this->elapsed_ms()/10) % 1024; + + if (_shape == SHAPE_DODECAHEDRON) { + + _render_shape(pixel, alpha, _dodecahedron, frame, true); + _render_shape(pixel, alpha, _dodecahedron, frame, false); + + } else if (_shape == SHAPE_CUBE) { + + _render_shape(pixel, alpha, _cube, frame, true); + _render_shape(pixel, alpha, _cube, frame, false); + } + } +}; + + +int main(int argc, char **argv) +{ + static Genode::Signal_receiver sig_rec; + + enum { UPDATE_RATE_MS = 20 }; + + static Scene + scene(sig_rec, UPDATE_RATE_MS, + Nitpicker::Point(-200, -200), Nitpicker::Area(400, 400)); + + scene.dispatch_signals_loop(sig_rec); + + return 0; +} diff --git a/repos/gems/src/app/nano3d/target.mk b/repos/gems/src/app/nano3d/target.mk new file mode 100644 index 000000000..2de4234f7 --- /dev/null +++ b/repos/gems/src/app/nano3d/target.mk @@ -0,0 +1,4 @@ +TARGET = nano3d +SRC_CC = main.cc +LIBS = base config +INC_DIR += $(PRG_DIR)