User:Michelinux/3DGlasses

From WiiBrew
Jump to navigation Jump to search
3DGlasses
3DGlassesLogo.png
General
Author(s)Michelinux
TypeLibrary

Description

3DGlasses is a C++ library to develop applications that can be seen wearing 3D glasses.

This is not a full featured 3D library: it's your responsability to draw objects in correct z-order.

Development status

  • Reading/writing configuration file works
  • Geometry init works
  • Simple shapes are drawn correctly

Supported glasses

Different kind of 3D glasses exists, this library supports the old ones with each lens of a different color; these glasses are called anaglyphs. Most common anaglyphs have a red lens for left eye, and a cyan lens for the right one, also exists glasses with green - magenta and very rare ones with yellow - blue lenses; 3DGlasses library supports all of them.

Geometry

Origin of the axis is set at the center of the screen, with x incresing towards right, y increasing towards botton and z increasing towards "internal" of screen. For negative values of z the object appears outside the tv, inside the screen for positive values.

For z = 0, x goes from -320 to 320; perspective makes the range narrower for z < 0 and wider for z > 0. Y scale may change because the library tryes to keep 1:1 aspect ratio between axis.

Configuration file

The configuration file 3DGlasses.cfg, inside the main directory of your SD, is shared among all applications using this library. This file contains geometry info about your screen and the kind of anaglyphs you wear.

Probably this file is created the first time you run an application using 3DGlasses. If you change tv or glasses just delete this file (it will be recreated) or edit it by hand (instructions are inside the file itself).

Use of colors

Since the two eyes see different colors of the image you must avoid pure colors in your applications: if you draw a monochromatic red object it will be seen only by left eye with no 3D effect at all.

Classes

3DGlasses library defines its own namespace: l3dg with the following classes

l3dg::Glasses

It's the main class of the library; you need to instanziate one (probably no more than one) object of this class.

This class incapsulates a GRRLIB_texImg where the drawing of the 3D scene happens. startDrawing() method clears internal image while doneDrawing() puts it on the screen.

You can provide a callback function to the constructor of this class; if configuration file is not found, che callback is called to ask user some parameters. If configuration file is found, callback is not called.

Glasses class objects have the quality attribute which can be FAST or ACCURATE (the default). Many objects use different rendering algorithms according the value of this attribute.

That's the public part of the class:

class Glasses {
public:
 Glasses(void callback(glassesTypes& , unsigned int&, bool&, float&, unsigned int&)=NULL);
 Glasses(const Glasses &g);
 ~Glasses();
 u32 getLeftColor() const;
 u32 getRightColor() const;
 void splitColor(u32 color, u32 &left, u32 &right) const;
 qualities getQuality() const;
 void setQuality(qualities quality);
 visibility getPerspectives(const Vector3D &point, Vector2D &left, Vector2D &right) const;
 float getMinX(float z, bool overscanSafe) const;
 float getMaxX(float z, bool overscanSafe) const;
 float getMinY(float z, bool overscanSafe) const;
 float getMaxY(float z, bool overscanSafe) const;
 float getMinZ(float x, float y, bool overscanSafe) const;
 void startDrawing();
 void doneDrawing() const;
 void drawDot(int x, int y, eyes eye, u32 color);
 void drawDot(int x, int y, eyes eye, u32 color, u32 opaqueness, u32 transparency);
 void drawDot(int x, int y, eyes eye, u32 color, u32 textureColor);
 void dualDotDraw(int x, int y, u32 color, u32 leftTextureColor, u32 rightTextureColor);
 void drawLine(const Vector2D &begin, const Vector2D &end, eyes eye, u32 color);
 Glasses &operator+=(const Glasses &g);
 Glasses operator+(const Glasses &g) const;
private:
 //...
};

Sample code

The user can select parameter using up/down buttons of wiimote, adjust values with left/right buttons. A rectangle is shown to adjust xYRatioCorrection: make it a square to set proper value. Press button A when finished.

