Polygon drawing and rudimentary 3D routines

This patch adds two new painters located at gems/include/polygon_gfx.
Both painters draw convex polygons with an arbirary number of points.
The shaded-polygon painter interpolates the color and alpha values
whereas the textured-polygon painter applies a texture to the polygon.
The painters are accompanied by simplistic 3D routines located at
gems/include/nano3d/ and a corresponding example (gems/run/nano3d.run).
This commit is contained in:
Norman Feske 2015-06-30 11:47:59 +02:00 committed by Christian Helmuth
parent c74a4fbbe2
commit 259b127f96
16 changed files with 2304 additions and 0 deletions

View File

@ -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 <nano3d/vertex_array.h>
namespace Nano3d { class Cube_shape; }
class Nano3d::Cube_shape
{
private:
enum { NUM_VERTICES = 8, NUM_FACES = 6 };
typedef Nano3d::Vertex_array<NUM_VERTICES> 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 <typename FN>
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_ */

View File

@ -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 <nano3d/vertex_array.h>
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 <typename... EDGE_INDICES>
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<NUM_VERTICES> 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 <typename FN>
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_ */

View File

@ -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 <timer_session/connection.h>
#include <nitpicker_session/connection.h>
#include <os/surface.h>
#include <os/pixel_alpha8.h>
#include <os/attached_dataspace.h>
#include <input/event.h>
namespace Nano3d {
struct Input_handler;
template <typename> class Scene;
}
struct Nano3d::Input_handler
{
virtual void handle_input(Input::Event const [], unsigned num_events) = 0;
};
template <typename PT>
class Nano3d::Scene
{
public:
class Unsupported_color_depth { };
typedef Genode::Pixel_alpha8 Pixel_alpha8;
virtual void render(Genode::Surface<PT> &pixel_surface,
Genode::Surface<Pixel_alpha8> &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<PT>() + i*size().count());
}
Pixel_alpha8 *alpha_base(unsigned i)
{
Pixel_alpha8 * const alpha_base =
(Pixel_alpha8 *)(ds.local_addr<PT>() + 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<PT> Pixel_surface;
typedef Genode::Surface<Genode::Pixel_alpha8> 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 <typename T>
void _clear(Genode::Surface<T> &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<Input::Event>();
if (_input_handler)
_input_handler->handle_input(ev_buf, num);
}
}
Genode::Signal_dispatcher<Scene> _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<Scene> _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<Command::Offset>(_view_handle, offset);
_nitpicker.execute();
_do_sync = false;
}
Genode::Signal_dispatcher<Scene> _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<Command::Geometry>(_view_handle, rect);
_nitpicker.enqueue<Command::To_front>(_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<Genode::Signal_dispatcher_base *>(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_ */

View File

@ -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_ */

View File

@ -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 <typename T>
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__ */

View File

@ -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 <nano3d/sincos_frac16.h>
namespace Nano3d {
template <typename> class Vec2;
template <typename> class Vec3;
typedef Vec3<int> Vertex;
template <unsigned> class Vertex_array;
}
template <typename T>
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 <typename T>
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 <unsigned MAX_VERTICES>
class Nano3d::Vertex_array
{
protected:
Vertex _buf[MAX_VERTICES];
void _rotate(int & (Vec3<int>::*x)(), int & (Vec3<int>::*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<int> 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_ */

View File

@ -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 <typename> struct Clipper_vertical;
template <typename> struct Clipper_horizontal;
struct Clipper_min;
struct Clipper_max;
template <typename, typename, typename> struct Clipper;
template <typename> 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 <typename POINT>
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 <typename POINT>
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 <typename CLIPPER_DIRECTION, typename CLIPPER_MINMAX, typename POINT>
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 <typename POINT>
struct Polygon::Clipper_2d
{
typedef Clipper<Clipper_horizontal<POINT>, Clipper_min, POINT> Top;
typedef Clipper<Clipper_horizontal<POINT>, Clipper_max, POINT> Bottom;
typedef Clipper<Clipper_vertical<POINT>, Clipper_min, POINT> Left;
typedef Clipper<Clipper_vertical<POINT>, Clipper_max, POINT> Right;
};
#endif /* _INCLUDE__POLYGON_GFX__CLIPPING_H_ */

View File

@ -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 <util/dither_matrix.h>
#include <os/pixel_rgb565.h>
#include <polygon_gfx/interpolate_rgba.h>
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_ */

View File

@ -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 <util/color.h>
namespace Polygon {
using Genode::Color;
template <typename PT>
static inline void interpolate_rgba(Color, Color, PT *, unsigned char *,
unsigned, int, int);
}
/**
* Interpolate color values
*/
template <typename PT>
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_ */

View File

@ -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 <base/allocator.h>
#include <util/geometry.h>
#include <util/misc_math.h>
#include <polygon_gfx/clipping.h>
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 <typename CLIPPER, typename POINT>
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 <unsigned N>
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 <typename POINT>
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<POINT> Clipper;
num_points = _clip_1d<typename Clipper::Top> (c0, num_points, c1, clip.y1());
num_points = _clip_1d<typename Clipper::Left> (c1, num_points, c0, clip.x1());
num_points = _clip_1d<typename Clipper::Bottom>(c0, num_points, c1, clip.y2());
num_points = _clip_1d<typename Clipper::Right> (c1, num_points, c0, clip.x2());
return num_points;
}
/**
* Determine bounding box of the specified polygon points
*
* \height maximum bounds
*/
template <typename POINT>
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 <unsigned N, typename POINT>
void fill_edge_buffers(Edge_buffers<N> &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_ */

View File

@ -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 <os/surface.h>
#include <polygon_gfx/polygon_painter_base.h>
#include <polygon_gfx/interpolate_rgba.h>
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<NUM_ATTR> _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 <typename PT, typename AT>
void paint(Genode::Surface<PT> &pixel_surface,
Genode::Surface<AT> &alpha_surface,
Point const points[], unsigned num_points)
{
Point clipped[2*max_points_clipped(num_points)];
num_points = clip_polygon<Point>(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_ */

View File

@ -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 <os/surface.h>
#include <os/texture.h>
#include <polygon_gfx/polygon_painter_base.h>
#include <polygon_gfx/texturize_rgba.h>
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<NUM_ATTR> _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 <typename PT, typename AT>
void paint(Genode::Surface<PT> &pixel_surface,
Genode::Surface<AT> &alpha_surface,
Point const points[], unsigned num_points,
Genode::Texture<PT> const &texture)
{
Point clipped[2*max_points_clipped(num_points)];
num_points = clip_polygon<Point>(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_ */

View File

@ -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 <util/color.h>
#include <os/pixel_rgba.h>
#include <util/geometry.h>
namespace Polygon {
template <typename PT>
static inline void texturize_rgba(Genode::Point<>, Genode::Point<>, PT *,
unsigned char *, unsigned, PT const *,
unsigned char const *, unsigned);
}
/**
* Texturize scanline
*/
template <typename PT>
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_ */

149
repos/gems/run/nano3d.run Normal file
View File

@ -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 {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="fb_sdl">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Input"/>
<service name="Framebuffer"/>
</provides>
</start>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="nitpicker">
<resource name="RAM" quantum="1M"/>
<provides><service name="Nitpicker"/></provides>
<config>
<domain name="" layer="2" />
<domain name="nano3d" layer="1" origin="pointer"/>
<policy label="nano3d" domain="nano3d"/>
<policy label="" domain=""/>
</config>
</start>
<start name="backdrop">
<resource name="RAM" quantum="4M"/>
<config>
<libc>
<vfs>
<rom name="genode_logo.png"/>
<rom name="grid.png"/>
</vfs>
</libc>
<fill color="#122334" />
<image png="grid.png" tiled="yes" alpha="200" />
<image png="genode_logo.png" anchor="bottom_right" alpha="150"/>
</config>
</start>
<start name="dynamic_rom">
<resource name="RAM" quantum="4M"/>
<provides><service name="ROM"/></provides>
<config verbose="yes">
<rom name="nano3d.config">
<inline description="initial state">
<config painter="textures"/>
</inline>
<sleep milliseconds="1000" />
<inline description="RGBA shading">
<config painter="shaded"/>
</inline>
<sleep milliseconds="1000" />
<inline description="switch to cube">
<config painter="shaded" shape="cube" />
</inline>
<sleep milliseconds="1000" />
<inline description="texturing">
<config painter="textured" shape="cube" />
</inline>
<sleep milliseconds="1000" />
</rom>
</config>
</start>
<start name="nano3d">
<resource name="RAM" quantum="8M"/>
<configfile name="nano3d.config"/>
<route>
<service name="ROM">
<if-arg key="label" value="nano3d.config" />
<child name="dynamic_rom" />
</service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</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

View File

@ -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 <os/config.h>
#include <polygon_gfx/shaded_polygon_painter.h>
#include <polygon_gfx/interpolate_rgb565.h>
#include <polygon_gfx/textured_polygon_painter.h>
#include <nano3d/dodecahedron_shape.h>
#include <nano3d/cube_shape.h>
#include <nano3d/scene.h>
#include <nano3d/sqrt.h>
template <typename PT>
class Scene : public Nano3d::Scene<PT>
{
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<PT> 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<Scene> _config_dispatcher;
public:
Scene(Genode::Signal_receiver &sig_rec, unsigned update_rate_ms,
Nitpicker::Point pos, Nitpicker::Area size)
:
Nano3d::Scene<PT>(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 <typename SHAPE>
void _render_shape(Genode::Surface<PT> &pixel,
Genode::Surface<Genode::Pixel_alpha8> &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<PT> &pixel,
Genode::Surface<Genode::Pixel_alpha8> &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<Genode::Pixel_rgb565>
scene(sig_rec, UPDATE_RATE_MS,
Nitpicker::Point(-200, -200), Nitpicker::Area(400, 400));
scene.dispatch_signals_loop(sig_rec);
return 0;
}

View File

@ -0,0 +1,4 @@
TARGET = nano3d
SRC_CC = main.cc
LIBS = base config
INC_DIR += $(PRG_DIR)