Archived
Got a model to load and appear in the GLArea
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# Blender MTL File: 'None'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Black_Piece
|
||||
Ns 650.249991
|
||||
Ka 0.275000 0.275000 0.275000
|
||||
Kd 0.107621 0.105609 0.108037
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 3
|
||||
|
||||
newmtl White_Piece
|
||||
Ns 650.249991
|
||||
Ka 0.650000 0.650000 0.650000
|
||||
Kd 0.882937 1.000000 1.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 3
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 512 KiB |
@@ -0,0 +1,12 @@
|
||||
# Blender 5.2.0 LTS MTL File: 'prop_sign_traffic01.blend'
|
||||
# www.blender.org
|
||||
|
||||
newmtl Material
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.500000
|
||||
illum 2
|
||||
map_Kd props_streetsigns.png
|
||||
map_d props_streetsigns.png
|
||||
@@ -0,0 +1,82 @@
|
||||
# Blender 5.2.0 LTS
|
||||
# www.blender.org
|
||||
mtllib prop_sign_traffic01.mtl
|
||||
o Pole
|
||||
v 0.178493 1.997270 0.017707
|
||||
v 0.000000 2.000000 0.000000
|
||||
v 0.000000 0.000000 -0.020000
|
||||
v 0.000000 2.000000 -0.020000
|
||||
v 0.019021 0.000000 -0.006180
|
||||
v 0.019021 2.000000 -0.006180
|
||||
v 0.011756 0.000000 0.016180
|
||||
v 0.011756 2.000000 0.016180
|
||||
v -0.011756 0.000000 0.016180
|
||||
v -0.011756 2.000000 0.016180
|
||||
v -0.019021 0.000000 -0.006180
|
||||
v -0.019021 2.000000 -0.006180
|
||||
v -0.178493 1.997270 0.017707
|
||||
v 0.178493 1.640285 0.017707
|
||||
v -0.178493 1.640285 0.017707
|
||||
v -0.178493 1.640285 0.014304
|
||||
v 0.178493 1.640285 0.014304
|
||||
v -0.178493 1.997270 0.014304
|
||||
v 0.178493 1.997270 0.014304
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 0.9511 -0.0000 -0.3090
|
||||
vn 0.5878 -0.0000 0.8090
|
||||
vn -0.5878 -0.0000 0.8090
|
||||
vn -0.9511 -0.0000 -0.3090
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vt 0.010888 0.998435
|
||||
vt 0.007837 0.996283
|
||||
vt 0.013939 0.996283
|
||||
vt 0.061975 0.125000
|
||||
vt 0.061975 1.000000
|
||||
vt 0.040605 1.000000
|
||||
vt 0.040605 0.125000
|
||||
vt 0.008128 0.999870
|
||||
vt 0.019236 1.000000
|
||||
vt 0.019236 0.125000
|
||||
vt 0.010888 0.999870
|
||||
vt 0.062500 0.125000
|
||||
vt 0.062500 1.000000
|
||||
vt 0.042822 1.000000
|
||||
vt 0.042822 0.125000
|
||||
vt 0.013647 0.999870
|
||||
vt 0.021453 1.000000
|
||||
vt 0.021453 0.125000
|
||||
vt 0.269104 0.368067
|
||||
vt 0.159931 0.368067
|
||||
vt 0.159931 0.249896
|
||||
vt 0.269104 0.249896
|
||||
vt 0.000084 1.000000
|
||||
vt 0.000084 0.125000
|
||||
vt 0.499907 0.496094
|
||||
vt 0.390624 0.496094
|
||||
vt 0.390624 0.378473
|
||||
vt 0.499907 0.378473
|
||||
s 0
|
||||
usemtl Material
|
||||
f 2/1/1 6/2/1 4/3/1
|
||||
s 1
|
||||
f 3/4/2 4/5/2 6/6/3 5/7/3
|
||||
s 0
|
||||
f 2/1/1 8/8/1 6/2/1
|
||||
s 1
|
||||
f 5/7/3 6/6/3 8/9/4 7/10/4
|
||||
s 0
|
||||
f 2/1/1 10/11/1 8/8/1
|
||||
s 1
|
||||
f 7/12/4 8/13/4 10/14/5 9/15/5
|
||||
s 0
|
||||
f 2/1/1 12/16/1 10/11/1
|
||||
s 1
|
||||
f 9/15/5 10/14/5 12/17/6 11/18/6
|
||||
s 0
|
||||
f 1/19/7 13/20/7 15/21/7 14/22/7
|
||||
f 2/1/1 4/3/1 12/16/1
|
||||
s 1
|
||||
f 11/18/6 12/17/6 4/23/2 3/24/2
|
||||
s 0
|
||||
f 19/25/7 18/26/7 16/27/7 17/28/7
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
+18
-185
@@ -1,189 +1,22 @@
|
||||
using GL;
|
||||
|
||||
namespace Worldbuilder.Core {
|
||||
|
||||
namespace Worldbuilder.Core
|
||||
{
|
||||
public class Camera : Object
|
||||
{
|
||||
/**
|
||||
* Encapsulates a camera object. A camera contains a projection matrix and a view matrix.
|
||||
*
|
||||
* The main purpose of this class is to provide equivalents of legacy OpenGL functions
|
||||
* removed in modern OpenGL. Advanced features like quaternion-based rotations are beyond
|
||||
* the scope of this project.
|
||||
* The position of the camera
|
||||
*/
|
||||
public class Camera : Object {
|
||||
private Matrix4x4 projection_matrix;
|
||||
private Matrix4x4 view_matrix;
|
||||
private Matrix4x4 result_matrix;
|
||||
private Matrix4x4 total_matrix;
|
||||
|
||||
/**
|
||||
* Instantiates a camera object.
|
||||
*
|
||||
* The projection and view matrices are set to the identity matrix.
|
||||
*/
|
||||
public Camera() {
|
||||
reset();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
result_matrix = projection_matrix;
|
||||
result_matrix.multiply(view_matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets both the projection matrix and the view matrix.
|
||||
*/
|
||||
public void reset() {
|
||||
reset_projection();
|
||||
reset_view();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the projection matrix, setting it to the identity matrix.
|
||||
*/
|
||||
public void reset_projection() {
|
||||
projection_matrix = Matrix4x4.identity();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the view matrix, setting it to the identity matrix.
|
||||
*/
|
||||
public void reset_view() {
|
||||
view_matrix = Matrix4x4.identity();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the camera to the next model object that will be drawn, by
|
||||
* multiplying the current projection and view matrices by the model matrix
|
||||
* passed to the function (which specifies the offset and rotation of the model
|
||||
* relative to the camera), and binds the matrix data to the specified
|
||||
* shader uniform variable.
|
||||
*
|
||||
* @param uniform_id The shader uniform variable to bind to
|
||||
* @param model_matrix The model matrix
|
||||
*/
|
||||
public void apply(int uniform_id, ref Matrix4x4 model_matrix) {
|
||||
total_matrix = result_matrix;
|
||||
total_matrix.multiply(model_matrix);
|
||||
glUniformMatrix4fv(uniform_id, 1, (GL.GLboolean)false, total_matrix.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a perspective projection. The previous projection matrix is ignored and overwritten.
|
||||
*
|
||||
* Replicates the effects of the legacy ``glFrustum`` function within this camera.
|
||||
* Refer to that function for the complete description of the projection parameters.
|
||||
*
|
||||
* @param left The coordinate of the left vertical clipping plane.
|
||||
* @param right The coordinate of the right vertical clipping plane.
|
||||
* @param bottom The coordinate of the bottom horizontal clipping plane.
|
||||
* @param top The coordinate of the top horizontal clipping plane.
|
||||
* @param near The coordinate of the near vertical clipping plane. Must be positive.
|
||||
* @param far The coordinate of the far vertical clipping plane. Must be positive.
|
||||
*/
|
||||
public void set_frustum_projection(
|
||||
float left,
|
||||
float right,
|
||||
float bottom,
|
||||
float top,
|
||||
float near, float far
|
||||
) {
|
||||
projection_matrix = Matrix4x4.from_data(
|
||||
2f * near / (right - left), 0, (right + left) / (right - left), 0,
|
||||
0, 2f * near / (top - bottom), (top + bottom) / (top - bottom), 0,
|
||||
0, 0, -(far + near) / (far - near), -2f * far * near / (far - near),
|
||||
0, 0, -1, 0
|
||||
);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a symmetrical perspective projection. The previous projection matrix is ignored and overwritten.
|
||||
*
|
||||
* Replicates the effects of the legacy ``gluPerspective`` function within this camera.
|
||||
* Refer to that function for the complete description of the projection parameters.
|
||||
*
|
||||
* @param fovy_deg The field of view angle, in degrees, in the y direction.
|
||||
* @param aspect The aspect ratio that determines the field of view in the x direction.
|
||||
* The aspect ratio is the ratio of x (width) to y (height).
|
||||
* @param near The coordinate of the near vertical clipping plane. Must be positive.
|
||||
* @param far The coordinate of the far vertical clipping plane. Must be positive.
|
||||
*/
|
||||
public void set_perspective_projection(float fovy_deg, float aspect, float near, float far) {
|
||||
var f = 1 / Math.tanf(MathUtils.DegToRad(fovy_deg / 2));
|
||||
|
||||
projection_matrix = Matrix4x4.from_data(
|
||||
f / aspect, 0, 0, 0,
|
||||
0, f, 0, 0,
|
||||
0, 0, -(far + near) / (far - near), -2f * far * near / (far - near),
|
||||
0, 0, -1, 0
|
||||
);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an orthogonal projection. The previous projection matrix is ignored and overwritten.
|
||||
*
|
||||
* Replicates the effects of the legacy ``glOrtho`` function within this camera.
|
||||
* Refer to that function for the complete description of the projection parameters.
|
||||
*
|
||||
* @param left The coordinate of the left vertical clipping plane.
|
||||
* @param right The coordinate of the right vertical clipping plane.
|
||||
* @param bottom The coordinate of the bottom horizontal clipping plane.
|
||||
* @param top The coordinate of the top horizontal clipping plane.
|
||||
* @param near The coordinate of the near vertical clipping plane.
|
||||
* @param far The coordinate of the far vertical clipping plane.
|
||||
*/
|
||||
public void set_ortho_projection(
|
||||
float left,
|
||||
float right,
|
||||
float bottom,
|
||||
float top,
|
||||
float near, float far
|
||||
) {
|
||||
projection_matrix = Matrix4x4.from_data(
|
||||
2f / (right - left), 0, 0, (right + left) / (right - left),
|
||||
0, 2f / (top - bottom), 0, (top + bottom) / (top - bottom),
|
||||
0, 0, -2f * far * near / (far - near), -(far + near) / (far - near),
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replicates the effects of the legacy ``gluLookAt`` function within this camera.
|
||||
* Refer to that function for the complete description of how the camera is positioned.
|
||||
*
|
||||
* @param eye The 3D position of the camera
|
||||
* @param center The 3D position of the point the camera is looking at
|
||||
* @param up The up-direction
|
||||
*/
|
||||
public void look_at(ref Vector3 eye, ref Vector3 center, ref Vector3 up) {
|
||||
Vector3 l = center;
|
||||
l.subtract(eye);
|
||||
l.normalized();
|
||||
|
||||
Vector3 uptemp = up;
|
||||
uptemp.normalized();
|
||||
|
||||
Vector3 s = l.cross(uptemp);
|
||||
Vector3 u = s.cross(l);
|
||||
|
||||
view_matrix = Matrix4x4.from_data(
|
||||
s.x, s.y, s.z, 0,
|
||||
u.x, u.y, u.z, 0,
|
||||
-l.x, -l.y, -l.z, 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
Vector3 translation = Vector3();
|
||||
translation.subtract(eye);
|
||||
view_matrix = view_matrix.translate(translation.x, translation.y, translation.z);
|
||||
update();
|
||||
}
|
||||
public Vector3 position { get; set; }
|
||||
|
||||
/**
|
||||
* The direction the camera is facing.
|
||||
*/
|
||||
public Vector3 direction { get; set; }
|
||||
|
||||
public Camera (Vector3 position = Vector3 (), Vector3 direction = Vector3 (0, 0, -1))
|
||||
{
|
||||
this.position = position;
|
||||
this.direction = direction;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
namespace Worldbuilder.Core
|
||||
{
|
||||
public class Material : Object
|
||||
{
|
||||
public string id { get; construct; }
|
||||
|
||||
public Vector3 ambient_color { get; set; }
|
||||
public Vector3 diffuse_color { get; set; }
|
||||
public Vector3 specular_color { get; set; }
|
||||
public float specular_exponent { get; set; }
|
||||
|
||||
public Texture2D? ambient_texture { get; set; }
|
||||
public Texture2D? diffuse_texture { get; set; }
|
||||
public Texture2D? specular_texture { get; set; }
|
||||
|
||||
public Material (string id) {
|
||||
Object (id: id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a .mtl file and return all the materials found.
|
||||
*/
|
||||
public static Material[] load_library (string path) throws Error
|
||||
{
|
||||
Material[] mats = {};
|
||||
var data = new Utf8InputStream (resources_open_stream (path, ResourceLookupFlags.NONE));
|
||||
|
||||
debug ("loading %s ...", path);
|
||||
while (data.next_line ()) {
|
||||
if (data.at_end_of_line || data.skip_char ('#')) { // empty line or comment
|
||||
continue;
|
||||
} else if (data.skip_symbol ("newmtl")) {
|
||||
string mtlname = data.read_string ();
|
||||
mats += new Material (mtlname);
|
||||
} else {
|
||||
if (mats.length == 0)
|
||||
throw new IOError.INVALID_DATA ("line %u: material undefined here", data.lineno);
|
||||
|
||||
if (data.skip_symbol ("Ka")) {
|
||||
float x = data.parse_float ();
|
||||
float y = data.parse_float ();
|
||||
float z = data.parse_float ();
|
||||
mats[mats.length-1].ambient_color = Vector3 (x, y, z);
|
||||
} else if (data.skip_symbol ("Kd")) {
|
||||
float x = data.parse_float ();
|
||||
float y = data.parse_float ();
|
||||
float z = data.parse_float ();
|
||||
mats[mats.length-1].diffuse_color = Vector3 (x, y, z);
|
||||
} else if (data.skip_symbol ("Ks")) {
|
||||
float x = data.parse_float ();
|
||||
float y = data.parse_float ();
|
||||
float z = data.parse_float ();
|
||||
mats[mats.length-1].specular_color = Vector3 (x, y, z);
|
||||
} else if (data.skip_symbol ("Ns")) {
|
||||
mats[mats.length-1].specular_exponent = data.parse_float ();
|
||||
} else if (data.skip_symbol ("map_Ka")) {
|
||||
string filename = Path.get_basename (data.read_string ());
|
||||
mats[mats.length-1].ambient_texture = new Texture2D (filename);
|
||||
} else if (data.skip_symbol ("map_Kd")) {
|
||||
string filename = Path.get_basename (data.read_string ());
|
||||
mats[mats.length-1].diffuse_texture = new Texture2D (filename);
|
||||
} else if (data.skip_symbol ("map_Ks")) {
|
||||
string filename = Path.get_basename (data.read_string ());
|
||||
mats[mats.length-1].specular_texture = new Texture2D (filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mats;
|
||||
}
|
||||
|
||||
public string to_string ()
|
||||
{
|
||||
var sb = new StringBuilder ();
|
||||
sb.append ("Material ");
|
||||
sb.append (id);
|
||||
sb.append (" { ambient: ");
|
||||
if (ambient_texture != null) {
|
||||
sb.append ("( ");
|
||||
sb.append (ambient_color.to_string ());
|
||||
sb.append (", ");
|
||||
sb.append (ambient_texture.path);
|
||||
sb.append_c (')');
|
||||
} else {
|
||||
sb.append (ambient_color.to_string ());
|
||||
}
|
||||
sb.append (", diffuse: ");
|
||||
if (diffuse_texture != null) {
|
||||
sb.append ("( ");
|
||||
sb.append (diffuse_color.to_string ());
|
||||
sb.append (", ");
|
||||
sb.append (diffuse_texture.path);
|
||||
sb.append_c (')');
|
||||
} else {
|
||||
sb.append (diffuse_color.to_string ());
|
||||
}
|
||||
sb.append (", specular: ");
|
||||
if (specular_texture != null) {
|
||||
sb.append ("( ");
|
||||
sb.append (specular_color.to_string ());
|
||||
sb.append (", ");
|
||||
sb.append (specular_texture.path);
|
||||
sb.append_c (')');
|
||||
} else {
|
||||
sb.append (specular_color.to_string ());
|
||||
}
|
||||
sb.append (" }");
|
||||
return sb.str;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
namespace Worldbuilder.Core
|
||||
{
|
||||
public class Mesh : Object
|
||||
{
|
||||
public struct Point
|
||||
{
|
||||
public Vector3 v;
|
||||
public Vector2 uv;
|
||||
public Vector3 n;
|
||||
|
||||
public Point (Vector3 v = Vector3(), Vector2 uv = Vector2(), Vector3 n = Vector3())
|
||||
{
|
||||
this.v = v;
|
||||
this.uv = uv;
|
||||
this.n = n;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Face
|
||||
{
|
||||
public Point p[3];
|
||||
|
||||
public string to_string ()
|
||||
{
|
||||
return @"($(p[0].v)/$(p[0].uv)/$(p[0].n), $(p[1].v)/$(p[1].uv)/$(p[1].n), $(p[2].v)/$(p[2].uv)/$(p[2].n))";
|
||||
}
|
||||
}
|
||||
|
||||
public string id { get; construct; }
|
||||
public GL.uint vao { get; construct; }
|
||||
public GL.uint vbo { get; construct; }
|
||||
|
||||
/**
|
||||
* Number of vertices in this mesh. Pass this value to {@link GL.draw_arrays}
|
||||
*/
|
||||
public int size { get; construct; }
|
||||
|
||||
public Material? material { get; set; }
|
||||
|
||||
/**
|
||||
* Construct GL buffers for all vertices, UVs, and normals.
|
||||
*
|
||||
* @param id a unique identifier for the mesh
|
||||
* @param program the current GL program, used to recover attribute locations
|
||||
* @param material the material for this mesh, or null
|
||||
*/
|
||||
public Mesh (string id, Face[] tris, GL.Program program, Material? material)
|
||||
{
|
||||
Object (id: id,
|
||||
vao: GL.gen_vertex_array (),
|
||||
vbo: GL.gen_buffer (),
|
||||
size: tris.length * 3);
|
||||
this.material = material;
|
||||
|
||||
// debug (@"mesh $id has $(tris.length) triangle(s):");
|
||||
// for (var i = 0; i < tris.length; i++)
|
||||
// debug (@" [$i] = $(tris[i])");
|
||||
// create one contiguous view of the mesh data
|
||||
unowned float[] data = (float[]) tris;
|
||||
|
||||
GL.bind_vertex_array (vao);
|
||||
|
||||
// upload the data
|
||||
GL.bind_buffer (GL.ARRAY_BUFFER, vbo);
|
||||
GL.buffer_floats (GL.ARRAY_BUFFER, data, GL.STATIC_DRAW);
|
||||
|
||||
// set up the vertex attribute locations for this data
|
||||
GL.VertexAttribute position_attr = program.get_attrib_location ("position");
|
||||
GL.VertexAttribute texcoord_attr = program.get_attrib_location ("texcoord");
|
||||
GL.VertexAttribute normal_attr = program.get_attrib_location ("normal");
|
||||
|
||||
debug (@"shader attributes: (position: $position_attr, texcoord: $texcoord_attr, normal: $normal_attr)");
|
||||
if (position_attr != GL.NULL_ATTRIBUTE) {
|
||||
position_attr.pointer (3, GL.FLOAT, GL.FALSE, (GL.sizei) ((3 + 2 + 3) * sizeof(float)));
|
||||
position_attr.enable_array ();
|
||||
}
|
||||
|
||||
if (texcoord_attr != GL.NULL_ATTRIBUTE) {
|
||||
texcoord_attr.pointer (2, GL.FLOAT, GL.FALSE, (GL.sizei) ((3 + 2 + 3) * sizeof(float)), 3 * sizeof(float));
|
||||
texcoord_attr.enable_array ();
|
||||
}
|
||||
|
||||
if (normal_attr != GL.NULL_ATTRIBUTE) {
|
||||
normal_attr.pointer (3, GL.FLOAT, GL.FALSE, (GL.sizei) ((3 + 2 + 3) * sizeof(float)), (3 + 2) * sizeof(float));
|
||||
normal_attr.enable_array ();
|
||||
}
|
||||
|
||||
GL.bind_vertex_array (0); // unbind VAO
|
||||
}
|
||||
|
||||
~Mesh ()
|
||||
{
|
||||
// TODO: GL.delete_vertex_array (vao);
|
||||
// TODO: delete other buffers
|
||||
}
|
||||
|
||||
public string to_string ()
|
||||
{
|
||||
return @"$id (vbo: $vbo)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
|
||||
namespace Worldbuilder.Core
|
||||
{
|
||||
/**
|
||||
* An object in the scene.
|
||||
*/
|
||||
public class Model : Object
|
||||
{
|
||||
public HashTable<string, Mesh> meshes { get; default = new HashTable<string, Mesh> (str_hash, str_equal); }
|
||||
public HashTable<string, Material> materials { get; default = new HashTable<string, Material> (str_hash, str_equal); }
|
||||
|
||||
public Vector3 position { get; set; }
|
||||
public Vector3 rotation { get; set; }
|
||||
public Vector3 scale { get; set; default = Vector3 (1.0f, 1.0f, 1.0f); }
|
||||
|
||||
public GenericSet<Model> children { get; default = new GenericSet<Model> (null, null); }
|
||||
|
||||
public string name { get; construct; }
|
||||
|
||||
private static uint next_id = 0;
|
||||
|
||||
public uint id { get; construct; }
|
||||
|
||||
/**
|
||||
* Construct a model from a Wavefront object.
|
||||
*
|
||||
* @param path GResource path
|
||||
*/
|
||||
public Model (string name, string path, GL.Program program) throws Error {
|
||||
Object (name: name, id: ++next_id);
|
||||
|
||||
debug ("created Model \"%s\" with ID %u", name, id);
|
||||
|
||||
var data = new Utf8InputStream (resources_open_stream (path, ResourceLookupFlags.NONE));
|
||||
Vector3[] vertices = {}; // all the vertices in the .obj
|
||||
Vector2[] uvs = {}; // all the UVs in the .obj
|
||||
Vector3[] normals = {}; // all the normals in the .obj
|
||||
Mesh.Face[] faces = {}; // only the faces of the current mesh
|
||||
string? mesh_id = null;
|
||||
Material? material = null; // the current material
|
||||
|
||||
debug ("loading %s ...", path);
|
||||
while (data.next_line ()) {
|
||||
if (data.skip_char ('#')) { // comments
|
||||
continue;
|
||||
} else if (data.skip_symbol ("mtllib")) {
|
||||
string mtllib = data.read_string ();
|
||||
foreach (var mat in Material.load_library (@"/com/brodybrooks/WorldBuilder/models/$mtllib")) {
|
||||
materials[mat.id] = mat;
|
||||
}
|
||||
} else if (data.skip_symbol ("usemtl")) {
|
||||
string mat_id = data.read_string ();
|
||||
if (!materials.contains (mat_id))
|
||||
throw new IOError.INVALID_DATA ("%s:line %u:%s is not a valid material ID", path, data.lineno, mat_id);
|
||||
// save a new mesh with the previous material first
|
||||
if (faces.length > 0) {
|
||||
var mesh = new Mesh (mesh_id ?? @"Mesh#$(meshes.length)", faces, program, material);
|
||||
meshes[mesh.id] = mesh;
|
||||
// reset faces
|
||||
faces = {};
|
||||
material = null;
|
||||
mesh_id = null;
|
||||
}
|
||||
material = materials[mat_id];
|
||||
} else if (data.skip_symbol ("o")) {
|
||||
if (faces.length > 0) {
|
||||
var mesh = new Mesh (mesh_id ?? @"Mesh#$(meshes.length)", faces, program, material);
|
||||
meshes[mesh.id] = mesh;
|
||||
// reset faces
|
||||
faces = {};
|
||||
material = null;
|
||||
mesh_id = null;
|
||||
}
|
||||
mesh_id = data.read_string ();
|
||||
if (mesh_id.length == 0) // object name is optional
|
||||
mesh_id = null;
|
||||
} else if (data.skip_symbol ("v")) {
|
||||
float x = data.parse_float ();
|
||||
float y = data.parse_float ();
|
||||
float z = data.parse_float ();
|
||||
try {
|
||||
float w = data.parse_float (); // w
|
||||
x /= w;
|
||||
y /= w;
|
||||
z /= w;
|
||||
} catch (Error e) { /* ignore */ }
|
||||
vertices += Vector3 (x, y, z);
|
||||
} else if (data.skip_symbol ("vt")) { // texture coordinates
|
||||
float u = data.parse_float ();
|
||||
float v = data.parse_float ();
|
||||
try {
|
||||
data.parse_float (); // w
|
||||
} catch (Error e) { /* ignore */ }
|
||||
uvs += Vector2 (u, v);
|
||||
} else if (data.skip_symbol ("vn")) { // normals
|
||||
float x = data.parse_float ();
|
||||
float y = data.parse_float ();
|
||||
float z = data.parse_float ();
|
||||
normals += Vector3 (x, y, z).normalized ();
|
||||
} else if (data.skip_symbol ("f")) { // face
|
||||
Mesh.Point points[3];
|
||||
int npoints = 0;
|
||||
|
||||
while (!data.at_end_of_line) {
|
||||
int vi = -1, vt = -1, vn = -1;
|
||||
|
||||
vi = data.parse_int ();
|
||||
if (data.skip_char ('/')) {
|
||||
if (data.peek_char () != '/')
|
||||
vt = data.parse_int ();
|
||||
}
|
||||
if (data.skip_char ('/'))
|
||||
vn = data.parse_int ();
|
||||
|
||||
if (vi - 1 < 0 || vi - 1 >= vertices.length)
|
||||
throw new IOError.INVALID_DATA ("%s:line %u: invalid vertex ID %d", path, data.lineno, vi - 1);
|
||||
points[npoints % 3].v = vertices[vi - 1];
|
||||
if (vt != -1) {
|
||||
if (vt - 1 < 0 || vt - 1 >= uvs.length)
|
||||
throw new IOError.INVALID_DATA ("%s:line %u: invalid texture ID %d", path, data.lineno, vt - 1);
|
||||
points[npoints % 3].uv = uvs[vt - 1];
|
||||
}
|
||||
if (vn != -1) {
|
||||
if (vn - 1 < 0 || vn - 1 >= normals.length)
|
||||
throw new IOError.INVALID_DATA ("%s:line %u: invalid normal ID %d", path, data.lineno, vn - 1);
|
||||
points[npoints % 3].n = normals[vn - 1];
|
||||
}
|
||||
|
||||
npoints++;
|
||||
if (npoints >= 3) {
|
||||
faces += Mesh.Face () { p = points };
|
||||
points[1] = points[2];
|
||||
npoints += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (faces.length > 0) {
|
||||
var mesh = new Mesh (mesh_id ?? @"Mesh#$(meshes.length)", faces, program, material);
|
||||
meshes[mesh.id] = mesh;
|
||||
faces = {};
|
||||
material = null;
|
||||
mesh_id = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
namespace Worldbuilder.Core
|
||||
{
|
||||
public class Utf8InputStream : Object
|
||||
{
|
||||
public DataInputStream base_stream { get; construct; }
|
||||
private string _line = "";
|
||||
private size_t _line_len = 0;
|
||||
private size_t _line_offset = 0;
|
||||
|
||||
public Utf8InputStream (InputStream base_stream) throws IOError
|
||||
{
|
||||
Object (base_stream: new DataInputStream (base_stream));
|
||||
}
|
||||
|
||||
public bool at_end_of_line { get { return _line_offset >= _line_len; } }
|
||||
|
||||
public uint lineno { get; private set; }
|
||||
|
||||
/**
|
||||
* Advance to the next line in the data. On the first call, advances to the
|
||||
* first line.
|
||||
*/
|
||||
public bool next_line () throws IOError
|
||||
{
|
||||
string? data = base_stream.read_line_utf8 ();
|
||||
if (data == null)
|
||||
return false;
|
||||
_line = data.strip ();
|
||||
_line_len = _line.length;
|
||||
_line_offset = 0;
|
||||
lineno += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public char peek_char ()
|
||||
{
|
||||
return _line[(long)_line_offset];
|
||||
}
|
||||
|
||||
public float parse_float () throws IOError
|
||||
{
|
||||
float value = 0;
|
||||
char *p = (char *)_line + _line_offset;
|
||||
unowned string end_p = (string)p;
|
||||
|
||||
while (*p != '\0' && p->isspace ())
|
||||
p++;
|
||||
|
||||
float.try_parse ((string)p, out value, out end_p);
|
||||
if ((char *)end_p - (char *)p == 0)
|
||||
throw new IOError.INVALID_DATA ("unexpected character %c in float", end_p[0]);
|
||||
|
||||
_line_offset = (char *)end_p - (char *)_line;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int parse_int () throws IOError
|
||||
{
|
||||
int value = 0;
|
||||
char *p = (char *)_line + _line_offset;
|
||||
unowned string end_p = (string)p;
|
||||
|
||||
while (*p != '\0' && p->isspace ())
|
||||
p++;
|
||||
|
||||
int.try_parse ((string)p, out value, out end_p);
|
||||
if ((char *)end_p - (char *)p == 0)
|
||||
throw new IOError.INVALID_DATA ("unexpected character %c in int", end_p[0]);
|
||||
|
||||
_line_offset = (char *)end_p - (char *)_line;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the rest of the current line after any spaces.
|
||||
*/
|
||||
public string read_string ()
|
||||
{
|
||||
char *p = (char *)_line + _line_offset;
|
||||
|
||||
while (*p != '\0' && p->isspace ())
|
||||
p++;
|
||||
|
||||
char *old_p = p;
|
||||
while (*p != '\0')
|
||||
p++;
|
||||
|
||||
_line_offset = (char *)p - (char *)_line;
|
||||
return (string)old_p;
|
||||
}
|
||||
|
||||
public bool skip_symbol (string to_skip)
|
||||
{
|
||||
char *p = (char *)_line + _line_offset;
|
||||
|
||||
while (*p != '\0' && p->isspace ())
|
||||
p++;
|
||||
|
||||
char *old_p = p;
|
||||
while (*p != '\0' && to_skip[(long)(p - old_p)] == *p)
|
||||
p++;
|
||||
|
||||
if (p - old_p != to_skip.length || *p != '\0' && !p->isspace ())
|
||||
return false;
|
||||
|
||||
_line_offset = p - (char *)_line;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool skip_char (char to_skip)
|
||||
{
|
||||
char *p = (char *)_line + _line_offset;
|
||||
|
||||
if (*p != '\0' && *p == to_skip)
|
||||
p++;
|
||||
else
|
||||
return false;
|
||||
|
||||
_line_offset = p - (char *)_line;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -2,13 +2,18 @@ worldbuilder_sources = [
|
||||
'main.vala',
|
||||
'application.vala',
|
||||
'window.vala',
|
||||
'scene_renderer.vala',
|
||||
|
||||
'core/math.vala',
|
||||
'core/vector3.vala',
|
||||
'core/matrix4x4.vala',
|
||||
'core/vector2.vala',
|
||||
'core/camera.vala'
|
||||
'core/camera.vala',
|
||||
'core/mesh.vala',
|
||||
'core/texture2d.vala',
|
||||
'core/material.vala',
|
||||
'core/model.vala',
|
||||
'core/utf8inputstream.vala',
|
||||
|
||||
'scene_renderer.vala',
|
||||
]
|
||||
|
||||
worldbuilder_deps = [
|
||||
|
||||
+195
-2
@@ -1,5 +1,198 @@
|
||||
using GL;
|
||||
using Worldbuilder.Core;
|
||||
|
||||
public class Worldbuilder.SceneRenderer : Object
|
||||
namespace Worldbuilder
|
||||
{
|
||||
public class SceneRenderer : Object
|
||||
{
|
||||
public GL.Program shader { get; private set; }
|
||||
public GenericSet<Model> scene { get; default = new GenericSet<Model> (null, null); }
|
||||
|
||||
}
|
||||
public Vector3 ambient_light { get; default = Vector3 (0.6f, 0.6f, 0.6f); }
|
||||
public Vector3 light_position { get; default = Vector3 (2.0f, 1.0f, 0.0f); }
|
||||
public Camera camera { get; set; default = new Camera(Vector3(0f, 0.1f, 0.5f)); }
|
||||
|
||||
public SceneRenderer () {}
|
||||
|
||||
public void Setup(Gtk.Widget widget)
|
||||
{
|
||||
print("SceneRenderer / Setting up...\n");
|
||||
var area = (Gtk.GLArea)widget;
|
||||
|
||||
area.make_current();
|
||||
|
||||
if (area.get_error() != null)
|
||||
return;
|
||||
|
||||
shader = GL.Program();
|
||||
|
||||
var vshader = GL.Shader(GL.VERTEX_SHADER);
|
||||
try
|
||||
{
|
||||
vshader.load("/com/brodybrooks/WorldBuilder/shaders/shader.vert");
|
||||
vshader.compile();
|
||||
|
||||
var fshader = GL.Shader(GL.FRAGMENT_SHADER);
|
||||
fshader.load("/com/brodybrooks/WorldBuilder/shaders/shader.frag");
|
||||
fshader.compile();
|
||||
|
||||
shader.attach_shader(vshader);
|
||||
shader.attach_shader(fshader);
|
||||
|
||||
shader.link();
|
||||
}
|
||||
catch (Error e)
|
||||
{
|
||||
printerr("ERROR: %s\n", e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
GL.use_program(shader);
|
||||
shader.set_int("AmbientTex", 0);
|
||||
shader.set_int("DiffuseTex", 1);
|
||||
|
||||
// Load the model
|
||||
try
|
||||
{
|
||||
const string model_dir = "/com/brodybrooks/WorldBuilder/models";
|
||||
|
||||
var sign = new Model("Sign", @"$model_dir/ChessPiecePawnWhite.obj", shader)
|
||||
{
|
||||
position = Vector3(0, 0, 0),
|
||||
scale = Vector3(4, 4, 4)
|
||||
};
|
||||
scene.add(sign);
|
||||
}
|
||||
catch (Error e)
|
||||
{
|
||||
printerr ("ERROR: %s\n", e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
area.add_tick_callback( (widget, frame_clock) => {
|
||||
widget.queue_draw();
|
||||
return Source.CONTINUE;
|
||||
});
|
||||
}
|
||||
|
||||
public bool Render(Gtk.GLArea area, Gdk.GLContext context)
|
||||
{
|
||||
Error? area_err;
|
||||
if ((area_err = area.get_error ()) != null)
|
||||
{
|
||||
printerr ("GL ERROR: %s\nGL Area Error: %s\n",
|
||||
GL.get_error ().to_string (),
|
||||
area_err.message);
|
||||
return Source.REMOVE;
|
||||
}
|
||||
|
||||
// update transformation
|
||||
float aspect_ratio = area.get_width () / (float)area.get_height();
|
||||
var projection_matrix = Matrix4x4.perspective ((float)Math.PI_4, aspect_ratio, 0.1f, 200f);
|
||||
|
||||
// construct view matrix from camera
|
||||
var view_matrix = Matrix4x4.look_at(camera.position, camera.position.add (camera.direction), Vector3 (0, 1, 0));
|
||||
|
||||
area.make_current ();
|
||||
GL.use_program (shader);
|
||||
|
||||
// get view from camera position and rotation
|
||||
shader.set_mat4x4 ("view", ref view_matrix);
|
||||
shader.set_mat4x4 ("proj", ref projection_matrix);
|
||||
|
||||
// ambient color and light position can vary throughout the scene
|
||||
shader.set_vec3 ("AmbientLight", ambient_light);
|
||||
shader.set_vec3 ("LightPosition", light_position);
|
||||
shader.set_vec3 ("CameraPosition", camera.position);
|
||||
|
||||
GL.clear_color(0.05f, 0.05f, 0.05f, 1.0f);
|
||||
GL.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
|
||||
|
||||
scene.foreach (model => render_model (model, Matrix4x4.identity(), shader));
|
||||
|
||||
GL.flush();
|
||||
GL.finish();
|
||||
|
||||
return Source.CONTINUE;
|
||||
}
|
||||
|
||||
public void OnResize(Gtk.Widget widget, int width, int height)
|
||||
{
|
||||
print("SceneRenderer / Resizing...\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a recursive procedure because we want to render each child in a
|
||||
* model hierarchy with a model transform that is relative to the parent
|
||||
* model.
|
||||
*/
|
||||
void render_model (Model model, Matrix4x4 parent_model_matrix, GL.Program program) {
|
||||
// construct a model matrix from the up-to-date transform
|
||||
// attributes of the object
|
||||
var model_matrix = Matrix4x4.identity ()
|
||||
.scale (model.scale.x, model.scale.y, model.scale.z)
|
||||
.rotateX (model.rotation.x/180 * (float)Math.PI)
|
||||
.rotateY (model.rotation.y/180 * (float)Math.PI)
|
||||
.rotateZ (model.rotation.z/180 * (float)Math.PI)
|
||||
.translate (model.position.x, model.position.y, model.position.z);
|
||||
model_matrix = parent_model_matrix.multiply (model_matrix);
|
||||
program.set_mat4x4 ("model", ref model_matrix);
|
||||
|
||||
// render every mesh of the object
|
||||
model.meshes.foreach ((_, mesh) => {
|
||||
// update the material
|
||||
if (mesh.material != null) {
|
||||
// debug (@"mesh has $(mesh.material)");
|
||||
program.set_vec3 ("AmbientColor", mesh.material.ambient_color);
|
||||
if (mesh.material.ambient_texture != null) {
|
||||
program.set_boolean ("HaveAmbientTex", GL.TRUE);
|
||||
GL.active_texture (GL.TEXTURE0);
|
||||
GL.bind_texture (GL.TEXTURE_2D, mesh.material.ambient_texture.id);
|
||||
} else {
|
||||
program.set_boolean ("HaveAmbientTex", GL.FALSE);
|
||||
}
|
||||
program.set_vec3 ("DiffuseColor", mesh.material.diffuse_color);
|
||||
if (mesh.material.diffuse_texture != null) {
|
||||
shader.set_boolean ("HaveDiffuseTex", GL.TRUE);
|
||||
GL.active_texture (GL.TEXTURE1);
|
||||
GL.bind_texture (GL.TEXTURE_2D, mesh.material.diffuse_texture.id);
|
||||
} else {
|
||||
program.set_boolean ("HaveDiffuseTex", GL.FALSE);
|
||||
}
|
||||
program.set_vec3 ("SpecularColor", mesh.material.specular_color);
|
||||
program.set_float ("SpecularCoeff", mesh.material.specular_exponent);
|
||||
if (mesh.material.specular_texture != null) {
|
||||
program.set_boolean ("HaveSpecularTex", GL.TRUE);
|
||||
GL.active_texture (GL.TEXTURE2);
|
||||
GL.bind_texture (GL.TEXTURE_2D, mesh.material.specular_texture.id);
|
||||
} else {
|
||||
program.set_boolean ("HaveSpecularTex", GL.FALSE);
|
||||
}
|
||||
} else {
|
||||
// reset the shader material
|
||||
// GL.active_texture (GL.TEXTURE0);
|
||||
// GL.bind_texture (GL.TEXTURE_2D, 0);
|
||||
// GL.active_texture (GL.TEXTURE1);
|
||||
// GL.bind_texture (GL.TEXTURE_2D, 0);
|
||||
// GL.active_texture (GL.TEXTURE2);
|
||||
// GL.bind_texture (GL.TEXTURE_2D, 0);
|
||||
program.set_vec3 ("AmbientColor", Vector3 ());
|
||||
program.set_vec3 ("DiffuseColor", Vector3 ());
|
||||
program.set_vec3 ("SpecularColor", Vector3 ());
|
||||
program.set_float ("SpecularCoeff", 0f);
|
||||
program.set_boolean ("HaveAmbientTex", GL.FALSE);
|
||||
program.set_boolean ("HaveDiffuseTex", GL.FALSE);
|
||||
program.set_boolean ("HaveSpecularTex", GL.FALSE);
|
||||
}
|
||||
|
||||
// render the mesh vertices
|
||||
GL.bind_vertex_array (mesh.vao);
|
||||
// GL.bind_buffer (GL.ARRAY_BUFFER, mesh.vbo);
|
||||
GL.draw_arrays (GL.TRIANGLES, 0, mesh.size);
|
||||
});
|
||||
|
||||
// now render all children
|
||||
model.children.foreach (child => render_model (child, model_matrix, program));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#version 300 es
|
||||
|
||||
precision mediump float;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
in vec2 TexCoord;
|
||||
in vec3 Normal;
|
||||
in vec3 FragPosition;
|
||||
|
||||
// the color of the ambient light
|
||||
uniform vec3 AmbientLight;
|
||||
|
||||
// position of the global light (TODO: have multiple lights)
|
||||
uniform vec3 LightPosition;
|
||||
// position of the scene camera
|
||||
uniform vec3 CameraPosition;
|
||||
|
||||
// the strengths of the various lighting types
|
||||
uniform vec3 AmbientColor;
|
||||
uniform vec3 DiffuseColor;
|
||||
uniform vec3 SpecularColor;
|
||||
|
||||
uniform float SpecularCoeff;
|
||||
|
||||
// the textures
|
||||
uniform bool HaveAmbientTex;
|
||||
uniform sampler2D AmbientTex;
|
||||
|
||||
uniform bool HaveDiffuseTex;
|
||||
uniform sampler2D DiffuseTex;
|
||||
|
||||
uniform bool HaveSpecularTex;
|
||||
uniform sampler2D SpecularTex;
|
||||
|
||||
// other properties
|
||||
uniform bool Selected;
|
||||
|
||||
void main() {
|
||||
vec3 ambient = AmbientLight * AmbientColor;
|
||||
|
||||
// direction to light
|
||||
vec3 Li = normalize(LightPosition - FragPosition);
|
||||
// direction to camera
|
||||
vec3 v = normalize(CameraPosition - FragPosition);
|
||||
// half vector (bisects Li and V)
|
||||
vec3 h = (v + Li) / length(v + Li);
|
||||
|
||||
// diffuse lighting
|
||||
vec3 diffuse = max(dot(Normal, Li), 0.0) * DiffuseColor;
|
||||
|
||||
// specular lighting
|
||||
vec3 specular = pow(max(dot(Normal, h), 0.0), SpecularCoeff) * SpecularColor;
|
||||
|
||||
// textures
|
||||
if (HaveAmbientTex)
|
||||
ambient *= vec3(texture(AmbientTex, TexCoord));
|
||||
if (HaveDiffuseTex)
|
||||
diffuse *= vec3(texture(DiffuseTex, TexCoord));
|
||||
if (HaveSpecularTex)
|
||||
specular *= vec3(texture(SpecularTex, TexCoord));
|
||||
|
||||
vec3 color = ambient + diffuse + specular;
|
||||
if (Selected) // highlight the edges of the object if it's selected
|
||||
color = mix(color, vec3(1.0, 0.0, 0.0), 1.0 - abs(dot(Normal, v)));
|
||||
outColor = vec4(color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in vec2 texcoord;
|
||||
layout(location = 2) in vec3 normal;
|
||||
|
||||
out vec2 TexCoord;
|
||||
out vec3 Normal;
|
||||
out vec3 FragPosition;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
|
||||
void main() {
|
||||
gl_Position = proj * view * model * vec4(position, 1.0);
|
||||
TexCoord = texcoord;
|
||||
// XXX: calculate transpose-inverse of model on the CPU side
|
||||
Normal = normalize(mat3(transpose(inverse(model))) * normal);
|
||||
FragPosition = vec3(model * vec4(position, 1.0));
|
||||
}
|
||||
+16
-17
@@ -18,23 +18,22 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
[GtkTemplate (ui = "/com/brodybrooks/WorldBuilder/window.ui")]
|
||||
public class Worldbuilder.Window : Adw.ApplicationWindow {
|
||||
[GtkChild]
|
||||
private unowned Gtk.GLArea draw_area;
|
||||
namespace Worldbuilder
|
||||
{
|
||||
[GtkTemplate (ui = "/com/brodybrooks/WorldBuilder/window.ui")]
|
||||
public class Window : Adw.ApplicationWindow {
|
||||
[GtkChild]
|
||||
private unowned Gtk.GLArea draw_area;
|
||||
|
||||
public Window (Gtk.Application app) {
|
||||
Object (application: app);
|
||||
private SceneRenderer renderer;
|
||||
|
||||
draw_area.render.connect(Render);
|
||||
public Window (Gtk.Application app) {
|
||||
Object (application: app);
|
||||
renderer = new SceneRenderer();
|
||||
|
||||
draw_area.realize.connect(renderer.Setup);
|
||||
draw_area.render.connect(renderer.Render);
|
||||
draw_area.resize.connect(renderer.OnResize);
|
||||
}
|
||||
}
|
||||
|
||||
private bool Render (Gtk.GLArea area, Gdk.GLContext context)
|
||||
{
|
||||
// set the background color
|
||||
GL.glClearColor (0.1f, 0.2f, 0.4f, 1.0f);
|
||||
GL.glClear(GL.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,15 @@
|
||||
<file preprocess="xml-stripblanks">window.ui</file>
|
||||
<file preprocess="xml-stripblanks">shortcuts-dialog.ui</file>
|
||||
</gresource>
|
||||
|
||||
<gresource prefix="/com/brodybrooks/WorldBuilder/shaders">
|
||||
<file alias="shader.vert">shaders/shader.vert</file>
|
||||
<file alias="shader.frag">shaders/shader.frag</file>
|
||||
</gresource>
|
||||
|
||||
<gresource prefix="/com/brodybrooks/WorldBuilder/models">
|
||||
<file alias="ChessPiecePawnWhite.obj">../data/ChessPiecePawnWhite.obj</file>
|
||||
<file alias="ChessPiece.mtl">../data/ChessPiece.mtl</file>
|
||||
<file alias="Marble_COL_1K.jpg">../data/Marble_COL_1K.jpg</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
||||
+5071
-9785
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user