#include <wiiuse/wpad.h>
#include <3DGlasses.h>
#include "BMfont4.h"

#define GRAY 0x808080FF
#define WHITE 0xFFFFFFFF

GRRLIB_texImg *tex_BMfont4;

void callback(l3dg::glassesTypes &glassesType, unsigned int &screenSize, bool &isWidescreen, float &xYRatioCorrection, unsigned int &distanceEyesScreen) {
 guVector v[5];
 v[0].x= 280; v[0].y= 320; v[0].z= 0;
 v[1].x= 370; v[1].y= 320; v[1].z= 0;
 v[2].x= 370; v[2].y= 320 + 100 * xYRatioCorrection; v[2].z= 0;
 v[3].x= 280; v[3].y= v[2].y; v[3].z= 0;
 v[4].x= 280; v[4].y= 320; v[4].z= 0;
 u32 textColors[4];
 u32 quadColors[5];
 u32 pressed;
 int type= glassesType;
 int i;
 int hilighted= 0;
 do {
   for (i= 0; i < 4; i++) {
     if (i == hilighted) {
       textColors[i]= WHITE;
     } else {
       textColors[i]= GRAY;
     }
   }
   for (i= 0; i < 5; i++) {
     if (hilighted == 4) {
       quadColors[i]= WHITE;
     } else {
       quadColors[i]= GRAY;
     }
   }
   WPAD_ScanPads();
   pressed= WPAD_ButtonsDown(0);
   switch (hilighted) {
     case 0:
       if (pressed & WPAD_BUTTON_RIGHT) type++;
       else if (pressed & WPAD_BUTTON_LEFT) type--;
       if (type < 0) type= l3dg::glassesTypesNumber - 1;
       else if (type >= l3dg::glassesTypesNumber) type= 0;
     break;
     case 1:
       if (pressed & WPAD_BUTTON_RIGHT) screenSize++;
       else if (pressed & WPAD_BUTTON_LEFT) screenSize--;
     break;
     case 2:
       if (pressed & (WPAD_BUTTON_LEFT | WPAD_BUTTON_RIGHT)) isWidescreen= !isWidescreen;
     break;
     case 3:
       if (pressed & WPAD_BUTTON_RIGHT) distanceEyesScreen++;
       else if (pressed & WPAD_BUTTON_LEFT) distanceEyesScreen--;
     break;
     case 4:
       if (pressed & WPAD_BUTTON_RIGHT) xYRatioCorrection+= 0.05;
       else if (pressed & WPAD_BUTTON_LEFT) xYRatioCorrection-= 0.05;
       v[2].y= 320 + 100 * xYRatioCorrection;
       v[3].y= v[2].y;
     break;
   }
   if (pressed & (WPAD_BUTTON_LEFT | WPAD_BUTTON_RIGHT)) {
   }
   if (pressed &  WPAD_BUTTON_DOWN) {
     hilighted++;
     if (hilighted > 4) hilighted= 0;
   }
   if (pressed &  WPAD_BUTTON_UP) {
     hilighted--;
     if (hilighted < 0) hilighted= 4;
   }
   GRRLIB_FillScreen(0x00000000);
   GRRLIB_Printf(64, 64, tex_BMfont4, textColors[0], 1, "GLASSES TYPE: %s", l3dg::glassesTypesStrings[type]);
   GRRLIB_Printf(64, 128, tex_BMfont4, textColors[1], 1, "SCREEN SIZE: %d\"", screenSize);
   if (isWidescreen) {
     GRRLIB_Printf(64, 192, tex_BMfont4, textColors[2], 1, "16:9 WIDESCREEN");
   } else {
     GRRLIB_Printf(64, 192, tex_BMfont4, textColors[2], 1, "4:3");
   }
   GRRLIB_Printf(64, 256, tex_BMfont4, textColors[3], 1, "DISTANCE EYES-SCREEN: %d CM", distanceEyesScreen);
   GRRLIB_NGone(v, quadColors, 5);
   GRRLIB_Render();
 } while (!(pressed & WPAD_BUTTON_A));
 glassesType= (l3dg::glassesTypes) type;
}

