Compare commits
7
Commits
5b449506cb
...
d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ff83f6734 | ||
|
|
6e04286cc2 | ||
|
|
f8fc3518dd | ||
|
|
8c109a741b | ||
|
|
5b734c315d | ||
|
|
8170593d56 | ||
|
|
e6c943aab4 |
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
/fart
|
/fart
|
||||||
*.bmp
|
*.bmp
|
||||||
*.png
|
*.png
|
||||||
|
/.propane*
|
||||||
/.rscons*
|
/.rscons*
|
||||||
/build/
|
/build/
|
||||||
+21
-3
@@ -2,10 +2,28 @@ configure do
|
|||||||
check_d_compiler
|
check_d_compiler
|
||||||
end
|
end
|
||||||
|
|
||||||
env do |env|
|
fart_env = env "fart" do |env|
|
||||||
env["DFLAGS"] += %w[-Werror -O2]
|
env["DFLAGS"] += %w[-Werror -O2]
|
||||||
env["D_IMPORT_PATH"] += %w[src]
|
env["D_IMPORT_PATH"] += %w[src]
|
||||||
|
|
||||||
sources = glob("src/**/*.d")
|
env.Command("^/src/sceneparser.d", "src/sceneparser.propane",
|
||||||
env.Program("fart", sources)
|
"CMD" => %w[./propane ${_SOURCES} ${_TARGET}],
|
||||||
|
"CMD_DESC" => "Generating scene parser")
|
||||||
|
env.add_build_hook do |builder|
|
||||||
|
if builder.sources.first =~ /sceneparser\.d/
|
||||||
|
builder.vars["DFLAGS"] -= %w[-Werror]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env["sources"] = glob("src/**/*.d") + ["^/src/sceneparser.d"]
|
||||||
|
env.Program("fart", "${sources}")
|
||||||
|
end
|
||||||
|
|
||||||
|
task "test" do
|
||||||
|
test_env = fart_env.clone "test" do |env|
|
||||||
|
env["DFLAGS"] += %w[-funittest]
|
||||||
|
env.Program("^/farttest", "${sources}")
|
||||||
|
end
|
||||||
|
test_env.process
|
||||||
|
sh test_env.expand("^/farttest")
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010-2023 Josh Holtrop
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
script = File.join(File.dirname(__FILE__), ".propane-1.0.0-5dab693259ff5ad86cc8d86fdd20c552.rb")
|
||||||
|
unless File.exist?(script)
|
||||||
|
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
|
||||||
|
require "zlib"
|
||||||
|
require "base64"
|
||||||
|
encoded_compressed = $1
|
||||||
|
unescaped_compressed = Base64.decode64(encoded_compressed)
|
||||||
|
inflated = Zlib::Inflate.inflate(unescaped_compressed)
|
||||||
|
File.open(script, "wb") do |fh|
|
||||||
|
fh.write(inflated)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Could not decompress."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
load script
|
||||||
|
if __FILE__ == $0
|
||||||
|
Propane::CLI.new.run(ARGV)
|
||||||
|
end
|
||||||
|
#==>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 truncated
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
module fart.color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure to represent a RGBA color value.
|
||||||
|
*/
|
||||||
|
struct Color
|
||||||
|
{
|
||||||
|
/** Red color component. */
|
||||||
|
double r;
|
||||||
|
|
||||||
|
/** Green color component. */
|
||||||
|
double g;
|
||||||
|
|
||||||
|
/** Blue color component. */
|
||||||
|
double b;
|
||||||
|
|
||||||
|
/** Alpha color component. */
|
||||||
|
double a;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert color to a 32-bit integer RGBA value.
|
||||||
|
*
|
||||||
|
* Each color component uses one byte.
|
||||||
|
*/
|
||||||
|
public ubyte[] rgba32() const
|
||||||
|
{
|
||||||
|
return [toubyte(r), toubyte(g), toubyte(b), toubyte(a)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale a floating point color component value to an unsigned 8-bit byte
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param v
|
||||||
|
* Floating point color component value.
|
||||||
|
*
|
||||||
|
* @return Unsigned 8-bit byte value for the color component.
|
||||||
|
*/
|
||||||
|
private static ubyte toubyte(double v)
|
||||||
|
{
|
||||||
|
return cast(ubyte)(0xFF * v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static immutable Color TRANSPARENT = Color(0, 0, 0, 0);
|
||||||
|
}
|
||||||
@@ -1,4 +1,27 @@
|
|||||||
|
module fart.main;
|
||||||
|
|
||||||
|
import fart.png;
|
||||||
|
import fart.color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main program entry point.
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* Program arguments.
|
||||||
|
*/
|
||||||
int main(string[] args)
|
int main(string[] args)
|
||||||
{
|
{
|
||||||
|
size_t width = 800;
|
||||||
|
size_t height = 600;
|
||||||
|
string output_filename = "out.png";
|
||||||
|
Color[] pixels = [];
|
||||||
|
for (size_t y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (size_t x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
pixels ~= Color.TRANSPARENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_png(output_filename, width, height, pixels);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
module fart.png;
|
||||||
|
|
||||||
|
import std.zlib;
|
||||||
|
import std.digest;
|
||||||
|
|
||||||
|
import fart.bfile;
|
||||||
|
import fart.crc32;
|
||||||
|
import fart.hton;
|
||||||
|
import fart.color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNG file header.
|
||||||
|
*/
|
||||||
|
private immutable ubyte[] HEADER = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNG IHDR chunk.
|
||||||
|
*/
|
||||||
|
struct IHDR
|
||||||
|
{
|
||||||
|
uint width;
|
||||||
|
uint height;
|
||||||
|
ubyte bit_depth;
|
||||||
|
ubyte color_type;
|
||||||
|
ubyte compression;
|
||||||
|
ubyte filter;
|
||||||
|
ubyte interlace;
|
||||||
|
ubyte[0] end;
|
||||||
|
|
||||||
|
this(size_t width, size_t height)
|
||||||
|
{
|
||||||
|
this.width = htonl(cast(uint)width);
|
||||||
|
this.height = htonl(cast(uint)height);
|
||||||
|
this.bit_depth = 8;
|
||||||
|
this.color_type = 6;
|
||||||
|
this.compression = 0;
|
||||||
|
this.filter = 0;
|
||||||
|
this.interlace = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const(void) * data()
|
||||||
|
{
|
||||||
|
return &this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public size_t data_length()
|
||||||
|
{
|
||||||
|
return end.offsetof;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNG IDAT chunk.
|
||||||
|
*/
|
||||||
|
struct IDAT
|
||||||
|
{
|
||||||
|
private const(ubyte)[] m_data;
|
||||||
|
|
||||||
|
this(ubyte[] data)
|
||||||
|
{
|
||||||
|
m_data = compress(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public const(void) * data()
|
||||||
|
{
|
||||||
|
return m_data.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public size_t data_length()
|
||||||
|
{
|
||||||
|
return m_data.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNG IEND chunk.
|
||||||
|
*/
|
||||||
|
struct IEND
|
||||||
|
{
|
||||||
|
public const(void) * data()
|
||||||
|
{
|
||||||
|
return &this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public size_t data_length()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a PNG chunk to the output file.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* Output file
|
||||||
|
* @param chunk_type
|
||||||
|
* Chunk type.
|
||||||
|
* @param chunk
|
||||||
|
* Chunk to write.
|
||||||
|
*/
|
||||||
|
private void write_chunk(Chunk)(BFile file, string chunk_type, Chunk chunk)
|
||||||
|
{
|
||||||
|
size_t chunk_data_length = chunk.data_length();
|
||||||
|
uint chunk_length_be32 = htonl(cast(uint)chunk_data_length);
|
||||||
|
file.writeObject(chunk_length_be32);
|
||||||
|
file.write(chunk_type);
|
||||||
|
const(ubyte)[] chunk_data = (cast(const(ubyte) *)chunk.data())[0..chunk_data_length];
|
||||||
|
file.write(chunk_data);
|
||||||
|
uint crc_be32 = htonl(crc32(chunk_type, chunk_data));
|
||||||
|
file.writeObject(crc_be32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a PNG image from the given metadata and pixel data.
|
||||||
|
*
|
||||||
|
* @param filename
|
||||||
|
* Name of file to write.
|
||||||
|
* @param width
|
||||||
|
* Width of image.
|
||||||
|
* @param height
|
||||||
|
* Height of image.
|
||||||
|
* @param data
|
||||||
|
* Pixel color data.
|
||||||
|
*/
|
||||||
|
public void write_png(string filename, size_t width, size_t height, const(Color)[] data)
|
||||||
|
{
|
||||||
|
if (data.length != width * height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Convert Color values to 32-bit RGBA values. */
|
||||||
|
ubyte[] pixel_data;
|
||||||
|
size_t data_index;
|
||||||
|
for (size_t y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
/* Filter method 0 (None) */
|
||||||
|
pixel_data ~= 0;
|
||||||
|
for (size_t x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
pixel_data ~= data[data_index++].rgba32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open output file. */
|
||||||
|
BFile file = BFile(filename);
|
||||||
|
/* Write PNG header. */
|
||||||
|
file.write(HEADER);
|
||||||
|
/* Write IHDR chunk. */
|
||||||
|
IHDR ihdr = IHDR(width, height);
|
||||||
|
write_chunk(file, "IHDR", ihdr);
|
||||||
|
/* Write IDAT chunk. */
|
||||||
|
IDAT idat = IDAT(pixel_data);
|
||||||
|
write_chunk(file, "IDAT", idat);
|
||||||
|
/* Write IEND chunk. */
|
||||||
|
IEND iend;
|
||||||
|
write_chunk(file, "IEND", iend);
|
||||||
|
/* Close output file. */
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
module fart.vector;
|
||||||
|
|
||||||
|
import std.math;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure to represent a 3-dimensional vector.
|
||||||
|
*/
|
||||||
|
struct Vector
|
||||||
|
{
|
||||||
|
/** Vector X coordinate. */
|
||||||
|
double x;
|
||||||
|
|
||||||
|
/** Vector Y coordinate. */
|
||||||
|
double y;
|
||||||
|
|
||||||
|
/** Vector Z coordinate. */
|
||||||
|
double z;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the cross product of two vectors.
|
||||||
|
*/
|
||||||
|
public Vector cross()(auto ref const Vector other) const
|
||||||
|
{
|
||||||
|
return Vector(y * other.z - z * other.y,
|
||||||
|
z * other.x - x * other.z,
|
||||||
|
x * other.y - y * other.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the dot product of two vectors.
|
||||||
|
*/
|
||||||
|
public double dot()(auto ref const Vector other) const
|
||||||
|
{
|
||||||
|
return x * other.x + y * other.y + z * other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the magnitude of the vector.
|
||||||
|
*/
|
||||||
|
public double mag() const
|
||||||
|
{
|
||||||
|
return sqrt(mag2());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the squared magnitude of the vector.
|
||||||
|
*/
|
||||||
|
public double mag2() const
|
||||||
|
{
|
||||||
|
return x * x + y * y + z * z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize the vector to have unit length.
|
||||||
|
*/
|
||||||
|
public ref Vector normalize()
|
||||||
|
{
|
||||||
|
double mag = mag();
|
||||||
|
x /= mag;
|
||||||
|
y /= mag;
|
||||||
|
z /= mag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a normalized vector in the same direction as this.
|
||||||
|
*/
|
||||||
|
public Vector normalized() const
|
||||||
|
{
|
||||||
|
double mag = mag();
|
||||||
|
return Vector(x / mag, y / mag, z / mag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project the vector onto a target vector.
|
||||||
|
*/
|
||||||
|
public Vector proj()(auto ref const Vector other) const
|
||||||
|
{
|
||||||
|
Vector on = other.normalized();
|
||||||
|
return on * dot(on);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add two vectors.
|
||||||
|
*/
|
||||||
|
public Vector opBinary(string op : "+")(auto ref const Vector other) const
|
||||||
|
{
|
||||||
|
return Vector(x + other.x, y + other.y, z + other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add two vectors and assign to self.
|
||||||
|
*/
|
||||||
|
public Vector opOpAssign(string op : "+")(auto ref const Vector other)
|
||||||
|
{
|
||||||
|
x += other.x;
|
||||||
|
y += other.y;
|
||||||
|
z += other.z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Negate a vector.
|
||||||
|
*/
|
||||||
|
public Vector opUnary(string op : "-")() const
|
||||||
|
{
|
||||||
|
return Vector(-x, -y, -z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract two vectors.
|
||||||
|
*/
|
||||||
|
public Vector opBinary(string op : "-")(auto ref const Vector other) const
|
||||||
|
{
|
||||||
|
return Vector(x - other.x, y - other.y, z - other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract two vectors and assign to self.
|
||||||
|
*/
|
||||||
|
public Vector opOpAssign(string op : "-")(auto ref const Vector other)
|
||||||
|
{
|
||||||
|
x -= other.x;
|
||||||
|
y -= other.y;
|
||||||
|
z -= other.z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale a vector.
|
||||||
|
*/
|
||||||
|
public Vector opBinary(string op : "*")(double m) const
|
||||||
|
{
|
||||||
|
return Vector(x * m, y * m, z * m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale a vector.
|
||||||
|
*/
|
||||||
|
public Vector opBinaryRight(string op : "*")(double m) const
|
||||||
|
{
|
||||||
|
return Vector(x * m, y * m, z * m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the cross product of two vectors.
|
||||||
|
*/
|
||||||
|
public Vector opBinary(string op : "*")(auto ref const Vector other) const
|
||||||
|
{
|
||||||
|
return cross(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static immutable Vector X = Vector(1.0, 0.0, 0.0);
|
||||||
|
public static immutable Vector Y = Vector(0.0, 1.0, 0.0);
|
||||||
|
public static immutable Vector Z = Vector(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Vector v = Vector(1, 2, 3);
|
||||||
|
const Vector w = Vector(0, 4, 5);
|
||||||
|
|
||||||
|
Vector v_times_2 = v * 2;
|
||||||
|
assert(v_times_2.x == 2);
|
||||||
|
assert(v_times_2.y == 4);
|
||||||
|
assert(v_times_2.z == 6);
|
||||||
|
|
||||||
|
Vector v_plus_w = v + w;
|
||||||
|
assert(v_plus_w == Vector(1, 6, 8));
|
||||||
|
|
||||||
|
Vector v_minus_w = v - w;
|
||||||
|
assert(v_minus_w == Vector(1, -2, -2));
|
||||||
|
|
||||||
|
assert(v_minus_w.mag2() == 9.0);
|
||||||
|
assert(v_minus_w.mag() == 3.0);
|
||||||
|
|
||||||
|
v += v_minus_w;
|
||||||
|
assert(v == 2 * Vector.X + 0 * Vector.Y + Vector.Z);
|
||||||
|
|
||||||
|
assert(Vector.X * Vector.Y == Vector.Z);
|
||||||
|
assert(Vector.Y * Vector.Z == Vector.X);
|
||||||
|
assert(Vector.Z * Vector.X == Vector.Y);
|
||||||
|
assert(Vector.Y * Vector.X == -Vector.Z);
|
||||||
|
assert(Vector.Z * Vector.Y == -Vector.X);
|
||||||
|
assert(Vector.X * Vector.Z == -Vector.Y);
|
||||||
|
|
||||||
|
assert(w.proj(Vector.X) == Vector(0, 0, 0));
|
||||||
|
assert(w.proj(Vector.Y) == Vector(0, 4, 0));
|
||||||
|
assert(w.proj(Vector.Z) == Vector(0, 0, 5));
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<<
|
||||||
|
import std.conv;
|
||||||
|
>>
|
||||||
|
|
||||||
|
token background;
|
||||||
|
token comma /,/;
|
||||||
|
token number /\d+(\.\d+)?/;
|
||||||
|
token lt /</;
|
||||||
|
token gt />/;
|
||||||
|
token semicolon /;/;
|
||||||
|
|
||||||
|
Start -> Statements;
|
||||||
|
|
||||||
|
Statements -> ;
|
||||||
|
|
||||||
|
Statements -> Statement Statements;
|
||||||
|
|
||||||
|
Statement -> BackgroundStatement;
|
||||||
|
|
||||||
|
BackgroundStatement -> background Vec3 semicolon;
|
||||||
|
|
||||||
|
Vec3 -> lt number comma number comma number gt;
|
||||||
Reference in new issue
Block a user