Commit c4d9ba7c authored by Michael Catanzaro's avatar Michael Catanzaro
Browse files
parent c51a1295
......@@ -32,9 +32,6 @@ PKG_CHECK_MODULES(GNOME_CHESS, [
gmodule-2.0
gtk+-3.0 >= $GTK_REQUIRED
librsvg-2.0 >= $RSVG_REQUIRED
gl
glu
x11
])
PKG_CHECK_MODULES(LIBCHESS, [
......
......@@ -536,61 +536,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="show_3d_check">
<property name="label" translatable="yes" comments="Preferences Dialog: Check box for selecting if 3D view is available">3_D chess view</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="show_3d_toggle_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label"> </property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="show_3d_smooth_check">
<property name="label" translatable="yes" comments="Preferences Dialog: Check box for selecting if the 3D view is smoothed (anti-aliased)">_Smooth display</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="show_numbering_check">
<property name="label" translatable="yes" comments="Preferences Dialog: Check box for selecting if board numbering is visible">_Board numbering</property>
......
/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* Copyright (C) 2010-2013 Robert Ancell
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 2 of the License, or (at your option) any later
* version. See http://www.gnu.org/copyleft/gpl.html the full text of the
* license.
*/
using GL;
public class TDSModel : Object
{
private GLfloat min_height = float.MAX;
private GLfloat max_height = float.MIN;
private GLfloat[] vertices;
private GLushort[] triangles;
private GLfloat[] normals;
private GLfloat[] texture_coords;
public TDSModel (File file) throws Error
{
var stream = file.read ();
parse_block (stream, stream.query_info (FileAttribute.STANDARD_SIZE).get_size ());
/* Calculate normals */
normals = new GLfloat[vertices.length];
for (int i = 0; i < normals.length; i++)
normals[i] = 0f;
for (int i = 0; i < triangles.length; i += 3)
{
var v0 = triangles[i] * 3;
var v1 = triangles[i+1] * 3;
var v2 = triangles[i+2] * 3;
/* Do cross-product of face to get normal */
GLfloat a[3], b[3], normal[3];
a[0] = vertices[v1] - vertices[v0];
a[1] = vertices[v1+1] - vertices[v0+1];
a[2] = vertices[v1+2] - vertices[v0+2];
b[0] = vertices[v2] - vertices[v0];
b[1] = vertices[v2+1] - vertices[v0+1];
b[2] = vertices[v2+2] - vertices[v0+2];
normal[0] = a[1]*b[2] - a[2]*b[1];
normal[1] = a[2]*b[0] - a[0]*b[2];
normal[2] = a[0]*b[1] - a[1]*b[0];
/* Add this normal to the three vertices of this face */
normals[v0] += normal[0];
normals[v0+1] += normal[1];
normals[v0+2] += normal[2];
normals[v1] += normal[0];
normals[v1+1] += normal[1];
normals[v1+2] += normal[2];
normals[v2] += normal[0];
normals[v2+1] += normal[1];
normals[v2+2] += normal[2];
}
/* Normalize normals */
for (int i = 0; i < normals.length; i += 3)
{
GLfloat length = (GLfloat) Math.sqrt (normals[i]*normals[i] + normals[i+1]*normals[i+1] + normals[i+2]*normals[i+2]);
normals[i] /= length;
normals[i+1] /= length;
normals[i+2] /= length;
}
/* Set texture coordinates to a conical projection */
texture_coords = new GLfloat[(vertices.length / 3) * 2];
for (int i = 0, j = 0; i < vertices.length; i += 3, j += 2)
{
var u = vertices[i];
var v = vertices[i+2];
var r = (GLfloat) Math.sqrt (u*u + v*v);
if (r != 0)
{
u /= r;
v /= r;
}
/* Maximum height is in the middle of the texture, minimum on the boundary */
var h = 1.0f - (vertices[i+1] / max_height);
texture_coords[j] = 0.5f + 0.5f * h * u;
texture_coords[j+1] = 0.5f + 0.5f * h * v;
}
}
private void parse_block (FileInputStream stream, int64 length) throws Error
{
while (length > 6)
{
var id = read_uint16 (stream);
int64 block_length = read_uint32 (stream);
if (block_length < 6)
{
return;
}
if (block_length > length)
{
// throw error
stderr.printf("Overflow, need %lli octets for %04X, but only have %lli\n", block_length, (int) id, length);
return;
}
switch (id)
{
/* Main chunk */
case 0x4D4D:
//stdout.printf("<root>\n");
parse_block (stream, block_length - 6);
//stdout.printf("</root>\n");
break;
/* Version */
/*case 0x0002:
var version = read_uint32 (stream);
//stdout.printf("<version>%u</version>\n", version);
break;*/
/* 3D editor */
case 0x3D3D:
//stdout.printf("<editor>\n");
parse_block (stream, block_length - 6);
//stdout.printf("</editor>\n");
break;
/* Object block */
case 0x4000:
var name = read_string (stream);
//stdout.printf("<object name=\"%s\">\n", name);
parse_block (stream, block_length - 6 - (name.length + 1));
//stdout.printf("</object>\n");
break;
/* Triangular mesh */
case 0x4100:
//stdout.printf("<triangles>\n");
parse_block (stream, block_length - 6);
//stdout.printf("</triangles>\n");
break;
/* Vertices */
case 0x4110:
//stdout.printf("<vertices>\n");
var n = read_uint16 (stream);
vertices = new GLfloat[n*3];
for (var i = 0; i < n; i++)
{
var x = read_float (stream);
var y = read_float (stream);
var z = read_float (stream);
var scale = 3.5f; // FIXME: Fix the model files
vertices[i*3] = scale*x;
vertices[i*3+1] = scale*z;
vertices[i*3+2] = scale*y;
var h = scale*z;
if (h < min_height)
min_height = h;
if (h > max_height)
max_height = h;
//stdout.printf ("<vertex x=\"%f\" y=\"%f\" z=\"%f\"/>\n", x, y, z);
}
//stdout.printf("</vertices>\n");
break;
/* Faces */
case 0x4120:
//stdout.printf("<faces>\n");
if (block_length < 2)
return;
var n = read_uint16 (stream);
triangles = new GLushort[n*3];
if (block_length < 2 + n * 8)
{
stderr.printf("Invalid face data, need %u, have %lli\n", 2+n*8, block_length);
return;
}
for (var i = 0; i < n; i++)
{
var a = read_uint16 (stream);
var b = read_uint16 (stream);
var c = read_uint16 (stream);
/*var flags = */read_uint16 (stream);
triangles[i*3] = (GLushort) a;
triangles[i*3+1] = (GLushort) c;
triangles[i*3+2] = (GLushort) b;
//stdout.printf ("<face a=\"%u\" b=\"%u\" c=\"%u\"/ flags=\"%u\">\n", a, b, c, flags);
}
parse_block (stream, block_length - (2 + n*8));
//stdout.printf("</faces>\n");
break;
/* Keyframer */
/* case 0xB000:
//stdout.printf("<keyframe>\n");
parse_block (stream, block_length - 6);
//stdout.printf("</keyframe>\n");
break;*/
default:
//stdout.printf ("<%04X>", id);
for (var i = 0; i < block_length - 6; i++)
{
/*var c = */read_uint8 (stream);
//stdout.printf("%02X ", c);
}
//stdout.printf ("<\\%04X>\n", id);
break;
}
length -= block_length;
//stream.seek (block_length - 6, SeekType.CUR);
}
if (length != 0)
{
return; // throw error
}
}
public void render ()
{
glEnable (GL_CULL_FACE);
glEnableClientState (GL_VERTEX_ARRAY);
glEnableClientState (GL_NORMAL_ARRAY);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
glVertexPointer (3, GL_FLOAT, 0, vertices);
glNormalPointer (GL_FLOAT, 0, normals);
glTexCoordPointer (2, GL_FLOAT, 0, texture_coords);
glDrawElements (GL_TRIANGLES, (GLsizei) triangles.length, GL_UNSIGNED_SHORT, triangles);
glDisableClientState (GL_VERTEX_ARRAY);
glDisableClientState (GL_NORMAL_ARRAY);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}
private uint8 read_uint8 (InputStream stream) throws Error
{
uchar buffer[1];
stream.read_all (buffer, null, null);
return buffer[0];
}
private uint16 read_uint16 (InputStream stream) throws Error
{
uchar buffer[2];
stream.read_all (buffer, null, null);
return buffer[1] << 8 | buffer[0];
}
private uint32 read_uint32 (InputStream stream) throws Error
{
uchar buffer[4];
stream.read_all (buffer, null, null);
return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
}
private float read_float (InputStream stream) throws Error
{
uint8 buffer[4];
stream.read_all (buffer, null, null);
float[] fbuffer = (float[]) buffer;
return fbuffer[0];
}
private string read_string (InputStream stream) throws Error
{
var value = new StringBuilder();
while (true)
{
var c = read_uint8 (stream);
if (c == 0)
return value.str;
value.append_c ((char)c);
}
}
}
......@@ -2,22 +2,16 @@ bin_PROGRAMS = gnome-chess
gnome_chess_SOURCES = \
config.vapi \
gl.vapi \
glu.vapi \
glx.vapi \
portability.vapi \
portability.c \
portability.h \
3ds.vala \
gnome-chess.vala \
ai-profile.vala \
chess-engine.vala \
chess-engine-cecp.vala \
chess-engine-uci.vala \
chess-scene.vala \
chess-view.vala \
chess-view-2d.vala \
chess-view-3d.vala
chess-view.vala
gnome_chess_CFLAGS = \
-w
......@@ -36,7 +30,6 @@ gnome_chess_LDADD = \
$(top_builddir)/lib/libchess.la
gnome_chess_VALAFLAGS = \
--pkg gdk-x11-3.0 \
--pkg glib-2.0 \
--pkg gmodule-2.0 \
--pkg gtk+-3.0 \
......
/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* Copyright (C) 2010-2013 Robert Ancell
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 2 of the License, or (at your option) any later
* version. See http://www.gnu.org/copyleft/gpl.html the full text of the
* license.
*/
private class ChessView2D : ChessView
{
private int border = 6;
private int square_size;
private int selected_square_size;
private Cairo.ImageSurface? model_surface;
private Cairo.Surface? selected_model_surface;
private string loaded_theme_name = "";
private double border_size
{
get { return square_size / 2; }
}
public ChessView2D ()
{
add_events (Gdk.EventMask.BUTTON_PRESS_MASK);
}
public override bool configure_event (Gdk.EventConfigure event)
{
int short_edge = int.min (get_allocated_width (), get_allocated_height ());
square_size = (int) Math.floor ((short_edge - 2 * border) / 9.0);
var extra = square_size * 0.1;
if (extra < 3)
extra = 3;
selected_square_size = square_size + 2 * (int) (extra + 0.5);
return true;
}
private void render_piece (Cairo.Context c1, Cairo.Context c2, string name, int offset)
{
Rsvg.Handle handle;
try
{
handle = new Rsvg.Handle.from_file (Path.build_filename (PKGDATADIR, "pieces", scene.theme_name, name + ".svg", null));
}
catch (Error e)
{
stderr.printf ("Failed to load piece svg: %s", e.message);
return;
}
c1.save ();
c1.translate (square_size * offset, 0);
c1.scale ((double) square_size / handle.width, (double) square_size / handle.height);
handle.render_cairo (c1);
c1.restore ();
c2.save ();
c2.translate (selected_square_size * offset, 0);
c2.scale ((double) selected_square_size / handle.width, (double) selected_square_size / handle.height);
handle.render_cairo (c2);
c2.restore ();
}
private void load_theme (Cairo.Context c)
{
/* Skip if already loaded */
if (scene.theme_name == loaded_theme_name && model_surface != null && square_size == model_surface.get_height ())
return;
model_surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, 12 * square_size, square_size);
selected_model_surface = new Cairo.Surface.similar (c.get_target (), Cairo.Content.COLOR_ALPHA, 12 * selected_square_size, selected_square_size);
var c1 = new Cairo.Context (model_surface);
var c2 = new Cairo.Context (selected_model_surface);
render_piece (c1, c2, "whitePawn", 0);
render_piece (c1, c2, "whiteRook", 1);
render_piece (c1, c2, "whiteKnight", 2);
render_piece (c1, c2, "whiteBishop", 3);
render_piece (c1, c2, "whiteQueen", 4);
render_piece (c1, c2, "whiteKing", 5);
render_piece (c1, c2, "blackPawn", 6);
render_piece (c1, c2, "blackRook", 7);
render_piece (c1, c2, "blackKnight", 8);
render_piece (c1, c2, "blackBishop", 9);
render_piece (c1, c2, "blackQueen", 10);
render_piece (c1, c2, "blackKing", 11);
loaded_theme_name = scene.theme_name;
}
public override bool draw (Cairo.Context c)
{
load_theme (c);
c.translate (get_allocated_width () / 2, get_allocated_height () / 2);
//c.scale (s, s);
c.rotate (Math.PI * scene.board_angle / 180.0);
int board_size = (int) Math.ceil (square_size * 4 + border_size);
c.set_source_rgb (0x2e/255.0, 0x34/255.0, 0x36/255.0);
c.rectangle (-board_size, -board_size, board_size * 2, board_size * 2);
c.fill ();
for (int file = 0; file < 8; file++)
{
for (int rank = 0; rank < 8; rank++)
{
int x = (int) ((file - 4) * square_size);
int y = (int) ((3 - rank) * square_size);
c.rectangle (x, y, square_size, square_size);
if ((file + rank) % 2 == 0)
c.set_source_rgb (0xba/255.0, 0xbd/255.0, 0xb6/255.0);
else
c.set_source_rgb (0xee/255.0, 0xee/255.0, 0xec/255.0);
c.fill ();
}
}
if (scene.show_numbering)
{
/* Files are centered individiual glyph width and combined glyph height,
* ranks are centered on individual glyph widths and heights */
c.set_source_rgb (0x88/255.0, 0x8a/255.0, 0x85/255.0);
c.set_font_size (border_size * 0.6);
c.select_font_face ("sans-serif", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
Cairo.TextExtents extents;
c.text_extents ("abcdefgh", out extents);
double y_offset = (square_size / 2 - extents.height) / 2 + extents.height + extents.y_bearing;
double top = -(square_size * 4 + y_offset);
double bottom = square_size * 4 + border_size - y_offset;
double file_offset = -(square_size * 3.5);
double rank_offset = -(square_size * 3.5);
string[] files;
string[] ranks;
Cairo.Matrix matrix = c.get_matrix ();
if (scene.board_angle == 180.0)
{
files = { "h", "g", "f", "e", "d", "c", "b", "a" };
ranks = { "1", "2", "3", "4", "5", "6", "7", "8" };
matrix.scale (-1, -1);
}
else
{
files = { "a", "b", "c", "d", "e", "f", "g", "h" };
ranks = { "8", "7", "6", "5", "4", "3", "2", "1" };
}
c.save ();
c.set_matrix (matrix);
for (int i = 0; i < 8; i++)
{
c.text_extents (ranks[i], out extents);
/* Black file */
c.save ();
c.move_to (file_offset - extents.width / 2, top);
c.show_text (files[i]);
c.restore ();
/* White file */
c.save ();
c.move_to (file_offset - extents.width / 2, bottom);
c.show_text (files[i]);
c.restore ();
c.text_extents (ranks[i], out extents);
y_offset = -(extents.y_bearing + extents.height / 2);
/* Left rank */
c.save ();
c.move_to (-((double) square_size * 4 + border_size - (border_size - extents.width) / 2), rank_offset + y_offset);
c.show_text (ranks[i]);
c.restore ();
/* Right rank */
c.save ();
c.move_to ((double) square_size * 4 + (border_size - extents.width) / 2, rank_offset + y_offset);
c.show_text (ranks[i]);
c.restore ();
file_offset += square_size;
rank_offset += square_size;
}
c.restore ();
}
/* Draw pause overlay */
if (scene.game.is_paused)
{
c.rotate (Math.PI * scene.board_angle / 180.0);
draw_paused_overlay (c);
return true;
}
/* Draw the pieces */
foreach (var model in scene.pieces)
{
c.save ();
c.translate ((model.x - 4) * square_size, (3 - model.y) * square_size);
c.translate (square_size / 2, square_size / 2);
c.rotate (-Math.PI * scene.board_angle / 180.0);
draw_piece (c,
model.is_selected ? selected_model_surface : model_surface,
model.is_selected ? selected_square_size : square_size,
model.piece, model.under_threat && scene.show_move_hints ? 0.8 : 1.0);