int main(int argc, char *argv[]) {
 GRRLIB_Init();
 tex_BMfont4= GRRLIB_LoadTexture(BMfont4);
 GRRLIB_InitTileSet(tex_BMfont4, 16, 16, 32);
 WPAD_Init();
 l3dg::Glasses glasses= l3dg::Glasses(callback);
 u32 pressed= WPAD_ButtonsDown(0);
 while (!(pressed & WPAD_BUTTON_HOME)) {
   GRRLIB_FillScreen(0x00000000);
   // draw your own 2D background here using GRRLIB
   glasses.startDrawing();
   // draw 3D scene here using 3DGlasses library
   glasses.doneDrawing();
   // draw your own 2D foreground here using GRRLIB
   GRRLIB_Render();
   WPAD_ScanPads();
   pressed= WPAD_ButtonsDown(0);
 }
 GRRLIB_Exit();
}

l3dg::DrawableObject

This abstract class is the ancestor of all the objects you can draw with 3DGlasses library.

The constructor needs a reference to a Glasses class object to learn about geometry.

Every object has its own visible attribute: visibility is an enum wich can be NOT_VISIBLE, PERHAPS_VISIBLE or VISIBLE. PERHAPS_VISIBLE is used when the object is near the border of the screen (overscan unsafe area) or when its visibility is hard to compute.

class DrawableObject {
public:
 DrawableObject(Glasses &glasses, const Vector3D &position, u32 color);
 DrawableObject(const DrawableObject &d);
 virtual void setPosition(const Vector3D &position);
 const Vector3D &getPosition() const;
 void setColor(u32 color);
 u32 getColor() const;
 visibility getVisibility() const;
 virtual void draw() const =0;
protected:
 Glasses *glasses;
 Vector3D position;
 u32 color;
 visibility visible;
};

l3dg::Point

A Point is the simplest object you can draw: it is projected to two screen points of different color, each one seen by only one eye.

class Point : public DrawableObject {
public:
 Point(Glasses &glasses, const Vector3D &position, u32 color);
 Point(const Point &p);
 void setPosition(const Vector3D &position);
 void draw() const;
private:
 //...
};

Sample code

Draw a single point, half way between screen and observer.

l3dg::Glasses glasses= l3dg::Glasses(callback);
l3dg::Vector3D position= l3dg::Vector3D(0, 0, glasses.getMinZ(0, 0, true) / 2);
l3dg::Point point= l3dg::Point(glasses, position, WHITE);
GRRLIB_FillScreen(0x00000000);
glasses.startDrawing();
point.draw();
glasses.doneDrawing();
GRRLIB_Render();

l3dg::Line

A line is described by an array of two endpoints: the first endpoint is considered the position of the object, the other one is the end.

class Line : public DrawableObject {
public:
 Line(Glasses &glasses, const Vector3D points[2], u32 color);
 Line(const Line &l);
 void setPosition(const Vector3D &position);
 Vector3D getEnd() const;
 void draw() const;
private:
 //...
};

Sample code

Draw two lines: the first one with antialiasing, the second one without it.

l3dg::Glasses glasses= l3dg::Glasses(callback);
l3dg::Vector3D endpoints[2];
endpoints[0]= l3dg::Vector3D(-100, -50, glasses.getMinZ(-100, -50, true) / 2);
endpoints[1]= -endpoints[0];
l3dg::Line line1= l3dg::Line(glasses, endpoints, WHITE);
endpoints[0].x = -endpoints[0].x;
endpoints[1].x = -endpoints[1].x;
l3dg::Line line2= l3dg::Line(glasses, endpoints, WHITE);
GRRLIB_FillScreen(0x00000000);
glasses.startDrawing();
glasses.setQuality(l3dg::ACCURATE);
line1.draw();
glasses.setQuality(l3dg::FAST);
line2.draw();
glasses.doneDrawing();
GRRLIB_Render();

