Task 096: Rubik's Cube

Your task is to implement a simple 3D simulation of Rubik's Cube. The user can manipulate a virtual cube on the screen, and can view it from various angles using the "trackball" controls. And by pressing the second button on a block, a rotation of a cube part by 90 degrees can be started.

Rubik's Cube

Overview

The template application 096puzzle from the repository grcis serves as the basis of this project. This is a complete template application which can render and manipulate a 3D scene. The puzzle-specific part that is called during rendering is located in Puzzle.Simulate(). To control the simulation, the Start / Stop, Reset sim and Update sim methods are provided. During initialisation / update the simulation can also use user-supplied parameters from the string Param.

Animation, Control

It is sufficient to implement a simple rotation of the moving puzzle parts with a constant angular velocity. But for extra points, you can also implement some improvements (acceleration and deceleration), turbo mode, etc.
Controls: the left mouse button is used for rotating the whole scene (Trackball). The right button is used to rotate the cube layer ("pulls"). Specific details of the operation are entirely up to you: for instance, what happens when you click on a cube part to initiate a layer rotation. You will need to use some simple gestures ("jerks") for the resolution of ambiguous clicks on the corner cubes. When the user clicks on the central squares you do not have to do anything, as this action cannot meaningfully initiate a layer rotation.

Technical Details

In the initial version of the project, there is the Cube class (a subclass of DefaultRenderObject) as default object, and the entire simulation is enclosed within the class Puzzle. The simulation is calculated at discrete time intervals, once for every display cycle. All simulated objects (initially, only a Cube instances) have to declare a function Simulate ( double time ) which computes their location in space for a given time, input as double value time, given in seconds. The application also has some built-in mechanisms for debugging and detailed examination of its behaviour: the Start / stop, Reset sim and Update sim controls. Furthermore, there is a checkbox that allows to slow the simulated time by a specified factor (default: slow=0.25, i.e. a quarter of normal speed).

Rendering

A universal interface (interface IRenderObject) is used to render all the components of the simulated game environment. All objects can define points (GL_POINTS), lines (GL_LINES) and triangles (GL_TRIANGLES), via which they will be displayed. In the case of Rubik's cube, the system is simplified, as only triangles are used (but if you want, you can also make use of lines and points during transition animations, if you find a good use for them). Rendering is implemented via two methods that are called after each other: The first first method is called TriangleVertices() (TriangleIndices(), LineVertices(), LineIndices() or PointVertices()), and only serve to determine the size of the vertex buffer in bytes - no data is provided in this step! The second method provides the actual data, and the VBO buffer is mapped into memory by via GL.MapBuffer(). The global simulation object Puzzle implements rendering of the cube via triangles (FillTriangleData()). Just when all of your simulation objects will correctly implement the interface IRenderObject and you will not have to render more involved. For simplicity, the project is not using shaders, but you can switch between color and texture mapping individual walls (the Tex checkbox).

The UnProject() Function

For interpreting mouse clicks on the displayed 3D scene, the application uses the standard UnProject function (originally gluUnProject()). The 2D coordinates on screen that are returned via MouseEventArgs events include the z-component, and the Geometry.UnProject() calculates the corresponding point in the global 3D coordinate system. The z-component may range from 0.0 (a point on the near clipping plane) to 1.0 (a point on the far clipping plane, or at infinity).

Based on this data, the application must decide which object the user has actually selected. Typically one considers that object in the 3D scene which first intersects the ray between eye and the mouse position (ie. the ray we get when we connect the "near" and "far" points). Our application includes sample code: there is also a debug mode where after pressing the right mouse button, such a test beam is calculated, and its intersection with the cube is shown. The endpoints of the line are coloured, and the intersection points are drawn in white. For illustration, you can also press F (Frustum) to generate a 3D representation of the visual field, which was valid at that particular moment (to properly see this, one obviously has to change the viewing direction, and also the zoom level). The sample implementation of this functionality consists of: the methods screenToWorld() glControl1_MouseDown() and the code at the end of Application_Idle(). The pilot implementation cube contains a function Cube.Intersect(), which passes all the triangles from the model (+ cached position of the current transformation matrix modeling objectMatrix), and which searches for the nearest intersection.

A similar function has been added to the project 086shader, where you can try these functions on objects that you read from disk.

Deadline

Hand in until: 21. 1. 2017

Points

Basis: 10 points (for a simple but functional simulation).
Bonus: up to 8 additional points for interesting extensions

Project

Visual Studio projects: 096puzzle

Source file

Modify and hand in the source file: Puzzle.cs
As a comment in the first line, please include your name!


Copyright (C) 2016 J. Pelikán & A. Wilkie, last change: 2015-10-26 01:59:27 +0100 (Mo, 26 Okt 2015)