l3dg::PolyLine

A PolyLine is a sequence of lines, each one (except the first line) beginning at the end of the previous one.

The polyline is defined by an array of n + 1 points; where n is the number of lines.

You can create a closed polyline (such as a polygon) making the last point egual to the first one.

The first point is the position of the polyline.

class PolyLine : public DrawableObject {
public:
 PolyLine(Glasses &glasses, unsigned int pointsNumber, const Vector3D points[], u32 color);
 PolyLine(const PolyLine &pl);
 ~PolyLine();
 void setPosition(const Vector3D &position);
 unsigned int getPointsNumber() const;
 const Vector3D &getPoint(unsigned int pointNumber) const;
 void draw() const;
protected:
//...
};

l3dg::Ellipse

An ellipse can be approssimated by a polygon with a convenient number of edges.

An Ellipse class object can be defined by the center and the two semiaxes; the semiaxes determinates both size and orientation of the ellipse.

The actual number of edges of an Ellipse class object depends upon the quality attribute of the glasses object

class Ellipse : public PolyLine {
public:
 Ellipse(Glasses &glasses, const Vector3D &center, const Vector3D semiAxes[2], u32 color);
 Ellipse(const Ellipse &e);
private:
//...
};

l3dg::FilledPolygon

FilledPolygon class objects are defined by the vertices of the polygon (the first one is the position).

Used algorithm can fill any kind of convex or concave polygon.

class FilledPolygon : public DrawableObject {
public:
 FilledPolygon(Glasses &glasses, unsigned int pointsNumber, const Vector3D points[], u32 color);
 FilledPolygon(const FilledPolygon &fp);
 ~FilledPolygon();
 void setPosition(const Vector3D &position);
 void draw() const;
protected:
//...
};

Sample code

Draw a filled five-pointed star out of the screen

l3dg::Glasses glasses= l3dg::Glasses(callback);
l3dg::Vector3D vertex[10];
vertex[0].x= 0; vertex[0].y= glasses.getMinY(-300, true); vertex[0].z= -300;
vertex[1].x= 0.225 * vertex[0].y; vertex[1].y= 0.309 * vertex[0].y; vertex[1].z= -300;
vertex[2].x= 0.951 * vertex[0].y; vertex[2].y= vertex[1].y; vertex[2].z= -300;
vertex[3].x= 0.363 * vertex[0].y; vertex[3].y= -0.118 * vertex[0].y; vertex[3].z= -300;
vertex[4].x= 0.588 * vertex[0].y; vertex[4].y= -0.809 * vertex[0].y; vertex[4].z= -300;
vertex[5].x= 0; vertex[5].y= -0.382 * vertex[0].y; vertex[5].z= -300;
vertex[6].x= -vertex[4].x; vertex[6].y= vertex[4].y; vertex[6].z= -300;
vertex[7].x= -vertex[3].x; vertex[7].y= vertex[3].y; vertex[7].z= -300;
vertex[8].x= -vertex[2].x; vertex[8].y= vertex[2].y; vertex[8].z= -300;
vertex[9].x= -vertex[1].x; vertex[9].y= vertex[1].y; vertex[9].z= -300;
l3dg::FilledPolygon star= l3dg::FilledPolygon(glasses, 10, vertex, WHITE);
GRRLIB_FillScreen(0x00000000);
glasses.startDrawing();
star.draw();
glasses.doneDrawing();
GRRLIB_Render();

l3dg::FilledEllipse

This subclass of FilledPolygon to approximate filled ellipses same way a PolyLine can approximates an Ellipse.

class FilledEllipse : public FilledPolygon {
public:
 FilledEllipse(Glasses &glasses, const Vector3D &center, const Vector3D semiAxes[2], u32 color);
 FilledEllipse(const FilledEllipse &fe);
private:
//...
};

Contacts

michelinux AT gmail DOT com