Compare commits

...
31 Commits
Author SHA1 Message Date
josh 0ba0ebfe71 Load scene render params 2026-11-02 22:28:18 -05:00
josh a3684b20b6 Add Parser::build_scene() 2026-11-02 22:12:34 -05:00
josh c1c8c46ac7 Add background RenderParams entry 2026-11-02 22:07:22 -05:00
josh 0a979c487c Invoke propane to parse input file 2026-11-02 22:05:01 -05:00
josh 88bfc29323 Add CLI option to set number of render threads 2026-11-02 21:47:42 -05:00
josh e964908e46 Add vector to common.h 2026-11-02 21:09:07 -05:00
josh bf09c094e1 Make RenderParams fields optional 2026-11-02 21:04:11 -05:00
josh 1475434c4c Add read_file 2026-11-02 20:19:20 -05:00
josh e13608b287 Add Parser and Scene classes 2026-11-02 18:43:39 -05:00
josh c8f6d36bdc Verify that output file can be written before rendering 2026-11-01 20:38:25 -05:00
josh 6b77b1a666 Build an ImageWriter 2026-11-01 20:20:31 -05:00
josh d7394027b1 default the output file name 2026-11-01 16:05:50 -05:00
josh 68d5b39cfd Error if no input file given 2026-11-01 15:43:30 -05:00
josh 0d36f13b17 Add RenderParams constructor 2026-11-01 15:39:57 -05:00
josh 88d482dcb3 Move input file / output file out of RenderParams 2026-11-01 15:26:46 -05:00
josh d71be68fae Add auto-included fart/common.h 2026-11-01 14:18:02 -05:00
josh 1947ea553c Rename Options to RenderParams 2026-11-01 13:20:59 -05:00
josh 7eaf5f9555 Add background grammar rule 2026-11-01 13:17:32 -05:00
josh d761433382 Add Number and Vector to grammar 2026-10-30 19:57:52 -04:00
josh e3d4c88f66 Add propane and initial skeleton grammar 2026-10-30 19:36:59 -04:00
josh 94105a883f Pass options to Engine; add Engine::run() 2026-10-30 19:06:22 -04:00
josh c05f2b1b59 Cli: validate that width and height arguments are numbers 2026-10-30 13:55:08 -04:00
josh cc041168a1 Parse command-line arguments 2026-10-30 13:50:11 -04:00
josh 05dfa4f977 Add Cli class 2026-10-30 10:26:56 -04:00
josh 8edec7e28a Add Engine 2026-10-30 10:25:57 -04:00
josh 0319619724 Move main.cpp 2026-10-29 08:38:30 -04:00
josh d764740b54 Make ImageWriter an abstract base class; add PngImageWriter 2026-10-29 08:37:33 -04:00
josh a233b746c2 Add ImageWriter 2026-10-28 21:28:28 -04:00
josh 917bfe4821 Use vector for pixels in Image 2026-10-28 21:16:22 -04:00
josh 6849d583ae Add Color and Image 2026-10-28 15:11:33 -04:00
josh 6d102b8de9 Start anew 2026-10-26 23:59:58 -04:00
101 changed files with 1383 additions and 16526 deletions

No files matched your search

+2 -1
View File
@@ -1,5 +1,6 @@
fart
*.bmp
*.png
/.propane*
/.rscons*
/build/
/fart
+20 -12
View File
@@ -1,19 +1,27 @@
configure do
check_cxx_compiler
check_program "flex"
check_program "bison"
check_lib "pthread"
check_cxx_compiler "clang++"
check_cfg package: "libpng"
end
class Propane < Builder
def run(options)
@sources += ["propane"] unless @sources.include?("propane")
if @command
finalize_command
else
command = %W[./propane -w --log #{@target}.log #{@sources.first} #{@target}]
standard_command("Propane <target>#{@target}<reset>", command, options)
end
end
end
env do |env|
env["CCFLAGS"] += %w[-Wall -O2]
env["CPPPATH"] += glob("src/**")
env.add_builder(Propane)
env["CCFLAGS"] += %w[-Wall -O2 -std=c++23 -include fart/common.h]
env["CPPPATH"] += %W[src #{env.build_root}/src]
env.CFile("^/parser/lexer.cc", "src/parser/parser.ll")
env.CFile("^/parser/parser.cc", "src/parser/parser.yy")
env["CPPPATH"] += ["#{env.build_root}/parser"]
sources = glob("src/**/*.cc")
sources += ["^/parser/lexer.cc", "^/parser/parser.cc"]
env.Propane("^/src/fart/propane.cpp", "src/fart.propane")
env.barrier
sources = glob("src/**/*.cpp") + ["#{env.build_root}/src/fart/propane.cpp"]
env.Program("fart", sources)
end
File renamed without changes.
View File
File renamed without changes.
Executable
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env ruby
# The MIT License (MIT)
#
# Copyright (c) 2010-2026 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-5.2.0-58c963344c9bc893689cbae5e4c49b1b.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
+500 -8
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
# Copyright (c) 2013-2022 Josh Holtrop
# Copyright (c) 2013-2025 Josh Holtrop
#
# MIT License
#
@@ -24,23 +24,515 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
script = File.join(File.dirname(__FILE__), ".rscons-3.1.0-4892f95d56c30bbe9aa0c9964062ec5b.rb")
BASE64CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def base64_decode(s)
out = ""
v = 0
bits = 0
s.each_char do |c|
if cv = BASE64CHARS.index(c)
v = (v << 6) | cv
bits += 6
elsif c == "="
break
end
if bits >= 8
out += (v >> (bits - 8)).chr
v &= 0xFFFFFFFF >> (32 - (bits - 8))
bits -= 8
end
end
out
end
script = File.join(File.dirname(__FILE__), ".rscons-3.4.2-b1e276fdbe6b603498fe893d904fe368.rb")
unless File.exist?(script)
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/m
require "zlib"
require "base64"
encoded_compressed = $1
unescaped_compressed = Base64.decode64(encoded_compressed)
inflated = Zlib::Inflate.inflate(unescaped_compressed)
compressed = base64_decode(encoded_compressed)
if ENV["rscons_dist_specs"]
require "digest/md5"
if Digest::MD5.hexdigest(compressed) != "b1e276fdbe6b603498fe893d904fe368"
raise "Hash mismatch when decompressing rscons executable"
end
end
inflated = Zlib::Inflate.inflate(compressed)
File.open(script, "wb") do |fh|
fh.write(inflated)
end
else
raise "Could not decompress."
raise "Error expanding rscons executable"
end
end
load script
if __FILE__ == $0
Rscons::Cli.new.run(ARGV)
end
#==>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 truncated
#==>
#eJztvWt7GzmOKPy9f0WlkulIji0nPTu776NO2k5spzt7nMuxnT6Zo3g0slSya6Lb
#Vkl2fBztbz8AeANIVkl2knl79zmZ6UTFCwiCIAiCIHj/3vaiLLbP8sl2NrlMisXZ
#9Q9F9h+LvMiSdJLNty/m81nqkgb5eVbO0x/G08FilCVHZX86KX9IEv39fFLm8JUk
#/VGvLJOnT5MyGw0pJUmODo4PTpJnSfox6zwepzp1kA2TqyKfZ418uplsjLOy7J1n
#TZ0L+dNufzqaFlBRtdbqzWajvN+b59NJC3J70KYqYuvkQ1utNclHOzZDgNN1d6Bh
#11w2Gdjf08Uc0U1jcBnI/qIossncoYn95E1mJdBuUJmvu9zKev0LgJ98GX9hudTq
#uJWX3d5O43he5JPzpshOklE+yUoAPG6VQJl5I/04STeTrSfRctRM9yqfX3TzySD7
#TC1izmaSf/EqUNuYl9wTdBD5svtQUPQ3UkcR9tE6BX3SrqrBRy9oDjsS5MZqQKfy
#5KkmV5n/nyzZSp6s2Xl/cAMsqgus4qO1Ooljv0Yn/ZRsVPq06ffKLBl7iVcX2SRp
#wxD8zFN9DqcZ/vOfn4x9VFT18yLLJj+vrP5TRfXrbDSaXv28qvqfK6qfjRYZx76i
#+r9UVB/DVJ3Mez+vqP6Xiur9695kjdb/taL61QVIytWk+7eqvk9HAz54sepPqgcO
#q/PBq6heNXBYnQ9eRfXKgYPqfPAqqlcNHFbng1dRvWrgsDofvIrqVQOH1fngVVSv
#G7hVbFM1akVWZvManonJmUA+iG/5tZ4YrBB/HJQnhL56XdRNjtfsCf+dT1tKKQEY
#BqzLnxX5ZW9ucEUNJqpLAIJH71/8tfvu8PnJy7dHr5Nn/5lsjwHRqy/j8rrcZng0
#Dt783klPDo5ep6fJMxjPz/OsGKfN5Mcfkz9ddYb5cJr0L3pFrw/px7Osn/dGp618
#0h8tBhm22irnvXlrOL+eMd3JIyiUms+vdyIdNr/Uv/g3/me1PuCfUOVTKt5zp4wR
#hN58XnSLrDfIiqQN2OaXWfeyV+S9ybx0BXr9PgwusEr7bJGPBt1BXsQyQ+3OKzDp
#zi+wsTJsu+wX+Wweq1TmI8Wrk2F+viiyWJnLrDibauLh+IKulM+B5qAMaJLt+mBg
#Js2Lhc22HYN0Gtyj4723b467L96/Otzv7r86goH+8iVJqZyZuFCwNchGGTBeWN6M
#667tNYB+P89HUAW5BdSVriQIlNW0754X08UMK3RO2Uirzs175adu9jnrL3AYu7ML
#WPkNl+zGMgMIxWLSKIgxiOqbBBJm5mTQnQHTjsvNpLyYXnUpeTPpjUbmZzbpnY1A
#GBkeMZ3cg0mfAYMDW0/6WScd9vIRyazxGKCWOEtMVzSSvDkcCy/JFPUbhKJ+0g92
#+oaFUQ/GSXnPh+8mY2pZIhWyoI9d6kba9/tq6tOs6g5681562kn9iukpm9qxbsXb
#q5n9yNQ0flD5mH60JtmVowYM4QJ4ejTtDbpserGKLczjnMA3VoiHj4ZrWsHXLGJr
#UZICpXIalncc6CKbL4pJ8jiAiTvFayqtB6kx7IFIbNbxtpzG+TBgpFY2nnEpCkTu
#A/xiirIDyH2ZF9PJOOOkBignAKVFoDopzJjeYjQXA4j5LKeloCrcstiYcdEeomiW
#TOrhpDeG3Z3K+uK3aUus0ab99TiQAPHhXUxGQJRk16YbduSkCXnX321mn2ElgYG5
#14jwOaypf2t8fPRlq7nNFYDIhAgqm/3yZsprlri8DnNZ92ZZA9vRW5JB6isyjyO+
#02htNB9sx0oP8hL/3UyMIMehAnwePNlMHvwkaoR4d3ilUyvpDFCi85YgtFSO0FJg
#0MXeRbuFEzdo2UlDndJpEw6+imZzNUkRyep+aCCnklCjEiU1ssgawEkA3EEhDFlY
#CgpXVs+imNjlE4YkG65qRgNJEm+Ni6wMwSoYrxm18ehMYeZBe51WdB+U8wEou5u0
#u99M0pcEMtG1ksb9mzx5lDxZbt+/8VtDE8my2U439V5jk6wQbqg16NZsAVOJFBZd
#tTufdsuG/vDVbE6s/ijrTQx9kSYBbXgmyNniPJsbac+lIaa77qvSRTaegpaqMhvq
#H4f8S+gr4ly2inF36GWzUSdQoO5l/fm0yDPbOKiT8+TmS28zOfuSnClz0tNnvyQ9
#RTaHnKl7XYGfzW/YXw7LSfZ5bqQtYmwxud6JFYcJ09jPixasUoSrK5JsJZ2HrYeb
#ycNW6+Fp01/skgSrFWOowCvBsPcXWTLJR7XLhuoP8VswxCCT5usPs1+whSnGIiiG
#rBg2nC7eDDmLZqvcC8QUB5zqbtRr1rr1lDnb4IBpAbgWaj2qt5hPsQwjvI9j9f6N
#96xGL7GLsy4DUzqqvRy4xM4pX+ou+UIAny0NqEKAxpuJi1HREa/jFm9HRNaKS5SQ
#kYDITib77QyV28aur6eeZef5hPXLjIiFC/KKq57E+GpTfFAUaFD5JcnEsAFvwlZS
#qp5Ut5eDuhkbznD7s+6WIKbl/BCgIpk5pPhiQi2PzGy+hcgl0F8ncQNZln0G2bAT
#lKPlJD1CWPnkPLl/owosU5xIfAOPf+4sxlWH/rtJ8YB2tm6cfN9P7msubahNynSG
#DK4UbtNRFIsxSxL+uXfPz2oNQeWBoTK6aiJ1R9zB4w+jzot9FLYkDSYh5UR+dO66
#PuhfrQErqdMsOoRNmMtUVgNlmM37F4223iNuJnwWs5qfsutILUjdpKZcDb+ro14J
#VDJ0PMXTWw2kRlQYGBpfq+93eVurNxL5MDJOHBSXm2q7H2j3tSzo9P4Y9kSBxgYI
#hXIz+fFsNO1/YtyHycBWRTmvMDfrPRmVK0d5P7vXeBzKFscXDiCaAO3W0jfWuQFo
#jTOQVzRH2omaKWac2knnlOFKuLvFDL+smSw2frRJMLAaQdcrJ55avFJRHeb5ZDKd
#wxIKv0a4e8DsfJL0IPUiK2RjCrk0INPqSbh+28lkagmF3c0n2SDS4jwrupfnODKj
#3vhs0CMeVam6suBUkaPWAuBWH+nS2YppcD1KQmMSDmyDZnIPK0evAoiQvXJv62HU
#CfE+ZfPdrWFV2/zqGSfpKBuCXaPZv/s79zV23uEodTqn0oTNT2Oc4luhBQWs5XVa
#pX9hmCgw/ekCbTZSYnDqqQIV5FtHVgmDs27umTVq4h99IIbHfW8YXxt7xBCUTyNk
#CT1u2cmHPuokZsV4GPjoRgAqQaw8Kn8zWP2X1VYjNTuZRkx6duDXU7954SvGHUeu
#cqTq+Pw6z0aDRDa8Ctc1bNRK3UJ7a9k2lCPr6ylpllF7Lf4UYgcTWoNMtQSrAq4d
#tiVft/tTJ4EhLGFuTObDRvqnrZ/+rUwVULU0L0lhlhCXnB8o07cn0zc3KEshAQtb
#d45KLBoWt+7fuOJLeTYN3aE82Dd8ymBNLc49WWIhITM+45Baixm6pngA/QNrSwSf
#DH9BMqT3b0wDy5RIobCposUq2WS2pPXr1m052PDNx8nvfFrHZ2ayA5P2YTxv+TBN
#2kmaLtucaHeaIAanxAkI0whLsNorItXQX02NRJXoqJ9loUlmOoEEuy0XhzorD0MY
#HNc7PYoSLh722YpkMXfHe95sCRoMZguItNEiCxZWyoM54x/IKHgdB8Zfcf0KZTbv
#soYaFQhIK3zE9ysuw9P3k0+T6dVEgcIz50RMTLM20EqEWGkRowVAs2bCrs8HlYeQ
#eluAuyLSIevP7bzSd7baaTi0vaANedgubL1K2JD/6apjbZzKgOnsLP4W3dqtpENG
#3AXjRa/M+8xIZ0nlfBYasX01iiNgGEgD8XKc0WFvQ8Fut/cPXj5/f3jSRR+Ek6P3
#eyev3r7p/v786NXzF4cHxwYEjUJIq3umPVHOTRG1soNMKO8F49s5bQDBPBw7uKkN
#ip4Dt0N+pHwryJJNPGvQfhgmQ6whUvNGMQkEnw2aQaXfYCRT6DOfZ8gRULjIhg31
#zyakzoseJmpF3gBVKUIvtCV9h2aNQN0eAPFTG0gHJqaIqc0PLsbkKJJNLtvkv02b
#Tdho4t+nDMVWvFcMjCQEsGKv7ILguJgOjOePPZVqC2CWcDNEvzsc9c7LBv3NNqf0
#rc5R73knqDoLpEU2GjUePAl348UlP9W9mhZ04HaM5elDnw2LNstP+cw7Q1RlY+dt
#mCMO2/CgFACwwYnAwz9oBoxuEBSTyW0q0IsYmS8qxSXuuNC/iDnJ8IxHjsP9Ni5g
#EEbZ6jZoQzEibkxo4zEKbWXIJiMARUTq0PmhXME0AYSZPuJeiFDwEBI3+BUNKdJ0
#CFP4e3S6Wn0j0IgaefVs9Yr+RbBjqsVet5nu7b08fP7rMeiVHQ2GqHUaL324v7q0
#pECMSurUW2GPE+D+jRF56d67dyC93x0dvHz1AZTbwLdAjXBHl3v15gBxecD6xmEj
#ZfRZ/jcgjoH0tT0mrMrrsphO598CLQNq7XGrqfFVY/fqzd5aA/fu+clvNaPmQT18
#9eLw1Zv/sRoyFKxjhhDs/qujtaCuwBfHczyZbvWvz69ycYEhMl5YRVA8MkJemaAt
#aGUwvSpjLa0PZaZ8Lb8vukjzrf/V2wyoa8A8PzZgHjxhDk110EbV0BxSa0ObVUMD
#Tl0bHHowF3NaRXca6ZbYLqwmaxWcR7eEs+bwZLEKavIEpSP2guIy0BCZonNPah1G
#l6lUhUi3i2XQR9BSfXGly8x6eUFLv9GTpRoDSqFQsfW5yPOi6F03aanGZZoncpEI
#tZWe7ashkq6uXJW2EtsfDhZjo2Ip1bd70SsvkmdWE55PuxdhAdqlkTGue3bd+LGN
#nklNYQkRlsVuOS+UNdgcCV2Pz6ajZrKj0tROOCGd2daLelvhnZJNA5J5UKEfAVlU
#DIakudk9NnOwilFh3Y2ZMguv74SMhaXHij6ONLsY/KeTdk1q6s0sZlnhWHEg8buY
#0JI94VyUwhBCDNYQuWzk4DOwt0CawNcDWI+rqfxtzBnV7fA2cjNrBZUpiVFY70Fd
#yXDXk5ttrISk0hgoLiNYhShAJZcEPEpi4FwRYRyDtJKOUoXNkwtCXmjdswO15/O8
#Cimx2x8P5L6akgFtq9Mc/3ZweCjWDMQkKIFLQ9pEC3xDg0DD4HiQosVzu0+Wzq2+
#gNOhgpsEr3It0VecsTHQMOfZuEu1Ij1/9bY1m8LANDobtm+bxl3ztKmOMKdMbE9b
#qKyskhnVRhp7YrH6sMIdHSh74+1MoiT3U306QabDtjxJOFWLoam7ki2M+czeZxrm
#o2yBzkCVF9lfoHEiK8qfNQhtaVOpBDp2w11ZetDEMuvNL4K5gtnCl6FczLLCqEXt
#tEkuGBUjJC43KfelML13VlZlldNF0c8iF6WgTmUeOe6Eydnk0iWyS1MwWSMN54Os
#mw2HsGrV3AADXp9ZTgwNl8y6j91z/i0dTQt7FciSwFyOgoTpaDHPaEwau9Lza1d3
#HQp7y4jOsL4Vu45OqEzon61xb0Z8rb4dG0eaVkVCEbpr3Ots25Rge4SBGZ5xz6BL
#m6WFrs3jFrpdTng0bWXuOg+f9owjSa8jxm7ZVKFbZbgSlI0NWAMLWONnblzQ/dNk
#axpvJn45oe8WMNOAgo0NjqcV1yzNCQ+W6ijNErvKhkiun7GxRyRVkQar1WQuUVii
#yM7zEp0NWJlGrBVWT9AahUKkeI3oxftzHqdrJ5eTi7xMtMl0vCjJwWV6mRVFPhhk
#kwSda0CMnNGgpREi55N5F6B39fk7rFao4rJzTrtmiKFUFc+UvJMAyCC8CozonKGn
#LlGDQ9wB0NwW4HlO/XAzllRqt7R61aCAWlH1N64iN1ChHUBYBlN0LTrueoS86uXz
#7nBaNPZUOp2v2I4qOno4NkPqoT4+6BWDO1PPoIU3AtUvM8esNAuEHio2u3xVUG5Z
#yo9zMcN7G6CkZjtuqu+6jqlqm8RIbopwJ2+u8+hDV634zC6mk2vtGLzjC2vVG8Jh
#/GmQF91Zw3jJoriyxXl5z+83gMg19XUYddendd0ubJhPaBWzAGPjc7th2DXexBpV
#mqJrD4N3rqhdAA2foq+eswao9ZCnrXUYqUQG15S8Fd0IFSoPa4T5Ns6X+lN4Iu6K
#SkAq8e0X0gdY/NMvQtBZGec2KZZGmLAb7hzLx4Mm9Mau7IBxIt2NdOQWBITVOnma
#/Ab7/koadtV6m036OY5zdBlFBbMhsRfVDAlEYoUCEShynFRPn5qBtW2jGq/TWlYr
#8w6lqgrBEnrGWIi35PTFEnTMcQ/IMjln4GBuoGLReKz2QEVWkrd0w0wRDbdsOhVC
#lUke2TztzXfz5exLcu9Mm3jM0bjZG8CvHqzD0GN/wfAPqfFAzy6mIDtxudcXJqAj
#OWzeKJPtcaEQJSHZsU/k1O4uVLheuE6gmuU42jgsM631UdKIcEDHjQt6EqN5rCdg
#tnqj0Y6625AJ56R7FX1wdz+hAu6RWR09RLUlBN/JonUmAOUjp+nGtqKSXkChjaCQ
#zgyo5zbPQ5dD/r6/iIBQLs9z/o7aNE2Eh/qliNxK+BzwekqKmEHmsTK6VoxH3H16
#D/1o6EiVmKuHSig5aplWW8m7aVnmmNLPi/5i1CsSyznX5ORz/0ZzJxCr8+RU85y5
#h7PeZlyFlNseD/6SRnfoNu0f5XSSrghK8vvB0fGrt2/Q//DPrX9p/ZTWSNo9u7l9
#99vbN3/tqoMsrNqmBO0sV7XR965BAd+aFBJxyp7q4ifEZEMQVMQlhI4xKpwDEkYX
#Jt3nH9N80ogExrNXHjeTtKUDMiCANOYOIzxoqFjc4YY7zjgvMlZDWeojOkbl9cxh
#w/WruQ4VEFCXXLzLxbhLlY19anc0nX4C7dTksp2C0HfsPSQ7z7QSSpep1GUzoVgy
#FL1Op7AVK4HeZJPSzMeHhyx0rjoMxlWq1qXhhZN6wwu6G9349+O3b1p4gNFQ4Jt1
#yiVXw/XEZzsBlNwYXeUyvinQiwXJdzRh3JBwj22aUeya9U2rhKqxmlt+K2wwvuBT
#9AH+0WZW+92wgFzhuv1CpHgSu0kYLSc3k4PsbHFOiyIP3rP3fO+3g+7+wYv3v6b+
#iYHyi6Vp305OVPftnURQJbNBCexmhDLs4/u9BfoM4qUBWJ1REBN6yXSCl6E/1fs3
#c7/DH+LF+G/joKy5Vg9hetq66JVI6Z2GJTqnyddQRNCDMettiIO3hoBESB3FJQO1
#OIGIgOVnTKKOFqJ8XnMhIUaqCHFuzVIBNTuWiOgsqqWQCujliaY/GgsCG8BnNsEg
#Y5NzILNmQlD6cUE7y6g8VJwnaKP+p/CmpKYSbIqY+6QytNuv9//Susg+Kw3CmFbM
#EekfjI9tJA34Bk1tahhZ0V6T/SuYWE2Prhbs9cTEQqnW+GMAukNc9mwMKZWo1gnY
#BfS/JIN+J6UyAMUFxpHGuCLvz6niaThzFJbPIm1+r9nwFQPH+6LxQZF0nl9mE6V2
#QyF0pJ4OE7G3tnIdBBWslFhsVmSX+XRRxsp//aSSWZrWDePaTXb4ZMscBDRC4jcj
#N8X/IKMAy0AP/gO8vzHNuCAypxUUKemSTmFtGpPY8YnDa9dPP1uyag7aAsFEtDmr
#Z6MefoZWCCWcdH8ESannBmK5ZYNBia0n6HLlN5KbfBokjwIKMS0XPpVq+iUq0Lp6
#zOvXfVZQD9l3UwDuOAIenTE2RIDzsor4Xz0Hb9EDkMGLGa6kWICiVqwik2soagj3
#jOq33lStuz26zd5IfVv+Yd4r3nrrbGan6I8fVWZjGqcMtOdfBev3Rmj4AewsB4dA
#+Pj5G8uI3bBik8nZobWY5P/hDCuBNJW7RI0RHeixdqzOiK5066mMm6K6mchY3xsH
#UZBEORaq63UicKN6ajLpipt+rmg+IkaasoaMTzgQCLoFB2E1wrVNLGzNP2IX9EU5
#gnTvnmV7c3/Ola2zxZsgRbqWszfRJwaYvCcDH4X75TIboWOndY2Nz+2OxZcWAQkU
#+Rso3PixTebS0CBnDjdxasQlzaxXzJVL8PxC+w9tdz5+/Lh9ui2KKGdedyeJ3Uai
#WEPKe29ednIVddrJgnJxhsChDWdm3FBlH28m6mJFYJThNhYNIH4Wa3oYKWRIzmIw
#wYTXBU9pjILRj6wkUddgFtbp1iwg8AnYYNDVtdh5TNcCvTUH1AXICnkycp4QAVYT
#eSreRw3WK20g+1E3YiJZBAHCaOjVi5Io6lnkHyWsDB5Q2YBEMgiuLVPDA75tmblf
#kfWVAhgTH6P/mrD8mmhX1vlEGC70ISGe2LpuoJt3VhQtodVc9Qo8smyDKuPALyEZ
#NKppUSxm8xaGqZtjdLz2x8n9G9NAEHrDon4TuqwEbEKnEi7WUGzI40VinuBeyfUM
#7/4KwG3+pGpGNSJeStDRB3f/hgjZXEsVDFDuYGVStiM6j8NBnsjUFFy3S30kVlw7
#cWyozg7G0wF0Oy3OUseMVmQL0kQwM9SB1oDvyuVXUQl2ktwFoN6jYY95HQnPUH66
#Kd1O/RoseP+8N1/EvEyVb5fwTPXyeDAz6U17YT2jPT8Lq/lrVG/pYbUbHCybDIeu
#8PuxqadewSCmXODNFor9xcQKhUvjE8Mb3kk67PM0aTtLhAbq1zJYsJrWmY7V1lnm
#HvmGaX8z2dArgHF707HGga4bznNpg7fuQvzTIGGQDPrRIkvQ1Okr1GZjg7UdPzyr
#cwa3aXhs9+dVZ82Gu21A0RgHyfCiu3OY1yp08gyaLYQv4+7VtPikX4xI79/UHeou
#t12AEiMAQu3KAmy66XxOq42JIg7N2ELLbQUSlsDzVFSwWiCdZkogeKJ5Zu85rA4U
#uuIlh7ol51mw8kZIFH3cA//k5HL8D3TI1Z3XgV15KtNDY5e1UjPYaC9J7eUtDoHd
#4Gq1WiIMtre5FlGxBWQND+q7pT7m76LeDHCMcBqEa4oGaroD8SrIcZzN5yp8KF3M
#AISJKvRElG62Rf4AVZHB6/V1Fay2XJDwlzx8oSLZBmzqouGtw4qro7wgm6j216mp
#yE+1KsZG34fhwCriourA1FZUNDb6/b4hguEdNSDpHpbFgcCj0L3EVKHxMFXiztMA
#ky6dRLVXVwWLad+mrUgsDQ3Ifxij34eqf7rqnMMPEJaT89AOR0WwrnVw6/fZZhqk
#JSdBv1/pdquRcKqxL2lhMLTayi9C1BR9+tTBYwhDIdoWAeY7yWNY9p5YnHSQFbaH
#Js4xPuOglvWdnQLFoMtJyQ9rusBI+zTy2QDVNCAM7bwxZFkTNNuIf7jmk8+f78Ap
#jx798XgFcUJeefTobtzCCfGV/PL58y04hgr/l+KZAeeYwVocs38rfhmsxy+Dlfwy
#CPllYPgFfowG/Z/wr5BhqBRWtwwz8BmGUWGwgl0GtewyWJ9ZBopVBjWsMrgDqwxu
#yyqDtcXL8Dx6d4Hsh/1PKiip3ZnoJGeWi+tSnLEMlIdMq1JJTHV4KJhORXwAXelc
#Bc9zzaukdZt/KBQ5rFndpB0nrUPpCrYlh006+3S+pZZ/p8TpWmQRQI7U+x90um3o
#qtwUWhGFj362E1cXwz7q6suHiR3qWj2LImyK/STtttD+AbNqa6uvok5sbY3yM7sJ
#c/vcjiWVvu9gxtwLY2zI3d1M1M4dA0uBskaPv5C81jdjvLtT+dCW5zFwy/m0QBtK
#gXqhBl05Y+1cUpC8kjFd64IMAg31T7c6Ir0uP5tpE3kH1M5T9K93Nzh5CeNb/8O6
#s2EvUShw3hRIVfCn26R5G7vhORK71U/1dq3KAxWk0tbB25eMB+9r/3vcKjIURBDP
#fIIuLiBH8AewA4hRfKsy2cCPS+i4d25k3mv7maW6sx/XvhtKfc/VgUkP9+koKH1w
#s7e3TJ3cS7vHb98f7R0cq+wqMrDyJ8+Pfj04qSk+FcX3D969fHV4UFN+PLQVlnYa
#aN5/EuMgt1/TCTz8NBqI/AiPYn3BBVJVY+SEei5OFAVaUYfTumT80oGb3nhCKOLz
#NYjQe6/3l6mKBuggdG8/uyvnd0VvzMTXAS5ubM9wEGTHloFA8HuZJGdF1vsUocBX
#iA7QOv8wwgM06O8kPkDH/e8oQD58uLUEQUr8PxliC64vQz58+H9CpHI/lo9n02Le
#MD/q5Icp010hRUS5W8uS/UTV5pLEYXdHMTK4vRDRWNy/MY0v+dS34uP7yon9Wysa
#gz+KkBBcwDwaefJtBYao7IuN/e6r1+/eHp10feEhat1JhOz/k9SQqv4FckT21UkT
#2dX/f2QK7N4a8F+dJIHsFTJEl7i19IB6Ra+45sKDcKmQGxS0PWYrcds/usnc1Qfr
#VXPOFneCiFWskj010qdGwsRljHRl48DYmPLToHU61v+uHVtT8/qK/tpfdKtdb9x0
#EsbGT0CpSvosvBhagRe9c+ZuHeUQU4z7QlMTNUW9693rzLzKlWHEHBVVDFBM7QCn
#n1atFnzUbrNAZJ+zdSW+nrOMmirhtlJeV/PluwnpyyS7LnknmX64/8+R6WFvAmlu
#eubkuOnYt5HgtZYtjgXjo7Dlu68I2nDXsAa8qCf7aoOttnTewnqqV5l1rJ+2U1Sn
#whAe2L9plMJ+17FNvPvyDVhz4kxXtFPtzUTee9p7XNnQ0YTOTK6GbMoDkHHxW/Qt
#afV7M5Cj2Z8bGy5iz3yQq7NkGZIqnzBPW4nMwed8riEjQuoXekjmHBVTRw2oQqwm
#F9B1uZ14P9zzJ+SFdlAUk2m7ffDm7cGbE1c3RU0f/nvy07/VvUGoeF8NKgqA+KCo
#UloMqw8k3TSInOYK2hCp0l3RxiENwxzzSJQROGGQg7oe2TdBvmmXTKzWO/UpH0Zh
#Ub+iQZwXWWUY5yo4OqyzDHifrVUzqFjrH8JPA+i44htSWUew9aIVRQo8fVoRZrte
#MAfHd8K1T2aR4p2qMx1dmR/v8Zo8XVVzx0H1C1A0OLX25AFtxENpKdx4xEWkDG/K
#4kubp8GQ6wnBX56gNS5W+TSmSEilWb0EhV2uuBblohsQYeTFJ0Ez8YCDfwnq3orO
#SUVnhG+AC+R2cEUcwBLWvs5Go+lVLc0JAJGcj2U1vacTauS04mla6WfWCKoJ97Qa
#uO+KaZ9DDcv1e9E4xTrMsqUGg1FxvrnHfasS5S74M67iyqMa6TXPJnjxD5Rk4Yy4
#XMu1zL+5sIaX0Xc4WMMN13ChNl74DlXcdIWvmWwkP61vwHKK9p/+Vwe9QZbJVj/Z
#IlpFLE7xZOtJQcseCtN0b4+mKsDTWXfQzWtVX7X8098RPZtHj4xd7FrH9ee7HG/M
#M5DywE1P59cAGR1LT35h2Sc0xid/vBH+/Dkyxh8+/OEHud5d5/uZnvuAfmsMGwzf
#8Izj+/V25077HLvTRlemU+6tRLR2aOLLp4lOtYlXFyAOEQBrzuOSwZ24RLzw7Phk
#X8mCQX/poYDYsxrw2Z2e/aNcDIf5Z6h59P7FX7vvDp+fvHx79JpejxnDWnX1ZVxe
#l9sYOL8FpSl0fmuartGXYRTr+zei3eUaXVvDFML6L4baEWPTT29vTZUtBzBNvdzj
#3+rzD/fra9fni4eZVKHDrcNIKfYqlCk28ovt7x+8+/XgjdLU0i28L/zswY05nlim
#p5tRLnd+5jWj/kU9+LQtWiRCd5BE717pd3rojkC6tVVko6nyVd/Cy1CjZ7O8n1ap
#YhbS2xf/fvz+JXaS4Aj2iN0d/daGp3VkX+I9EbbGxie+ifEfSkFkZPAFd1lQxGEu
#1fJVFWAgeCNF5PBbmQFo/MOehKnSnOsc9lc43qtnaeSOeK0qHd1tr278ypB/AQig
#6XfEVgg3xubAsF3zoBOrvwObTBB86Ho3BK5HSte/wGoFUfr8SM3dXqFnLqTsvVa2
#aYD34Ob50TLBv2kq4U9taKaf2iy9PLV11YzTtYt+aXOOQ48rSJRNHZtlRrTy/FjJ
#EPxtX7hbYro6gldl9Fth6sMhG2CITbbVvVWv5W/YrH79S2CAyUyyLunTABLSVGVx
#nJkQxUF+/Xo/2Xr9MmGC1JV1I9BxieYlNTMGBu2UlbCGa1WAUHP5WgZqFFrHBrZW
#7dPzvnGMS8nVyxEXBjw2rHt7cfruOfLuMeruuV971cNLTQfDSxh8h+a/7TAbjNYZ
#ZvMEnRjmaJpnLJHpYvXeT12emMZECVt6aT4R6JLBs/zDG+kKVsA7+e+O3gKdjrs0
#KAeJTDRj4o/WGiMU8oLHsn2boXcq6fmjR6lLk0z74UOcaz98uB3ffPiwHuNS+yHn
#EhrfB4dvzL0GqbXYV2PkscuHD/6Q9ZNWfzZLcMeZtPZMwX0jcwZW5giF2CWKMd1X
#ggh/QGFvLduPk1S4Z1C56jHcjwofbPVbNflth2x//QHT32x4UudAlO5HBnP/1fHz
#49dy+oMqvf/+9Tvqk8quISYrYDHcGuRlryyzMd5b2NpSJ+G2xrs9bA73kd6Y/wb/
#H3Lab023vmacdUPnazQ0LK8n897nrelkdP01TXoTY2AzAvchOwLuNWA1LV6Z4dKu
#B5N8ZBMik4dO1V3/DvcVA4Zs/G2Z8jDGTIcHuhPDUfY5dYmSvSCBwEO6xsiXnAFd
#bVmvvcgWOHV5wcZ3xDLDgXDH8TyFT6WerX/4+2ujKtNFRpnh9ZfSosuULl7NUA5e
#ILAs2BUwv/G4M+gesVSOINjIUlzLFJUOG3MMgM7yvHkzNUBB4fiV5YnNVEquMmhJ
#MoCOfzNqrti/YLIYEUz4FsouwOG/a/QGjUIwiBqT74QGmpC+5eBb4FxnVw2gS6Hd
#9rrifmld2BUwOp7w0FcZ/pB9I1WPIK0/bHGFz6Dz/XD5DmPHtTmjtvoDsm8n0D6f
#QExvM0oSz/SGympwuuIdFSqoXTc0cT1Otf4tm/72I7HvjQNvy9pDeXlrI8Uq4TQL
#NAVhOzbqQMoyvQFTWoOu5ZGssvuQUUkAyqsjwaFPA9vIVnnRK7IBK1qzynvt+Os8
#ZbMsuX7Q2jHKz0Tp6tVmgJEp8dyitMrX8av/rX338Y0enioJDAlEFfi3hqNNrlxX
#//rcLGpnOT6Ow5KlnoEpCJhy1tWsXGGrvSvSL1cHUKKASe4LHyi+mhaDyIO7KooS
#O3ZJngYnMYRP7B0eLzgYvmCUT84xnJWoasLJAIAuxmayFuJdVrLsnl2b0EDMiCyK
#8FBFLMqADn+YO2P2bj4gi/JjkfLoGXs2ajf3TfwyCnFDPJ8o0QAqmEhn+Ccfklut
#F8Eo2rmOKXiqfHFt+Zivqn4dSBNlwj1ocu8p46rmqCnXiDwWEFVqMDH/xqLHReK5
#DbozEPzqmJjfwYg+P5z1L6YVwLvFdDqP1Zl0VTSySNuWKF4cMPMsoHzWMLcPvKkH
#BQUX0fvbkzk6MCHjxV556vXn+WXW1SVLP36vTnfHxzqh07bPOxFxvUv7+Eg8YKtf
#84r7FZ31ShtFjAqHT5/t6uyOLbuZbPBOnWrP2S3uq+oov04YtPs3u+I2qWAw6Adv
#zj6WxjiwPsRCbyIE01U+v0gUyRNLcvMor34OgmZF1A3J71tdx4KZ6Pwa3cjgaPsR
#0eTh2ToRrn6QpEjfqeBjtl+uLHwNpyDO+8DrFPZLksd/7s28Phm84K2mjoh8Z2Re
#NuhG36h0JWsetWbPc8LyI2S1pryOhSoA8mcbXCp7LtBL7F5Mp58oHYQMBj3cTGbT
#co6/vLFwL3Z/phvQ5gVI9iCKOeN78f7V4f7B0bG7riEeGqXJw2/0+M+hmicpW8hS
#8y4GDQ4BNCsBgEyLPwpo4leqYvdvQqDiHndvMDCdlO3XRyLZRRnMDp75sTNm8XPn
#igzlvBiZUpdZcTYVi46ILqrrinx69TeC8K4V+/HZK1cFyy/0UmniVIHdyuWJMxtb
#FKxCgGnsmN+9WYt/uONKRA/S64yT31i5g0mntwDhwutVxOubVKx06ywpdavJLSRh
#ECq1NVjMdB5hyP2lKYEcpdvOfdWUAuHSaZOgPxvhC7tW5TA/lSw4pfvmqs6zKjhe
#oQn8CEupZ3nwu2mLx9zvVYaOz22xasqJUyF05AQiJXXX6kAYFv7KBG6l8dhJlArH
#47vaMqCGyis5fqNt8mpxg3gp1R16P9lAY6yiO25efnU99JbSrChDiakUDf3FhSZq
#vhEJVS+bqJJyW99FlxF8JSGCoeWSVb1QLBPoO5SKoauyU69HlKUeka7sDJVp/OiX
#r9VCTIuwdt2ySaxy63YlRY2C0jV0wxW70XYSdpNJ228r8lhw5btJvMRum7jsq1z6
#QBKiyjid8G6IV8SFNMS1OJp9PT6bjgJhKYsKJ6wKHUH/Q3MvfNH6OMdLKy9MtGs1
#l7wOiPUyikckBj+bsh2z/QwfcY9S1HBZ+CA8nzb0ZHj0Ffc4z9ZAwylRDQ73aKo8
#PX5ir2ArWimvwk3ZNzb02ed50TNBOyTVXJabP7x0CrsdlxA8r+xukZv4xuyzdV4u
#zhrpx4/oAL2dRhFqAa/vJD/+KCvio2fzLm59dsip2u1hlttsn4NXhFi1Jd4YIvfb
#6M7MBzQVPYN+0iXRce9TBhvrUY82uRQ5v7qZZsV7027QZn2zucjRuDIQISh8jGb9
#GixsZd0mb2lWTFm4YLkfa9W+Xqxr0i0Z2G/JDYzJJJ5EMXXVy1E+cddOW8jE1qZC
#WFi9OR8HqHkQi8rtjleijEFxKi7XcKXiK3ZloM0OFv0MNDtab6Cbi5F7nh2LuCVH
#ZSaPeG6LwYZRwdgF0MFgKsgbvFcXeHFI4mGMAShwzZ5UJq6gZsycYKogeiwZ4xbE
#G0e3dpmlNGQV7lRlG+R42ircYmW9cYw99ehhGZh0fAdr8wKKz7qrYfMHwdfj3qAK
#R4UvziWPXO5NQP72dxDNc2U3xuU58nNQTJmx/Ct6vi0mCHwf4yBs4tEzDJP+Hvbj
#f9cyyLyXAJTQTyZ0QfglWy//jtfgLvPsih5OVC3Y+xJ0Za6YjuXLo+rd2eksU0ah
#WIj5KrsYIOdvyOT+D6WbNrJ4q74/6rzeOJtfTAfdcV6WMNwN9akji/LgMFbnt9c6
#VVHSe9g1IkJgUy9jpQoqAeBgpOZ622jL0q1gz21ccwPdqGahi1EmsLTfe8WxfAZP
#m1DeT7LP+E4PDQQMfrEg7Skxqi4+b4mX9RWs4E0fPgr2oUCAiFcO/Kf3dA8TE4JD
#fzsDsErgSrxKsdpBSzeBdxdUgurdC39vlBgcVDGh4reGsDjOMydv3bMnTuljQ3VK
#zMRAKyTaZuQCdGEfaQaT5fXVWzdymm0KAXHZpi0AT0Syt704I4F8DsSXtCyibkjL
#EEut2WsZqBgM29bNCq+UXi/xjMiwoC6oxyTU1l/0iiLnoyTtnB1T3z5dy7Ruf7Bi
#ahoZcGsmvLLSmucDYYq5Z1PdtFWZVXxlWVwXU/+EEppZaeUbuMjmi4GIcLAYVHPx
#Ak9coIBox1tLFKMvBr6sq3+5U85Ru7Ww2HbMEIiQAqIz95Ib7EsMOmCzDKrYbTsn
#QN1r9gAFbV0hUs5MsRhwckXQBwZexBdeq3wRE/aGaLM0rOFxBueisDjGky4yOj4u
#Yb223OTs9/KpVZ0t6thCdZDu9kZrfKRXHWOI8feDxAskHVY8mUtxCnzusqrZXUKG
#7ohq3iTwtTuvajUleMGmYJ/1KAIsxUHUMJe308KtBBM//HDIoGFl361mrbdluQRx
#m4WmRrFbcXadkm3toUjHiV9GGqvM0BMJti/RPuAfdae4dt7Fz3B6iRsErfzN5VPK
#nhbCaWyfRGZoNdhvSzBxLgfjGSF1UM3foAfv0a4hdb23MQNxVd0j9861eO/Z6I3O
#lKPUx6hBZ33Mpov5bDG3tpm4EWmF+Wi3y6YTQ9+IxVCwmI1NfUUn/DmWTM1Ei30X
#WBl2IF1p3WzpONesniOgVLJWYY+HCQxMwN13lq7rC6HIKMYoEqDJ+UsJFFWZG5iq
#nj+VMfOY9U+/b4GGuWgEJsoxGpAM7ZdUYlEZTEe9JLw4a/ypuPnbR/hfY+dZBx8U
#xrB3da4KTVaL12HWtGaF9TEm1LU8hn/sOsb7on8DZxXZUJUKZcmsyCdzq5sXi4mJ
#omN4dzOhM17gA/UGHHTIvvJuz9gw0gI/GVMhDvzD43xoKlbFyXJRmvytfBneIVeD
#LiGGIX0cyMhZtgq3I3u3BqSgToRjFAnk+XgNhCSGSjCv86GB4ZSv6bw36sasiOpT
#HnTML/KSFUZMiAOGjfRPsMT5sMjqssRIIGZ75DLZ7MKAhBkdnKQdgCLbQBN1AHh5
#6nbxschOG3r8R9MCMOiOe8WnxQwt2qYtDFVh4j2lTflWXmyuGA4fTou7r5hyx1i1
#ap6pnWZMPareDYuQBaC+zJjjg7VVGN1FhUzRC5bOVIFQbr6UXxJQimHS6GOI1iC1
#u6GB70JBq5WUE6nvxu1iUjpPDFPfbs0kZKf9RWNme4D0Ei+OHKKBrwMElC7M02xZ
#xT+jrDfpCh8SJeXl92YSba7a+aQj4sQ/q0CCbfIUR3Qtq+lF4jybdPkKp4vTqGh7
#RUPWtQHx9KBbQvvWK3qyXnCIzxTRosrj2DgrEhFxbFhyYNTi1WWcfRe9lKXjDIQh
#UVBk3O/SqUOswcrxR0G33hA5CH07BCRLBDwUVKxdvRC39Dq8bA1y9po4b7dap2Ra
#ZVjeqZIGLREyEpngnZMBpoyhkdg6xrDBydGvMd1QA8YqJblsM/ExquJlIbv8YHFV
#dj+99zZOPPU656bcL8V3Q8iDbHmR8poWOutOLvY3oO5UeLjc1aoYrpPEFtVIsmBl
#qEXpczy7KUZXi4aLz0laBalNtkVVw0kCOgmaZ2Ou4sZPMI3NM25mWV0LOMw0xmpR
#NyLphPqJez7ctqLP7DqmTszwqqvveSqcwLLqJDeOJsF7ETQTBOOxJxU4w84noI0M
#nHMnbemW1oavF2ZsCU8tTKuRk4uqEznCSoYgCs+6oUPpS3V8NZ9qM4VDSU3MpX0P
#mUCKgEmmJJ9XbCVxqZyFvMNBaxpQ9gBWaZMuUJGzL50nJCxP+rhR7wQS2k0vYjSR
#BLvr7PwKjy5jqnW5FMUz9IyrGlp34l1k4+llRmzqVxfsdwfWo5DAyHtaKvicV2Eb
#DNAyrGtV36gowL1FLzD1bdJ0K4W1TxW3C54PnviUimgOCAt4UVJV6fDsKY6nhhoZ
#q0rPjcjZ00oTq+/PoCQbep6gEgR/5eUFLHIqucGnAp79UKpyLMIT6YbnafEscT7V
#3OVCeLmww6LatknENEMZU+cyI7ahcynIYzykijTZpCXomVUJmOkXlUJDFq+aiQ1t
#vQFQ7zNrhvrXKXiWZnrEfWD5MISnxceeb1+I9yqoziuEsG1cPD28Mma3lWRVFYUA
#WukkQW8qB5B8u4cY8BWLSjBmwfKi/ngW92qHE8ZCkr+fMvZey20ncAhh7LGybngB
#y3CUoRoUO40YjURHq71iVvgPaWnN9c/KqhbXqiNWFDn5SJtX8K6Rmx/Ggwwfp+Eu
#ZHpebCbTs380AycyJ/oi89IdkEfHNOq6xadf6O9AZTuPT1tSScY9BlGgEe9m5IpO
#Sweu197n2tGeZzbDqXcHJeLurugW2XX0CP+MUX5FeCiAsdbM4wNUuRCsiDccXWQm
#04l/scUkhQKbbh3aYL4qlVPunhagvVF+me3EeshVJzI9IOqcgzdEY2taC+PLEiOi
#WYOqlpGgXI0TScW6JlFUf3N3fHZbO4vE3lRe7cdkVSY4KuHX0fSsN9ovjdO+dwtX
#WaFZP1QCWg0dJD4AbsvvbmUmt7u6Sft5QKuxMUMvqYJHfVXGqGimBDCYXk1GUxiy
#RQH7kEFWVjwOye73gJik+BfdUT7O9cHYX/zdRlA6E4GRgSUopHb2GTZGOw1st4ke
#4u5BhIveT3/513Ix5rWo3n5+DqXb7ePfnv/Uusg+D+i7QeDO8gnxH8Fr4oWoVQAj
#q3GdJBH7qiJHu93RK6Qd09zKrFuWI/QGKkbSy/1iPp+V7W3u3g6ryAzwy8SNEDRZ
#gh4Bw1bAnHIHIanTI1SvIU2Qg5skpKdykrzJoNRvJyfvFE6AdN66mOJ44y/18KbG
#vW1+qIUQ0ZZy2YVDVxxT9UCc10MEhAsXtY1eGRj2GH6rqniJ2K9M8dPVcQdmN4Ns
#TQdYhTHfy848Ra56XPVzQ8dEdvJLxcgUXPesu9BtZxEqFPdvkCZL3FdmLXfqUrU3
#M+TREtGO0pGeN+x+jK5RORG96NCVOBvQCdUDDFBVHijbR6QnKW8/NPmoldXKEdOf
#TmoiaqennmDRqoZEv13dry18osvJEW0XWceIoIVRFY2P1dszoa/t+sNcPbARyVMh
#4ZJ7zzQfO3m2GiXuDqzvfSTTSS1eUvRnk8v4ndroOqQfhTaX9h10mNdebPsqqPGL
#1PNe+Uk7n2MogdlFr8y4lSJryZsv3mhHuzbr4Ut0ymmENpGboP5/ykq8D0rMaA6N
#HXIngEa7/Q4rUh9uUdlve35Bb0lMJyL6yMGb3zupetuwVUL31ZIFTUJS9/jg3fOj
#5ydvj2pgguJM10VDxw665NjRC6rnn0FB0iRKNW3oG6kVTXhwAHJVozVNICC8mivb
#QHVXJ1b5O5h8XCpNUbogUUdHyS1sDBiUKK4FcWpDHXmKGwOJzjPIBCR48FjSLlYm
#6D2VUCJuWlz77jt8w94hbLgaA9XwYFVc8JNymtVOt4bpZsL6FUIyuNARZvXbm54S
#l4LowdApijpi1ZAE8wpGYTOEWWVkZt/IoJxyFOL4NwjTtJWiqNVfAe3386I1u2K7
#DnqgJVXttEGageKFIt6ORvJQYfwwVQ6yukHfFADK/nU5z8bs6cX+BZRrI6Sq1g6z
#3uWtGnNrGrS5epl4Ka7utJPUkdC9L7l6sSgvbK+CLYGMa8XcjMgDld10Ydi6qtaG
#p0w3W0/izAbdmueTBQuyYOMUmCzfcqnA4q79Gd59AI7QiWg/qRAyju9c2SrmXysq
#iI/KLyxSl+WGNRy4IqoXVY2gWa0P+XuCgF+DqAsZJQQql9GX51CrVwyiGjPbt6Aq
#fNbrf5oXvX7W+enU1459eqnxFJ31HJ/oyVDUFDdNO+x5WHFhrvKYMriEpuCs1ivl
#zED9pUrjoXGtLiAB6chPWvOIb8JjLBerVttCN5vgfbHBTsMkSNeR6kbqa8YbOy+m
#i9ktFM016smGcHNtI5U11mhBdcJWqStaB9th0bEg2n32aHe7fzGeBt/dI5ECWwr/
#W5aYiY/uqJDf/JNOO0YjljKaiI9u6X2yG2zt8SdYZvzvLm9+fMk+YAVlX8VYfHSH
#8rPwPmW2bHc+XfQvzDczFauLfs5CoN7L7KpkfWNTWRI002xsfLri7MONC6gU4Ows
#nf+5uB4a1q5bI82/ylZ4Mp0dZpcZWguTp1HL4UzFQlM+YvwjsCC2eC55z7nP6Gzo
#LeZT3KU1zI8QpMnBO6v6Zy1Lo2MgG58BaHt+mt0X8nLqXaZa/lxMvLRgvL0QZbFB
#V5TjIx+ONwlhVpw8fe1Y33aEzUuiWfUQB8bhTRfxrjtlDsEqmF1gQOaFSRtxn7WD
#pbbp9jHDzSCLPWYZZA5q6g3Pw7TuBUWnjDZSkWWcGIOMUX4WpJmX378hcwhPL05V
#XxCYwJJy61elBfQX5Xw6VhE4GlYj8fnKPJmr9BijID2CDQt/z95EFBLoVb1z7kUQ
#DeSDly8mvORT3cAuEw/sIIsJNdKWKGaci7eU4XOjg6ntAc23lMSE9+4fWVBgH3JE
#fgL68LxXzPNhD694Nc4Wc7pjJiJMNn0gBsm28rpo8rbj9itEJaoUa1QNrSvQtVNe
#Bz0gMq/AysslqxRs+TrKPJXOCuBXehp2e1EW22gvHcEXEh3SjvH2vBKO6sFiVTxp
#aLnaTlytZtpkDy6rDlkxvQ793e4TNz+C9F9NeYtIDfV1RytQfaVyDdXR54JFD40Q
#KfWJYdeZihbem/w1R3Z1r22LaywqOA/JeM5tLRGYJUjmydwZYeUMVUsI5s51SGmy
#fNCBmDL34NOW0NnizNJnjVnd8OAa29GT0LvYItaILXX9AZ2K8bXTthlfHF0ajiLU
#RSMtizJrg8zKgi0VVoy5aaoE9totoMJ6+TnrNySITR8kTdkumYPDyGvhkXP1yTJ2
#gWkSMuz4reJi52rvSSEOyk+lFxHb8D5LWHMfnCKKaIUip8mH6sLvdDEZpJVbYwmY
#0Fm5Ytroo1hczle8LgQ0N+HcWDfCrbdraJc+697bNEQnK78uEQ+y7Qf+Dq6ZiWx7
#PMAz3ZrL3dg81fBWRxQm7rXgg12qDKnSWW7XAiMCSvzwug67Tfcsqb5GJ9QM0189
#vyIZXLuopWCc7CJVLZcV6XoqRtQYaQgJIqPHtZtdLy60C0QmCeXO6CODsaswlrHO
#GLIiQ9OQt4XzLh6UF0RJPrxuRPvEBTDIDtUg30tSCjn6EXodV8LGClEm5RmbFpXC
#wQsCQHXQUwEFhoO8fBhcMPSIEaAhL+tSLCpTT38O5JihW5G3f73VyWVlUePWFXlE
#gHWGc4xbadz9c7fa0KrlMk51M7KT0udMMdezhK11dXhHFAN5LyLWV48g0rdq5RKa
#JCqlo5ZvPrQxg3lM5YmRQbOiGXHmuxalVoRpbB3LNkHJuunEz+5ZjN2INYVJEnuo
#b9LCtzyiUFG48DuUQtZYoJi8NkC6oiZgMsH2yMSuYbCpQuTANgpfiRAH3oOmsk+b
#LNAMpsjbVUwK2SVeih68b4e3ES1LNVxpzWrKN3yl86b0n1TPmXgOC9Vvu6hR1nKa
#rs9O+1LvNzt1qXZhIGLfZ6FWYeKNSIzrJ1CUSl1Gpa6gUj5cYy1IOH3NyHS5crGG
#QI/Uq9eN9TdaZGuVY3Hj3D8kxyO5msfStxnF1Un0fyYYXqKx0/541W7uUISJZdUY
#qTgW3Jtvew0fG3lDPkCXuTuEIRRW+AxE8FuJTuUd737d3W7yI8UrwH199XdjY3sD
#b/iyeT7IvXvr1LQ58de5HdMQHYU+etJstU6DQcHTQoYGXjQ2kJYpxQTZ/giNP9je
#TOTVbLt4cKcE342CW/7xmKKhUbvtqZ8fbSE4PHTRJ4gN/9Zobew0G087f/vl9NEv
#TfhqPhAMSXYL3HYObLQeoMWDJ5vJg5/gvz+zsnRpFAuyNHLeS58q7/9f5H0TLAqw
#2mNAZiKcJHUtdUm6olb/ujcJq9BRa0UNyqugZ5J0ZFc3fEqqvjdP9fF+Fwh58+Xy
#S3JJ+lBaOUE7mtx12ocZu7qDdnNajxFwbpIv/S9Jv5Mm6Sm+xwY6bl9fdX+IdruH
#H+HPQ9B10VujnfSTpfOpiDY9QFV5jEZq/1EP4hjyTzo63nv75rj75uS3o4Pn+8fo
#qkQM9HHwSPKMdr188AQPM/JIt8MDf2KdozNlg2m3oaGXr37tpOgH3J2W6ak/0Nuj
#fLL4vC2GWTfrjEvpNq522/3ZIp8MpyAVyn5v0tj+m14Dp8XHcqO93aRJHzQwLq/y
#yZe4lCaa/P1qnPcTAI73wpM3i/FZVrwdHk7PQZEdvTNNlMkWrTV/J2JVlnpGVJRt
#VBLSJ6ZBedArAOcQUcC+Px8lW5Pk4qo1AYwVMl/XZOilHB3oJ1F2U0FyhtNi3NOh
#HkgglsEiVDo/GU8g68DRVbMOlwVXapk0HpkEAriVPFk2Yx5eEk+6EaPUdTyab8ij
#UG366sCKe4q3OdXHx49p7EYBPhq4owCQLVsvBgb0jgebjYVnXqvR18iPEURpoYwF
#t/Ps9AG4ezw29fqLz1t6aZyjw7wNDMII5qqqtxV8R3ndRaEk+J1cb+GrD1FyiwAl
#fsDgeCgvoxoaTaI65ggdow0cr90ixk6N6sFaB+2jCkc2RqY/MQ5jsFdSPYm+EEIj
#oKZzNurNSrw/lo+zhv5wGF0AEvS4n85JtpPGvz5ONpJ/fdxseuuFLrH1TNfCQvSX
#GwNYBVToUwYPIFUCMhUElBKUzgk9cqVLtvoZu/NivSjZNRcgq8LJe11PB/CnEaMC
#y4sk7kjKIWCIWoNZDUBdZDmuACkK604tmR+rcM2UYxePDxT6QnMVNlrGlTJ3nngx
#pTHAsqva+lg2Ph5vNH+OruMPnlTwon2DRTUDmkOAslXHSRv3J4SqTphcPXoQbbxO
#5lYIoKorb/ZRWMSMHku0WaZw1Q1fne8HwlWpPBQU7HxSKVp1IWKG7aqQJZxl0PmY
#uqBrNitDLSbJO90TsrzSvskTQE11rE1ZInRdQMpW6WLrSXpGniEJ2LF2282En95Q
#f7xqtmmHsxTdSbv3bx48WcJfP8noLv65FBZUEnf99Xgl38zwmjOPD4qaQmM89IOS
#6mDgzKZDR+QL9MJ5+NAmOvXXgqBVpgsac0bjiT88lqI8sx1swt6h3PDmhWkLOOUh
#/O+RNz19SgXFsQVfebD4Y8Odv7VPH30EefDoYxP1ctiWtnUaJjY/BqoqEeTpU4lJ
#oiNCCtitjXYS7GzxD5BIk9UDoqED+rqIVqCgMzQxGj+2S9CSZnLSyblVMUDVCyu2
#E2UR8hUI3W+jN5nqLPR3s9F7qxY78m7FD3jpkLTelh09mzVQ3TWn6toSp/pO50Pp
#OdK152HrvR6riqfhGbY4fVe+6fcasabWuRNYeZoc3ID3noljoVwiwXR0/KE3U1ME
#vVQoHgCAjOsR/7HIyMT6P/FfcXfXtmKjyAdX/E90BJzsKjBJe3t+B5CME95MEo/a
#MLxas0V5EYSE8btQTWYNJOKt6Jlg3Lsztq67hCFWwNhFq2ZLLF1dfJbGXvCRa6KD
#mrZAs9efy5SFlaGbFLjN9CsFneDh+7TwWGdv5pIZV5WsNGA36o3PBr2oSsB2uErP
#i+5wa29s1Rh/lL2c7DHw32B6VXK7DDdSVNxXU730uIV3zX208C4tWqNEYXQ4d/tq
#HEb9VQme1XAb6cgemm/xHCxlXMYfqVeaUhFj/DeOLJGh0dAleefwSR+vKP5xRfFC
#jC29xMFLb1sHGPPWdc56c9AZiWkE03Vk4E/zp8poEC7ANSIh1NSig+Cw5CPBMEBi
#1yFddVJW742lHlui4p4XiY6O4y7f7I6vuzbRpPWnPZNoFUZ9h9ZF0PE8ND5llofx
#qF+DdYesLN+12oFUewYsTuoNCm6xMCme8mkLxlsKWiO/oGzWBc2qYepSRtR6FkM0
#YIZa6wYnE+6TAdofmVa634IyX93vzukzbIlcwnzGM4MCWUG9GJqrCcbjFMXpZEVq
#jE4riFR/dm1nCep0zuGzd89hr/W9+LNoOhNd9vEGaO9eM0TfeMWVkXeBz+ldYFMS
#rYf2I0oSN/vtpJC4u+7aM2vhc0I3KqfzCwrn5cQK67IKxJX4L1mL3MoXjg3ym2z0
#AMmmrK2pTmhI4QRSr1fqqxOJer58M2kT1rYPMvi5+mdT6yviqW06ZlL5OoVOTtQZ
#N9dGVBm2JX7w8abR+dvyFDaiy8pDUqjFH8WtOifVDvPPPLwNFI63dYGjvFC7h6p4
#wcEHZcSURqgZJQYniJm7jCgyQLGmi8aj4pKyw0idUn65vPxiqqiE2RdaY5f3by4v
#2Vu2y2XwpF7A6RXgVQhwhOLDrNEE2KhvJuqlaXO/QV2QEEGP1u57VVcJ52rUwl6K
#Tvm16zScysDHnyagKla8zzi/nmX6gUakLU3wZdKglzQplXY6JgotXqe+f+NzmM1u
#rmkbE7OQl4wwnyprDZKgnUWefjFPpEzmFdyeBQ83KjZYNfyhvMBz2x/izat/OhsM
#g9MoPvEgwWsOE1LDDJNNCEYgtrzBztMozfZcwy4cZjGxK7yV2NpArapII3W4Agtt
#BraeeUEVjP6wENZHETaiVq3BAoEC6LnbVes30cMPqaoQ+XQJpFOLJ/GiaoHt9uaN
#DR6EkKIfGiaFD9dNXDiF9ue17wfdZ4uvCUpqNCZvCXBDtJiUF/lwHgxifIcQo4Al
#KgYU5SsmfNcsl5CLK3r19MUC2msFBLYbumay5JUwFIeoo5kOdBIZ9/QTsFGT85Dh
#DckZzaBETAXk09D1c7292v7By+fvD0+6L96/Otw/ODrG3RZVM/FG1WWttg4nab9m
#1/rnvrFT2O8SJnU2humuU17xa8jmy692mH02v/KzomfT357hlTH94Z6hcAnaE8Ym
#sFut7eOLHgyABKjSBNhj2JTqn3/t9fv4E7lcbWOZl37yNDlaTPBQmD5/1qQNHSq1
#Em7s0940CHMSkQhD8JwZt7nxkhdrdYP7pcx91G+ETxL5mFnsFSLjOEnrSrjTrngv
#ppHOMQSOe8np8f+HVzALfBzsSffx48f4X6v1L+Yn/tdUR+bl9TjSyryroqh0y4vM
#3vbbpQ+8i2TJ4ptlyV6oigmbX3BLOW7RTUsVvC5FO8+rt2hpzSaNjr7QnuL7Yokp
#oo8782kYXjGf0iFWi05ZvFzfyuO5H6k/8gwjrCa/jI/b8W8Hh4fpKZmiRAJGb0op
#2dGnw0tA37b63hnBigLqpErNphat+bT7S4WzcNqscRdWpn+HUFpepDFEAJWqrIj2
#2Un74wGd5AaF67XNikZiSzLnVWN01xZ3++iWny7Y9isppwacBSy+oRUb13Cq9fr4
#r8fby5XcDmAcj6fZ5DLVPI7/Mja/QSaXTL0kQ6gpE0wABc0fxjVYv/YYBD1NOuG9
#Jp/SzxrczhOMg2/rqVof9bcJw001dNrr/HM+MRYNnbafzfAMweKr3bpISJso8OTx
#gCeyoqQ9Hd/Vb2rJkeZeKxRWvJN29w/evXx1eJB6VkNz7EsRkuJH87UQJPV36cl2
#/52VXfNA066Nb0VHzItJ/h+QCOPOIYpXX+5mTeYv5Mgri7BvyAp8s2lkoNx61NQs
#dPNSafDBYMZuTphB1nUGjU/89Vf1h5JcDIx29hn2b0CxPUx/TWahtY5XNba8mocF
#KEOXue2xHUKbLN8xDwdbXB4Oz30T1wBd2nG3SOsR9+z/IWD+aD3+UZG57Rk3M9pZ
#BJz9lBeWq/Cue5OwKOfM22gDOdR7cJCD6bSVpQJWPJoqlRFxZfT+bkCRYF+cT2YL
#vN/JNsMelqseaWLSDFSj2aiHD337C6qa4G11iOhLYNlTHRBYw1y5ssrK8VoS2/CN
#0WcelKAERj7HuCCo68Pax0mxyyd8AIlnxoML6vfWIkyIZIu9UbKu/A6IZaTsyfOj
#Xw9OKC7qbvB8uyt2/Pb90d7BsSoXLgIk2LFJa5mNMDG+mqlk+vH7ly9ffVim/pPO
#vEUr/j0GirAQDsg9jb28qwUMjHi4p4OX26lvVUwUrv4L2nYVET3bZPPdBxRhSDI1
#akhLDNHMQEXOngcxSoiXRtUAxGnrvUhqTSUVvs+GlAis8olIZzGKXhEzfzr1UMRD
#k+K2WaCAxWgY95+OkSy0QWpSKkVBh7ZriEC3ob4h+8eezHG8pBMbgbgzQlmAIJ6B
#1QHtLeUqFWmVhBTxNUMBttw+zCf4Kkli7nfdvyFtK3IpxMzk5lJf7ELjs7lM5hg3
#eusrMlKrMLsLRnXmeCCsDkRqx8OG4IoFUq31Mv6mqrZS2vadw6FUX7pTyifPR/Mg
#TTeQKUpMkEtMZMZ3YMq/ffHvTpTC9+GrF+b7NMKIwRO7BDZ4SXnXe0oZ/1g9wAgV
#/y1dzbnBM7obphe+0Ihcl4iwlCbUkL9ni0hbSrLdg6f9m1soBgGlwmiy2Me6Q7Lr
#nkTecOV/lMDTL8P6WG4m8ReJQ+6r6uf6W87vycbaYhnoJ160WU2xtRQU8053O9lV
#fZa7C58DRoPK9e7+zYg+l8j8EaGLftcDdXnUI7Zp2anpKiFiJhNvcVnxr99IV7XC
#1dC2bd/N8jaC4R+xC2X0CBglllbvlqWxUY8mgqZUQRTCV8eY87Gs0EzupDxq2scK
#2hGlkh4OtcswZ4e91/sVHBGsFqldKivXPLuSGIFSxbnfeFZqy4ISI8lT7xXeyolY
#+QTZ7dvWpyrrtx0XAb4AcFTygmGsw1NrbkdW8UoVi/THA1JYkmemJSgJetrxXmp3
#fQiDz51oMHymxRGI45P9t+9PPHOjexKGooNWa/YOQhRrOTtDJr9/Y/q1XI/Rg5Fd
#xdCRO1i34bPZdZTJmFXGi8YqTTK7OuIiezKVe1KZuwv6cM8/va+bTEShbl7qZyas
#ZtQaZZNzUEZ+SZ40gS+iEh3/kCeztosKQ8ratXz9AfhE4SJx2zFzJWnLVzwCAKRh
#oBLnP94gAMY0P/f4ddEP9D6HcwLZXtfs3Te8N1n0VSARYDh1Xfrlm9fd/bcnr5+f
#7P3WBG10hKE+6U2xkETMEX4YUaoYjoszLBZCgAytR4o7Brp89FqBLFj0m03/0p0k
#bUcDO1UbITVk6O2s21658Y/owQ520Q/gehc1AcN6L6ZqIeya4dx6avktIhH0Syyg
#2rjtnzwcNEAFB6m3wSKeKnqrDlsSIPIAdtQ7+rE5dbCfEAVCM76LZiexqduiqtfB
#v+822TxUg0+Q6sbdJlVE+qwiZqAaBtpYjXGDXkTcNLFy24GsrI+Rg5UrCvRnDTuK
#m3TSUWbFZYaU8Z+vX+fMpmqI10KdN9DwBWNEmhoh6SU3oTTinrSjT3qbf9WqZWIT
#P6X16+c7a1nW1+SfuATaNm+xFkau/gRri3+YRufvMUqzGtXvu6jH99rJ390Me5j0
#Rni2dp0QwFJfaaGwlD0XzDqtfhPGu3rp3VIJZmq6B83N5TtNdSqUN6G9mWlNE2tN
#x7UOJj125Nxkf38Nc1p/qX/2NuArNfz9V8fPj193qxT9UEl2Xf0mptOaXSQp++0k
#mAvfbtd4mH3+77hrOzz4cIsB/TWbZIWau6PsM4hEHXiG3K6/+ZofMTh/w/FUToLB
#mBpfAGW6a7cDq7O3hARjr94gCZKNfQNGImqxfivsXustHmvwWL1p0F9avg0Xhtao
#VVz4/Gh9HqThQg6ErHneT0Z6IHtF/wL2GF9tdvp2LKaGdE0O+6qWnJ/sP0tGYW23
#nyFftPJLUq5wucBFxB2uaE8L6bE26/erT2P3lt5JM15xwQr/mWyPBv0g2sas3+2P
#6ZY6VH63h5KuDeX8nVy4NYzWPA9rfkMbbswBoHbeaBzXtMpyFrnDieFXH/5Zcxbu
#XBe43bmNPcv5fa+cTtKJ6yvZP+768a3mwt6HD6tmw6xL80EV9rgP0DrPJjZ3/+Dd
#rwdv6qMBcXh7deD2YtDknlMz8bujg3dHb4GNj7t7e8TK1Eh9SQU9VZcfsNnvpP5E
#HF/QSGGdR27hUCNIsdIY/u5dtVq1vuNCdBrrifBdvBG+q8qlTz5vr3L5BeQRatxL
#Eyc+2hZtOJ6N8bQQJhQe4dga+uz5gSLMabOTtqqOGHQRdL21acANpyFHAdv/6tjJ
#Fsahr/HE5Q/fU9G494p2Ka/vbCAJfA/Meln+T1d7w5gwkWNUKcUkrurg0hU93I9f
#Mbw9G4tLRn8UZgYt2F7RXsGMx78dvnoBsnglN643QYRzoEWjWT9lngVJGP7wXoNC
#3rU2tps7jc7ftk8fNR8s0d708cn9Gwt6+fHjT5XPJ/1TpzVR8rvNa35x7b/o7OZd
#uPscP/5t7VkOI/KN5/ntN3F1kzj7PC96vjNS2i2pqbrYznfuRD6ejTKdEfSi6sGu
#Fa9AoTUXtRDGCLHntKqYxlRHrtE/o4OLertnoZfvzJkXZ3d9hL6eav/nv5px9vjV
#/z5Y34iiOvjdtMd/ikUWr+7+dzTJ/vX53t7dbLJ09+uPZ5RVY9lumzE00tK9z6ZB
#tlHaPz/WfZf3VFyeXiyxiHJD1VluIQ68pKHEc3sQAbpDiJFYbP9AeNVhtLdXjRHl
#xTHaMwjdjQ64Gle3q3Pv0HJtTz98qOkqZVa0yC0s3GHU5H4FFepwMtl3wYrq3p5G
#+zWjsl89Jvs1mCh7693IU4eOzr09QlhxPcr8YOUsVTw8/P3182MtVX9IYki5Ig6v
#HxKOmS5h8cPcbyVp/ij4mlfVy+tJ3/jJWNMtLKq0xISWW+11OMpJ/JsYI+r1nb2j
#V+9OMM7In646qgI1nrDfreJM7X3M8g1rjvVEwt9QG/9hMVvkzfbscz4nZXI+BXX3
#MhsJCOrGefKWevyOFsl2+9XkEpb+wVt9Q/GXJHehf7XXRGu2gL1NPm35fk8ifyGy
#CJOfouthGDNHXdbGKMr6QmNDPZjMkWe53L/w6gJN+UQUeh2Gv8GAUeYho/NYP3e0
#tYXhyHeajZ32MwwE19yRoeDiT93ZUHBBUZWNV4s9PyqG64on7NzQtspR3s/uNR4z
#hUIqX2ewQ6l7xk+0K+gtaVx2exNzc7Thk5hnrktnHQBcZWIgI5HDIdIjzJoOK0dc
#9ouDCTpXzfJsovGndkEAXClieC6F6DATS88mGMhroEIkqqfQ7TUHPqF09OzkCwgJ
#disbv1rTSSPdek5RNrbQW9h78Z03XeGgwwGdKUCkNyf7r47SJrsVDqKQ+z9GYsfb
#cvYuMffNjTeZqSYtDX4/1o2aFPlWX0Ax83NFM8OEjgCaoaeuHM5YCDoO56VCF8d6
#a9jLRxhb3qN5LKg+soYuzptWsVkfr2hTBVHZushGM7+tiJRcG+w/Evt2myKLfett
#xTDbcsj+5nf1426uxUJ1hJ7SS16/3TfjQQn44IxwgNbvB5oslqMe95sOh/KULYLq
#YApTvMy7qknf09dAgk1bP7s1LE9Ax+2AvPsnqvs0H/2BFMJjjZl6qadNVpxNy3VY
#UJdcDzrCLaFWlN1SBT3RZZL7N0YT+/3g6PjV2zex2NgxZkQP+GkBq/29VUvGGgsN
#LMxsJtvWIm9tsmJ8+y8Uj3SvN0EvTWRt6CCrEuvck1jnREBHIWNiOpy72zcMIulr
#vId1ZgEXOTFCBK9r8mn0+UXG9UV8D0JH9mmlFRAOp9NP2QBfjMCYG9HuqPclgUf5
#4a5HLoe71Dgj3IuKK+vZZsAkm2wGbbplbzNYMjzFlYekA0XVDVm1o2/Wsg7xcbdd
#v39cq+Cy+unTg7cvf3iPKUjHB4+XSQfvnfRGxiB8mnS0ekP/uOQWPkn7w6+icBvA
#bj3fTEgRoCaOgSRIC0WtpAGqzATGG8UAninBZiRh+5WymeQTpXiNgOFayXsop4cF
#iPSPxURFIMWqxDVbJ6hbLeh56h+w8bOEQh8iCrT8P6NPhUo2VyoB80xu6DOJtspp
#KiAZqAAIwQzYs9+PCcIBDSQ+rDjIS/ppSrSgBrpV99Q+b6vMkCfmwKHYj2Q6NCX1
#aKhnGJJXQ+qFeoWAOLBUfUu3UsrRtRC0bnJAVnKUCVMTOpuSLbDhAqhNrQqAj1LE
#W8OdJLCNofdw8P0ISCBIVqWZX/TmQNfMwE7Ors3hDf6cTkbXKgvNkpYa+GFAYftQ
#z5HnLcbRvsoxkq3qsQKMUbwdcUoiYZmc58glxmDbm1y7AigzNHBE0BCllZwAVA3N
#AGdQf1DzbZzha6coN9B6SisvYjMrpnQMlJf2WR4ASayglDaqjryIH4gh391SuZfI
#L1wlc9yPCdCkMQyPpueq+VmRXebThQ42RI75tL1sDKaZ7ugUhplyCZpeMFSDF9gg
#6mOuISWdEkyshyYhgRKGsCZai3r2xs6WCT0OiyQ12lZjNO3DdLezxiy8ceVsqVso
#kr23h2+PsBXSXJ7Rp21GaTOoYCUN0KhAdi7m082EVCIN4QTrkghx3bVigjpLMhjf
#DC5Z37HLpdfn5IVlZoJF3KykE82UHhdIxGSK35Qw2np+gstTKcUacBLyN8GDH9fJ
#Re8y8yDhc6xTzS2XJF2UOkSVjhaTRH+PrpGZkRV7izIzrIaN0mULN8dVtwxbLUqa
#p0ok4K0xc5GeGjTaVMAsRoNak19wtXDriTkCAMWn28Wp0e3ipZkHqGkZhWxvlOPm
#kZbP50e//t6kGv8X0VCbvw==
-41
View File
@@ -1,41 +0,0 @@
scene
{
options
{
multisample 3
width 800
height 600
ambient_occlusion 5
}
camera
{
position <2, -3, 2>
look_at <0, 0, 1>
up <0, 0, 1>
}
light
{
position <200, -10, 50>
}
define material white
{
color <1, 1, 1>
}
sphere
{
radius 1
material white
translate <0, 0, 1>
}
plane
{
position <0, 0, 1>, 0
material white
}
}
-228
View File
@@ -1,228 +0,0 @@
scene
{
options
{
multisample 3
}
camera
{
position <3, -2.6, 2>
look_at <1.5, -1, 0>
up <0, 0, 1>
}
light
{
position <-2, -5, 6>
}
plane
{
position <1, -1, 1.5>, 3
material
{
color <1, 1, 0>
}
}
define material green
{
color <0, 1, 0>
shininess 3
}
subtract
{
intersect
{
box
{
size <1, 1, 1>
material
{
color <1, 0.4, 0.4>
}
}
sphere
{
radius 0.67
material
{
color <0, 0, 1>
shininess 3
}
}
}
union
{
cyl
{
size <0.35, 0.35, 1.2>
translate <0, 0, -0.6>
material green
}
union
{
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <1, 0, 0>
translate <0, 0, -0.6>
material green
}
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <0, 1, 0>
translate <0, 0, -0.6>
material green
}
}
}
}
scale <0.5, 0.5, 0.5>
{
translate <0, -3, -2>
{
intersect
{
box
{
size <1, 1, 1>
material
{
color <1, 0.4, 0.4>
}
}
sphere
{
radius 0.67
material
{
color <0, 0, 1>
shininess 3
}
}
}
translate <0, -1.5, -1>
{
box
{
size <1, 1, 1>
material
{
color <1, 0.4, 0.4>
}
}
}
translate <1.5, 0, -1>
{
sphere
{
radius 0.67
material
{
color <0, 0, 1>
shininess 3
}
}
}
}
translate <3, 0, -2>
{
union
{
cyl
{
size <0.35, 0.35, 1.2>
translate <0, 0, -0.6>
material green
}
union
{
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <1, 0, 0>
translate <0, 0, -0.6>
material green
}
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <0, 1, 0>
translate <0, 0, -0.6>
material green
}
}
}
translate <0, -1.5, -1>
{
cyl
{
size <0.35, 0.35, 1.2>
translate <0, 0, -0.6>
material green
}
}
translate <1.5, 0, -1>
{
union
{
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <1, 0, 0>
translate <0, 0, -0.6>
material green
}
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <0, 1, 0>
translate <0, 0, -0.6>
material green
}
}
translate <0, -1.5, -1>
{
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <1, 0, 0>
translate <0, 0, -0.6>
material green
}
}
translate <1.5, 0, -1>
{
cyl
{
size <0.35, 0.35, 1.2>
rotate 90, <0, 1, 0>
translate <0, 0, -0.6>
material green
}
}
}
}
}
}
-84
View File
@@ -1,84 +0,0 @@
scene
{
options
{
width 800
height 600
multisample 3
}
camera
{
position <1.0, -2, 1.2>
look_at <0, 0, 0.4>
up <0, 0, 1>
}
light
{
position <10, 0, 10>
}
define material cyan
{
color <0, 1, 1>
}
plane
{
position <0, 0, 1>, 0
material
{
color <1, 1, 0>
reflectance 0.6
}
}
translate <0, 0, 0.7>
{
box
{
size <0.4, 0.4, 0.4>
material
{
color <1.0, 0.6, 0>
}
}
cyl {
size <0.2, 0, 0.3>
translate <0, 0, 0.3>
material cyan
}
cyl {
size <0.2, 0, 0.3>
rotate 90, <1, 0, 0>
translate <0, 0, 0.3>
material cyan
}
cyl {
size <0.2, 0, 0.3>
rotate -90, <1, 0, 0>
translate <0, 0, 0.3>
material cyan
}
cyl {
size <0.2, 0, 0.3>
rotate 180, <1, 0, 0>
translate <0, 0, 0.3>
material cyan
}
cyl {
size <0.2, 0, 0.3>
rotate 90, <0, 1, 0>
translate <0, 0, 0.3>
material cyan
}
cyl {
size <0.2, 0, 0.3>
rotate -90, <0, 1, 0>
translate <0, 0, 0.3>
material cyan
}
}
}
-152
View File
@@ -1,152 +0,0 @@
scene
{
options
{
multisample 3
exposure 1.2
}
camera
{
position <1.2, -2, 1>
look_at <0, 0, 0>
}
light { position <10, -12, 8> }
plane
{
position <0, 0, 1>, 0.501
material
{
color <0.4, 1, 0.4>
reflectance 0.4
}
}
plane
{
position <1, 0, 0>, 1
material
{
color <1, 0.4, 0.4>
reflectance 0.4
}
}
plane
{
position <0, -1, 0>, 1
material
{
color <0.4, 0.4, 1>
reflectance 0.4
}
}
subtract
{
union
{
box { size <1, 0.8, 0.8> }
box { size <0.8, 1, 0.8> }
box { size <0.8, 0.8, 1> }
cyl { size <0.1, 0.1, 0.8> translate <0.4, 0.4, -0.4> }
cyl { size <0.1, 0.1, 0.8> translate <0.4, -0.4, -0.4> }
cyl { size <0.1, 0.1, 0.8> translate <-0.4, 0.4, -0.4> }
cyl { size <0.1, 0.1, 0.8> translate <-0.4, -0.4, -0.4> }
cyl {
size <.1, .1, .8>
translate <.4, -.4, .4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, -.4, -.4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <-.4, -.4, .4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <-.4, -.4, -.4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, -.4, .4>
rotate -90, <0, 1, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, -.4, -.4>
rotate -90, <0, 1, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, .4, .4>
rotate -90, <0, 1, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, .4, -.4>
rotate -90, <0, 1, 0>
}
sphere { radius .1 translate <.4, .4, .4> }
sphere { radius .1 translate <.4, .4, -.4> }
sphere { radius .1 translate <.4, -.4, .4> }
sphere { radius .1 translate <.4, -.4, -.4> }
sphere { radius .1 translate <-.4, .4, .4> }
sphere { radius .1 translate <-.4, .4, -.4> }
sphere { radius .1 translate <-.4, -.4, .4> }
sphere { radius .1 translate <-.4, -.4, -.4> }
material
{
color <1, 0.4, 1>
transparency 0.4
}
}
union
{
sphere { radius .1 translate <0, -.5, 0> }
sphere { radius .1 translate <-.5, .25, -.25> }
sphere { radius .1 translate <-.5, -.25, .25> }
sphere { radius .1 translate <0, 0, -.5> }
sphere { radius .1 translate <.25, -.25, -.5> }
sphere { radius .1 translate <-.25, .25, -.5> }
sphere { radius .1 translate <-.25, -.25, .5> }
sphere { radius .1 translate <-.25, .25, .5> }
sphere { radius .1 translate <.25, -.25, .5> }
sphere { radius .1 translate <.25, .25, .5> }
sphere { radius .1 translate <.5, 0, 0> }
sphere { radius .1 translate <.5, .25, .25> }
sphere { radius .1 translate <.5, .25, -.25> }
sphere { radius .1 translate <.5, -.25, .25> }
sphere { radius .1 translate <.5, -.25, -.25> }
sphere { radius .1 translate <.25, .5, 0> }
sphere { radius .1 translate <.25, .5, -.25> }
sphere { radius .1 translate <.25, .5, .25> }
sphere { radius .1 translate <-.25, .5, 0> }
sphere { radius .1 translate <-.25, .5, -.25> }
sphere { radius .1 translate <-.25, .5, .25> }
material
{
color <1, 1, 1>
}
}
}
}
-134
View File
@@ -1,134 +0,0 @@
scene
{
options
{
ambient <0.2, 0.2, 0.2>
}
define material dw_blue {
color <0.000, 0.520, 0.546>
specular <0.5, 0.5, 0.5>
}
define material dw_brown {
color <0.634, 0.628, 0.126>
specular <0.5, 0.5, 0.5>
}
define material grey_ground {
color <0.3, 0.3, 0.3>
specular <0, 0, 0>
}
define shape dw_logo union {
subtract {
intersect {
cyl {
size <3.055, 3.055, 0.5>
translate <-4.454, 0.074, -0.25>
}
box {
size <6.01, 5.94, 1>
translate <-1.782, 0, 0>
}
}
cyl {
size <0.738, 0.738, 2>
translate <-3.645, 0.041, -1>
}
box {
size <1.96, 0.456, 2>
translate <-2.212, 0.048, 0>
}
material dw_blue
}
cyl {
size <0.307, 0.307, 0.5>
translate <-3.645, 0.041, -0.25>
material dw_brown
}
subtract {
extrude {
offset 0.5
polygon {
<4.787, -2.971>
<4.787, 1.565>
<4.196, 1.565>
<2.756, -2.375>
<1.367, 1.052>
<0.007, -2.40192>
<-0.891, -0.174>
<-3, -0.174>
<-3, -2.971>
}
translate <0, 0, -0.25>
}
cyl {
size <3.488, 3.488, 2>
translate <-4.454, 0.074, -1>
}
material dw_blue
}
subtract {
extrude {
offset 0.5
polygon {
<4.787, 2.025>
<4.787, 2.971>
<-3, 2.971>
<-3, 0.283>
<-0.588, 0.283>
<0.000, -1.179>
<1.368, 2.273>
<2.728, -1.083>
<3.877, 2.025>
}
translate <0, 0, -0.25>
}
cyl {
size <3.488, 3.488, 2>
translate <-4.454, 0.074, -1>
}
material dw_brown
}
scale 0.104
}
camera
{
position <0, 0, 10>
look_at <0, 0, 0>
up <0, 1, 0>
}
light
{
position <100, -100, 100>
}
plane
{
position <0, 0, 1>, 0
material grey_ground
}
for (local $i = 0; $i < 4; $i = $i + 1)
{
local $n_logos = 2 ^ ($i + 2)
for (local $j = 0; $j < $n_logos; $j = $j + 1)
{
shape dw_logo
{
rotate $j * 360 / $n_logos, <0, 0, 1>
translate <1.5 * ($i + 1), 0, 0.4 - 0.1 * $i>
if ($i > 0 && ($j + $i) % 2 == 1)
{
rotate 90, <0, 0, 1>
}
rotate -90, <0, 0, 1>
}
}
}
shape dw_logo { translate <0, 0, 0.5> }
}
-123
View File
@@ -1,123 +0,0 @@
scene {
define material dw_blue {
color <0.000, 0.520, 0.546>
specular <0.5, 0.5, 0.5>
}
define material dw_brown {
color <0.634, 0.628, 0.126>
specular <0.5, 0.5, 0.5>
}
define material grey_ground {
color <0.3, 0.3, 0.3>
shininess 30
}
define shape dw_logo union {
subtract {
intersect {
cyl {
size <3.055, 3.055, 0.5>
translate <-4.454, 0.074, -0.25>
}
box {
size <6.01, 5.94, 1>
translate <-1.782, 0, 0>
}
}
cyl {
size <0.738, 0.738, 2>
translate <-3.645, 0.041, -1>
}
box {
size <1.96, 0.456, 2>
translate <-2.212, 0.048, 0>
}
material dw_blue
}
cyl {
size <0.307, 0.307, 0.5>
translate <-3.645, 0.041, -0.25>
material dw_brown
}
subtract {
extrude {
offset 0.5
polygon {
<4.787, -2.971>
<4.787, 1.565>
<4.196, 1.565>
<2.756, -2.375>
<1.367, 1.052>
<0.007, -2.40192>
<-0.891, -0.174>
<-3, -0.174>
<-3, -2.971>
}
translate <0, 0, -0.25>
}
cyl {
size <3.488, 3.488, 2>
translate <-4.454, 0.074, -1>
}
material dw_blue
}
subtract {
extrude {
offset 0.5
polygon {
<4.787, 2.025>
<4.787, 2.971>
<-3, 2.971>
<-3, 0.283>
<-0.588, 0.283>
<0.000, -1.179>
<1.368, 2.273>
<2.728, -1.083>
<3.877, 2.025>
}
translate <0, 0, -0.25>
}
cyl {
size <3.488, 3.488, 2>
translate <-4.454, 0.074, -1>
}
material dw_brown
}
scale 0.104
}
shape dw_logo {
translate <0, 0.387, 0.309>
rotate 90, <1, 0, 0>
}
subtract {
plane {
position <0, 0, 1>, 0
}
shape dw_logo {
translate <0, 0, -0.025>
}
material grey_ground
}
camera {
position <-0.2, -0.8, 0.4>
look_at <-0.1, 0, 0.2>
up <0, 0, 1>
}
light {
position <10, -12, 5>
radius 0.2
jitter 3
}
options {
ambient_occlusion 5
multisample 3
}
}
-53
View File
@@ -1,53 +0,0 @@
scene
{
options
{
ambient_occlusion 2
multisample 2
exposure 1.2
}
camera
{
position <0, -10, 10>
look_at <0, 0, 2>
}
light { position <10, -12, 8> }
plane
{
position <0, 0, 1>, 0
material
{
color <0, 0.5, 0.1>
}
}
subtract
{
extrude { ngon 5, 6 offset 2 }
extrude { ngon 5, 5 offset 3 translate <0, 0, -0.5> }
material { color <1, 0.7, 0> }
}
union
{
extrude
{
ngon 6, 2
offset 3 { scale 0.5 position <4, 0> }
offset 3
offset 1 { scale 0 }
}
extrude
{
ngon 6, 2
offset 3 { scale 0.5 position <-4, 0> }
offset 3
offset 1 { scale 0 }
}
material { color <0, 0.9, 0.9> }
}
}
-55
View File
@@ -1,55 +0,0 @@
scene
{
camera
{
position <0, -40, 50>
look_at <0, 100, 0>
}
light
{
position <100, -100, 50>
}
define material mat
{
color <0, 0.8, 1>
}
define material glass
{
transparency 1
refraction 1.5
}
plane
{
position <0, 0, 1>, 0
material
{
color <0, 0.7, 0>
}
}
for (local $x = -200; $x <= 200; $x = $x + 40)
{
for (local $y = 0; $y <= 4000; $y = $y + 40)
{
sphere
{
radius 10
translate <$x, $y, 10>
if ($x == 0 && $y == 0)
{
material glass
translate <0, 0, 20>
}
else
{
material mat
}
}
}
}
}
-57
View File
@@ -1,57 +0,0 @@
scene
{
camera
{
position <0, -120, 80>
look_at <0, -40, 5>
}
light
{
position <100, -100, 100>
}
define material mat { color <0, 0.8, 1> }
define material mat2 { color <1, 0.7, 0> }
define material mat3 { transparency 0.5 reflectance 0.7 }
plane
{
position <0, 0, 1>, 0
material
{
color <0, 0.7, 0>
}
}
local $m = 0
for (local $x = -100; $x <= 100; $x = $x + 40)
{
for (local $y = -100; $y <= 100; $y = $y + 40)
{
sphere
{
radius 10
translate <$x, $y, 10>
if ($m % 3 == 0)
{
material mat
}
elsif ($m % 3 == 1)
{
material mat2
}
else
{
material
{
color <1, 0, 0>
transparency abs($x / 120)
}
}
}
$m = $m + 1
}
}
}
-54
View File
@@ -1,54 +0,0 @@
scene
{
options
{
multisample 4
width 1024
height 768
max_depth 15
}
camera
{
position <0.00001, -1.9, -0.000001>
look_at <0, 0, 0>
up <0, 0, 1>
}
define material mirror
{
color <0, 0, 0>
specular <0, 0, 0>
reflectance 1.0
}
plane { position <1, 0, 0>, 2 material mirror }
plane { position <-1, 0, 0>, 2 material mirror }
plane { position <0, 1, 0>, 2 material mirror }
plane { position <0, -1, 0>, 2 material mirror }
plane { position <0, 0, 1>, 2 material mirror }
plane { position <0, 0, -1>, 2 material mirror }
sphere
{
radius 0.5
material
{
reflectance 0.2
ambient <0.5, 0.5, 0.5>
diffuse <0.5, 0.5, 0.5>
shininess 80
}
}
light
{
position <1, -1, 1>
}
light
{
position <-1, 1, -1>
}
}
-151
View File
@@ -1,151 +0,0 @@
scene
{
options
{
ambient_occlusion 3
multisample 3
exposure 1.2
}
camera
{
position <5, -13, 9>
look_at <0, 0, 2>
}
light { position <10, -12, 8> radius 0.2 jitter 4 }
plane
{
position <0, 0, 1>, 0
material
{
color <0, 0.5, 0.1>
}
}
# define a reusable Die type
define shape Die subtract
{
union
{
box { size <1, 0.8, 0.8> }
box { size <0.8, 1, 0.8> }
box { size <0.8, 0.8, 1> }
cyl { size <0.1, 0.1, 0.8> translate <0.4, 0.4, -0.4> }
cyl { size <0.1, 0.1, 0.8> translate <0.4, -0.4, -0.4> }
cyl { size <0.1, 0.1, 0.8> translate <-0.4, 0.4, -0.4> }
cyl { size <0.1, 0.1, 0.8> translate <-0.4, -0.4, -0.4> }
cyl {
size <.1, .1, .8>
translate <.4, -.4, .4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, -.4, -.4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <-.4, -.4, .4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <-.4, -.4, -.4>
rotate -90, <1, 0, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, -.4, .4>
rotate -90, <0, 1, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, -.4, -.4>
rotate -90, <0, 1, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, .4, .4>
rotate -90, <0, 1, 0>
}
cyl {
size <.1, .1, .8>
translate <.4, .4, -.4>
rotate -90, <0, 1, 0>
}
define shape Corner sphere { radius 0.1 }
translate <.4, .4, .4> { shape Corner }
translate <.4, .4, -.4> { shape Corner }
translate <.4, -.4, .4> { shape Corner }
translate <.4, -.4, -.4> { shape Corner }
translate <-.4, .4, .4> { shape Corner }
translate <-.4, .4, -.4> { shape Corner }
translate <-.4, -.4, .4> { shape Corner }
translate <-.4, -.4, -.4> { shape Corner }
material
{
color <1, 0.4, 1>
transparency 0.4
}
}
union
{
define shape Pip sphere { radius 0.1 }
translate <0, -.5, 0> { shape Pip }
translate <-.5, .25, -.25> { shape Pip }
translate <-.5, -.25, .25> { shape Pip }
translate <0, 0, -.5> { shape Pip }
translate <.25, -.25, -.5> { shape Pip }
translate <-.25, .25, -.5> { shape Pip }
translate <-.25, -.25, .5> { shape Pip }
translate <-.25, .25, .5> { shape Pip }
translate <.25, -.25, .5> { shape Pip }
translate <.25, .25, .5> { shape Pip }
translate <.5, 0, 0> { shape Pip }
translate <.5, .25, .25> { shape Pip }
translate <.5, .25, -.25> { shape Pip }
translate <.5, -.25, .25> { shape Pip }
translate <.5, -.25, -.25> { shape Pip }
translate <.25, .5, 0> { shape Pip }
translate <.25, .5, -.25> { shape Pip }
translate <.25, .5, .25> { shape Pip }
translate <-.25, .5, 0> { shape Pip }
translate <-.25, .5, -.25> { shape Pip }
translate <-.25, .5, .25> { shape Pip }
material
{
color <1, 1, 1>
}
}
}
# Define an individual stack of dies
define shape DieStack union
{
translate <0, 0, 1.5> { scale 3 { shape Die } }
translate <0, 0, 4> { rotate 45, <0, 0, 1> { scale 2 { shape Die } } }
translate <0, 0, 5.5> { rotate 90, <0, 1, 0> { shape Die } }
}
# Instantiate various DieStack objects
translate <-5, 0, 0> { shape DieStack }
translate <5, 0, 0> { rotate 90, <0, 0, 1> { shape DieStack } }
translate <0, 5, 0> { rotate 180, <0, 0, 1> { shape DieStack } }
translate <0, -5, 0> { rotate 270, <0, 0, 1> { shape DieStack } }
}
-27
View File
@@ -1,27 +0,0 @@
scene {
options {
width 400
height 300
}
camera {
position <5, -5, 4>
look_at <0, 0, 1>
}
light {
position <10, -10, 50>
}
plane {
position <0, 0, 1>, 0
material {
color <0, 1, 0.2>
reflectance 0.4
}
}
sphere {
radius 1
translate <0, 0, 1>
material {
color <1, 1, 0>
}
}
}
-55
View File
@@ -1,55 +0,0 @@
scene
{
options
{
multisample 3
ambient <0.2, 0.2, 0.2>
}
camera
{
position <1, -2, 1>
look_at <0, 0, 0>
}
light
{
position <-5, -15, 20>
jitter 5
}
plane
{
position <0, 0, 1>, 1
material
{
color <0, 1, 1>
}
}
subtract
{
box
{
size <1, 1, 1>
material { color <0.2, 0.2, 1> }
}
subtract
{
sphere
{
radius 0.4
material { color <1, 0, 0> }
translate <0, -0.5, 0>
}
box
{
size <1, 1, 0.2>
material { color <0, 1, 0> }
translate <0, -0.5, 0>
}
}
}
}
-46
View File
@@ -1,46 +0,0 @@
scene
{
options
{
width 800
height 600
multisample 3
}
camera
{
position <2, -2.5, 3>
look_at <-0.5, 0, 0>
up <0, 0, 1>
}
light { position <5, 0, 5> }
plane
{
position <0, 0, 1>, 0
material { color <0.8, 0.8, 0.8> }
}
box
{
size <1, 1, 1>
translate <0, -1.5, 0.6>
material { color <0, 0, 1> transparency 0.8 }
}
box
{
size <1, 1, 1>
translate <0, 0, 0.6>
material { color <0, 0, 1> transparency 0.4 }
}
box
{
size <1, 1, 1>
translate <0, 1.5, 0.6>
material { color <0, 0, 1> transparency 0.1 }
}
}
-438
View File
@@ -1,438 +0,0 @@
#include "distrib.h"
#include <fstream>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h> /* gethostbyname() */
#include <string.h> /* memset() */
#include <fcntl.h> /* fcntl(), F_GETFD, F_SETFD, FD_CLOEXEC */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h> /* TCP_NODELAY */
using namespace std;
#define MSG_WANT_DATA 1
#define MSG_SEND_DATA 2
distrib::distrib()
{
pthread_cond_init(&m_listen_cond, NULL);
pthread_cond_init(&m_tasks_complete_cond, NULL);
pthread_mutex_init(&m_listen_mutex, NULL);
pthread_mutex_init(&m_task_mutex, NULL);
pthread_mutex_init(&m_tasks_in_progress_mutex, NULL);
pthread_mutex_init(&m_tasks_complete_mutex, NULL);
m_num_clients = 0;
m_data = NULL;
m_server = true;
m_next_task = 0;
m_client_socket = -1;
m_tasks_complete = 0;
}
distrib::~distrib()
{
if (m_client_socket != -1)
close(m_client_socket);
}
int distrib::readHostFile(const char * filename)
{
ifstream ifs(filename);
if ( ! ifs.is_open() )
return 1;
string host;
while ( ! ifs.eof() )
{
ifs >> host;
if ( ifs.eof() )
break;
m_hosts.push_back(host);
}
ifs.close();
return 0;
}
int distrib::startClients(const std::vector<std::string> & client_options)
{
int ret = 0;
for (int i = 0, sz = m_hosts.size(); i < sz; i++)
{
ret += clientConnect(m_hosts[i], client_options);
}
return ret;
}
int distrib::clientConnect(const string & host,
const std::vector<std::string> & client_options)
{
int id = fork();
if (id < 0) /* check for fork() error */
{
cerr << "Error forking: " << id << endl;
return 1;
}
else if (id > 0) /* in the parent */
{
m_children.push_back(id);
m_num_clients++;
}
else /* in the child */
{
char server_port_str[15];
sprintf(server_port_str, "%d", m_serverport);
vector<string> args;
args.push_back("ssh");
args.push_back(host);
args.push_back("fart");
args.push_back("--host");
args.push_back(m_servername);
args.push_back("--port");
args.push_back(server_port_str);
for (int i = 0, sz = client_options.size(); i < sz; i++)
args.push_back(client_options[i]);
const char * char_star_args[args.size() + 1];
for (int i = 0, sz = args.size(); i < sz; i++)
char_star_args[i] = args[i].c_str();
char_star_args[args.size()] = (char *) NULL;
#if 0
/* debug */
cout << "executing: 'ssh', ";
for (int i = 0, sz = args.size(); i < sz; i++)
cout << "'" << char_star_args[i] << "', ";
cout << endl;
#endif
execvp("ssh", (char * const *) char_star_args);
/* we should not get here */
cerr << "Error " << errno << " with execlp()!" << endl;
exit(33);
}
return 0;
}
void connection_thread(distrib::connection_thread_arg_t * arg)
{
distrib * the_distrib = arg->the_distrib;
int client_socket = arg->client_socket;
delete arg;
int flag = 1;
if (setsockopt(client_socket,
IPPROTO_TCP,
TCP_NODELAY,
(char *) &flag,
sizeof(flag)) < 0)
{
cerr << "Failed to set TCP_NODELAY on client socket in connection thread!" << endl;
}
bool done = false;
/* loop listening for messages from the client */
while (!done)
{
int msg_type;
size_t nread = read(client_socket, &msg_type, sizeof(msg_type));
if (nread == sizeof(msg_type))
{
switch (msg_type)
{
case MSG_WANT_DATA:
{
int task = the_distrib->getTask();
if (write(client_socket, &task, sizeof(task)) < 0)
done = true;
}
break;
case MSG_SEND_DATA:
{
unsigned char data[3 * UNIT_TASK_SIZE];
int task;
if (read(client_socket, &task, sizeof(task)) < 0)
done = true;
else if (read(client_socket, &data[0], sizeof(data)) < 0)
done = true;
else
the_distrib->send_data(task, &data[0], sizeof(data));
}
break;
default:
break;
}
}
else if (nread > 0 && nread < sizeof(msg_type))
{
cerr << "Error: nread = " << nread << "!" << endl;
}
else if (nread < 0)
{
break;
}
}
close(client_socket);
cerr << "Closing connection thread!" << endl;
pthread_exit(NULL);
}
void distrib_server(distrib * the_distrib)
{
char hostname[1000];
gethostname(&hostname[0], 1000);
the_distrib->m_servername = hostname;
int listen_socket = socket(PF_INET, SOCK_STREAM, 0);
if ( listen_socket == -1 )
{
cerr << "Error " << errno << " creating listen socket!" << endl;
exit(39);
}
int flags = fcntl(listen_socket, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(listen_socket, F_SETFD, flags);
if ( listen(listen_socket, 5) == -1 )
{
cerr << "Error " << errno << " when trying to listen!" << endl;
exit(40);
}
struct sockaddr_in addr;
int addr_len = sizeof(struct sockaddr_in);
getsockname(listen_socket,
(struct sockaddr *) &addr,
(socklen_t *) &addr_len);
int ip_addr = ntohl(addr.sin_addr.s_addr);
the_distrib->m_serverport = ntohs(addr.sin_port);
cout << "Listening on "
<< (unsigned int) ((ip_addr >> 24) & 0xFF)
<< '.'
<< (unsigned int) ((ip_addr >> 16) & 0xFF)
<< '.'
<< (unsigned int) ((ip_addr >> 8) & 0xFF)
<< '.'
<< (unsigned int) (ip_addr & 0xFF)
<< ':'
<< the_distrib->m_serverport
<< endl;
/* signal readiness of the listen thread */
pthread_mutex_lock(&the_distrib->m_listen_mutex);
pthread_cond_signal(&the_distrib->m_listen_cond);
pthread_mutex_unlock(&the_distrib->m_listen_mutex);
for (;;)
{
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_socket = accept(listen_socket,
(struct sockaddr *) &client_addr,
&client_addr_len);
if (client_socket < 0)
break;
#if 0
int cip = ntohl(client_addr.sin_addr.s_addr);
cout << "Connection from "
<< (unsigned int) ((cip >> 24) & 0xFF)
<< '.'
<< (unsigned int) ((cip >> 16) & 0xFF)
<< '.'
<< (unsigned int) ((cip >> 8) & 0xFF)
<< '.'
<< (unsigned int) (cip & 0xFF)
<< ':'
<< client_addr.sin_port
<< endl;
#endif
distrib::connection_thread_arg_t * arg =
new distrib::connection_thread_arg_t;
arg->the_distrib = the_distrib;
arg->client_socket = client_socket;
pthread_t client_thread;
pthread_create(&client_thread,
NULL,
(void * (*)(void *)) &connection_thread,
arg);
}
cout << "Listen thread exiting!" << endl;
}
int distrib::startServer()
{
m_server = true;
pthread_mutex_lock(&m_listen_mutex);
/* start the listen thread */
int ret = pthread_create(&m_server_thread,
NULL,
(void * (*)(void *)) distrib_server,
this);
if (ret)
return ret;
/* wait for the listen thread to be running */
pthread_cond_wait(&m_listen_cond, &m_listen_mutex);
pthread_mutex_unlock(&m_listen_mutex);
return ret;
}
int distrib::startClient(const char * server, int port)
{
m_server = false;
m_client_socket = socket(PF_INET, SOCK_STREAM, 0);
if (m_client_socket < 0)
{
cerr << "Error creating client socket: " << errno << endl;
return 1;
}
int flag = 1;
if (setsockopt(m_client_socket,
IPPROTO_TCP,
TCP_NODELAY,
(char *) &flag,
sizeof(flag)) < 0)
{
cerr << "Failed to set TCP_NODELAY on client socket!" << endl;
return 2;
}
struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;
struct addrinfo * res;
char portstr[15];
sprintf(portstr, "%d", port);
getaddrinfo(server, portstr, &hint, &res);
if (connect(m_client_socket, res->ai_addr, res->ai_addrlen) == -1)
{
cerr << "Error connecting from client socket: " << errno << endl;
return 2;
}
return 0;
}
int distrib::send_data(int task, unsigned char * data, int num_bytes)
{
if (m_server)
{
if (m_data != NULL)
{
int num_to_copy = num_bytes;
if (3 * task * UNIT_TASK_SIZE + num_to_copy > m_data_size)
num_to_copy = m_data_size - 3 * task * UNIT_TASK_SIZE;
if (num_to_copy > 0)
{
memcpy(m_data + 3 * task * UNIT_TASK_SIZE, data, num_to_copy);
}
taskDone(task);
}
}
else
{
int msg_header = MSG_SEND_DATA; /* send data */
if ( write(m_client_socket, &msg_header, sizeof(msg_header)) < 0
|| write(m_client_socket, &task, sizeof(task)) < 0
|| write(m_client_socket, data, num_bytes) < 0)
{
return -1;
}
}
return 0;
}
int distrib::getTask()
{
int task = -1;
if (m_server)
{
pthread_mutex_lock(&m_task_mutex);
if (m_next_task < m_num_tasks)
{
task = m_next_task;
m_next_task++;
}
pthread_mutex_unlock(&m_task_mutex);
if (task > -1)
startTask(task);
else
{
pthread_mutex_lock(&m_tasks_in_progress_mutex);
std::map<int, int>::const_iterator it = m_tasks_in_progress.begin();
if (it != m_tasks_in_progress.end())
{
task = it->first;
}
pthread_mutex_unlock(&m_tasks_in_progress_mutex);
}
}
else
{
int msg_header = MSG_WANT_DATA;
if (write(m_client_socket, &msg_header, sizeof(msg_header)) < 0)
return -1;
/* wait for a message back */
if (read(m_client_socket, &task, sizeof(task)) < 0)
return -1;
}
return task;
}
void distrib::startTask(int task)
{
pthread_mutex_lock(&m_tasks_in_progress_mutex);
m_tasks_in_progress[task] = 1;
pthread_mutex_unlock(&m_tasks_in_progress_mutex);
}
void distrib::taskDone(int task)
{
pthread_mutex_lock(&m_tasks_in_progress_mutex);
m_tasks_in_progress.erase(task);
m_tasks_complete++;
if (m_tasks_complete == m_num_tasks)
{
pthread_mutex_lock(&m_tasks_complete_mutex);
pthread_cond_signal(&m_tasks_complete_cond);
pthread_mutex_unlock(&m_tasks_complete_mutex);
}
pthread_mutex_unlock(&m_tasks_in_progress_mutex);
}
void distrib::waitAllTasks()
{
int done;
pthread_mutex_lock(&m_tasks_in_progress_mutex);
done = m_tasks_complete;
if (done < m_num_tasks)
pthread_mutex_lock(&m_tasks_complete_mutex);
pthread_mutex_unlock(&m_tasks_in_progress_mutex);
if (done < m_num_tasks)
{
pthread_cond_wait(&m_tasks_complete_cond, &m_tasks_complete_mutex);
pthread_mutex_unlock(&m_tasks_complete_mutex);
}
}
-72
View File
@@ -1,72 +0,0 @@
#ifndef DISTRIB_H
#define DISTRIB_H DISTRIB_H
#include <string>
#include <vector>
#include <map>
#include <pthread.h>
#define UNIT_TASK_SIZE 100
class distrib
{
public:
distrib();
~distrib();
int readHostFile(const char * filename);
int startServer();
int startClient(const char * server, int port);
int startClients(const std::vector<std::string> & client_options);
int getNumClients() { return m_num_clients; }
void set_data(unsigned char * data, int size)
{
m_data = data;
m_data_size = size;
}
void set_num_tasks(int num_tasks) { m_num_tasks = num_tasks; }
int getTask();
int send_data(int task, unsigned char * data, int num_bytes);
int getNumTasksInProgress() { return m_tasks_in_progress.size(); }
void waitAllTasks();
std::string & getServerName() { return m_servername; }
int getServerPort() { return m_serverport; }
typedef struct
{
distrib * the_distrib;
int client_socket;
} connection_thread_arg_t;
friend void distrib_server(distrib * the_distrib);
friend void connection_thread(connection_thread_arg_t * arg);
protected:
int clientConnect(const std::string & host,
const std::vector<std::string> & client_options);
void startTask(int task);
void taskDone(int task);
std::vector<std::string> m_hosts;
std::vector<int> m_children;
std::string m_servername;
int m_serverport;
int m_client_socket;
pthread_t m_server_thread;
pthread_cond_t m_listen_cond;
pthread_mutex_t m_listen_mutex;
int m_num_clients;
unsigned char * m_data;
int m_data_size;
int m_num_tasks;
int m_tasks_complete;
int m_next_task;
bool m_server;
pthread_mutex_t m_task_mutex;
std::map<int, int> m_tasks_in_progress;
pthread_mutex_t m_tasks_in_progress_mutex;
pthread_mutex_t m_tasks_complete_mutex;
pthread_cond_t m_tasks_complete_cond;
};
#endif
+73
View File
@@ -0,0 +1,73 @@
tree;
tree_prefix P;
ptype double;
<<header
/* User terminate codes. */
#define PARSER_TERMINATE_NUMBER_RANGE 1u
>>
<<
#include <charconv>
/* Store the numeric value of the matched text in *out. Returns false if the
* value cannot be represented. */
static bool parse_number(uint8_t const * match_text, size_t match_length, double * out)
{
const char * first = (const char *)match_text;
const char * last = first + match_length;
std::from_chars_result r = std::from_chars(first, last, *out);
return (r.ec == std::errc()) && (r.ptr == last);
}
>>
drop /\s+/;
token background;
token comma /,/;
token gt />/;
token height;
token lcurly /\{/;
token lt /</;
token options;
token rcurly /\}/;
token scene;
token width;
token uint /\d+/ <<
if (!parse_number(match_text, match_length, &$$))
{
$terminate(PARSER_TERMINATE_NUMBER_RANGE);
}
>>
token number /-?\d+(\.\d+)?/ <<
if (!parse_number(match_text, match_length, &$$))
{
$terminate(PARSER_TERMINATE_NUMBER_RANGE);
}
>>
Start -> RenderParams Scene;
Background -> background Vector;
Height -> height uint;
Number -> uint;
Number -> number;
RenderParams -> ;
RenderParams -> RenderParam RenderParams;
RenderParam -> Background;
RenderParam -> Height;
RenderParam -> Width;
Scene -> scene lcurly SceneItems rcurly;
SceneItems -> ;
Vector -> lt Number comma Number comma Number gt;
Vector -> lt Number comma Number comma Number comma Number gt;
Width -> width uint;
+226
View File
@@ -0,0 +1,226 @@
#include <fart/Cli.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#define FLAGS \
FLAG(height, "h", true) \
FLAG(height_l, "height", true) \
FLAG(help, "help", false) \
FLAG(output, "o", true) \
FLAG(output_l, "output", true) \
FLAG(threads, "t", true) \
FLAG(threads_l, "threads", true) \
FLAG(version, "version", false) \
FLAG(width, "w", true) \
FLAG(width_l, "width", true) \
#define FLAG(name, flag, arg) flag_##name,
typedef enum
{
FLAGS
flag__count
} flag_id_t;
#undef FLAG
#define FLAG(name, flag, arg) static const char flag_##name##_str[] = flag;
FLAGS
#undef FLAG
typedef struct
{
const char * flag;
size_t flag_length;
bool arg;
} flag_def_t;
#define FLAG(name, flag, arg) { \
flag_##name##_str, \
sizeof(flag_##name##_str) - 1u, \
arg},
static const flag_def_t flag_defs[] = {
FLAGS
};
#undef FLAG
int Cli::run(int argc, char * argv[])
{
for (size_t i = 1; i < argc; i++)
{
size_t dashes = 0u;
if (strncmp(argv[i], "--", 2) == 0)
{
dashes = 2u;
}
else if (strncmp(argv[i], "-", 1) == 0)
{
dashes = 1u;
}
if (dashes > 0u)
{
const char * flag = &argv[i][dashes];
size_t flag_idx;
for (flag_idx = 0u; flag_idx < flag__count; flag_idx++)
{
size_t flag_length = flag_defs[flag_idx].flag_length;
size_t flag_dashes = flag_length == 1u ? 1u : 2u;
if (flag_dashes != dashes)
{
continue;
}
if (strncmp(flag, flag_defs[flag_idx].flag, flag_length) != 0)
{
continue;
}
if (dashes == 2 && flag[flag_length] != '\0' && flag[flag_length] != '=')
{
continue;
}
bool arg = flag_defs[flag_idx].arg;
const char * arg_value = nullptr;
if (arg)
{
if (flag[flag_length] == '\0')
{
if (i >= (argc - 1))
{
fprintf(stderr, "Error: option %s requires an argument\n", flag_defs[flag_idx].flag);
return 2;
}
i++;
arg_value = argv[i];
}
else if (dashes == 1u)
{
arg_value = &flag[flag_length];
}
else if (flag[flag_length + 1] != '\0')
{
arg_value = &flag[flag_length + 1];
}
else
{
fprintf(stderr, "Error: option %s requires an argument\n", flag_defs[flag_idx].flag);
return 2;
}
}
else if (dashes == 2u && flag[flag_length] == '=')
{
fprintf(stderr, "Error: option %s does not take an argument\n", flag_defs[flag_idx].flag);
return 2;
}
else
{
/* TODO: support combining short flags, e.g. -cofoo */
}
int r = handle_option(flag_idx, arg_value);
if (r != 0)
{
return r;
}
break;
}
if (flag_idx == flag__count)
{
fprintf(stderr, "Unknown flag: %s\n", flag);
return 2;
}
}
else
{
if (!this->input_file.empty())
{
fprintf(stderr, "Extraneous argument: %s\n", argv[i]);
return 2;
}
this->input_file = argv[i];
}
}
return 0;
}
static bool parse_size(const char * flag, const char * value, opt<size_t> * out)
{
bool valid = value[0] != '\0';
for (const char * c = value; *c != '\0'; c++)
{
if (*c < '0' || *c > '9')
{
valid = false;
break;
}
}
if (valid)
{
errno = 0;
unsigned long long v = strtoull(value, nullptr, 10);
if (errno == 0 && v <= SIZE_MAX)
{
*out = (size_t)v;
return true;
}
}
fprintf(stderr, "Error: invalid value for option %s: '%s'\n", flag, value);
return false;
}
int Cli::handle_option(size_t flag_idx, const char * arg_value)
{
switch (flag_idx)
{
case flag_height:
case flag_height_l:
if (!parse_size(flag_defs[flag_idx].flag, arg_value, &this->params.height))
{
return 2;
}
return 0;
case flag_help:
printf("Usage: fart [options] input-file\n");
printf("\n");
printf(" Options:\n");
printf("\n");
printf(" -h, --height H Set render height to H\n");
printf(" --help Show this help and exit\n");
printf(" -o, --output F Write rendered image to file F\n");
printf(" -t, --threads N Use N render threads (default: number of CPUs)\n");
printf(" -w, --width W Set render width to W\n");
printf(" --version Show fart version and exit\n");
return 1;
case flag_output:
case flag_output_l:
this->output_file = arg_value;
return 0;
case flag_threads:
case flag_threads_l:
if (!parse_size(flag_defs[flag_idx].flag, arg_value, &this->params.threads))
{
return 2;
}
if (*this->params.threads == 0u)
{
fprintf(stderr, "Error: option %s must be at least 1\n", flag_defs[flag_idx].flag);
return 2;
}
return 0;
case flag_width:
case flag_width_l:
if (!parse_size(flag_defs[flag_idx].flag, arg_value, &this->params.width))
{
return 2;
}
return 0;
case flag_version:
printf("fart, version 0.0.0.0.0.1\n");
return 1;
}
fprintf(stderr, "Internal error: unhandled flag_idx=%zu\n", flag_idx);
return 2;
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <fart/RenderParams.h>
class Cli
{
public:
RenderParams params;
string_view input_file;
opt<string_view> output_file;
int run(int argc, char * argv[]);
private:
int handle_option(size_t option_idx, const char * arg_value);
};
+14
View File
@@ -0,0 +1,14 @@
#pragma once
struct Color
{
double r;
double g;
double b;
double a;
Color(double r = 0.0, double g = 0.0, double b = 0.0, double a = 1.0)
: r(r), g(g), b(b), a(a)
{
}
};
+70
View File
@@ -0,0 +1,70 @@
#include <fart/Engine.h>
#include <fart/Parser.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
Engine::Engine(const RenderParams & params)
: params(params)
{
}
Engine::~Engine()
{
delete scene;
}
int Engine::run(string_view input_file, opt<string_view> output_file)
{
string output_path;
if (output_file)
{
output_path = *output_file;
}
else
{
string_view base = input_file;
if (base.ends_with(".fart"))
{
base.remove_suffix(5u);
}
output_path = string(base) + ".png";
}
image_writer.reset(ImageWriter::build(output_path));
if (!image_writer)
{
return 1;
}
/* Verify the output file can be written before rendering. */
FILE * fh = fopen(output_path.c_str(), "wb");
if (fh == nullptr)
{
fprintf(stderr, "Error opening output file '%s': %s\n", output_path.c_str(), strerror(errno));
return 1;
}
if (fclose(fh) != 0)
{
fprintf(stderr, "Error closing output file '%s': %s\n", output_path.c_str(), strerror(errno));
return 1;
}
if (remove(output_path.c_str()) != 0)
{
fprintf(stderr, "Error removing output file '%s': %s\n", output_path.c_str(), strerror(errno));
return 1;
}
delete scene;
scene = Parser::load_scene(input_file);
if (!scene)
{
return 1;
}
/* CLI params take precedence over scene params; defaults fill the rest. */
params.merge(scene->params);
params.apply_defaults();
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <fart/ImageWriter.h>
#include <fart/RenderParams.h>
#include <fart/Scene.h>
#include <memory>
class Engine
{
public:
Engine(const RenderParams & params);
~Engine();
int run(string_view input_file, opt<string_view> output_file);
protected:
RenderParams params;
std::unique_ptr<ImageWriter> image_writer;
Scene * scene = nullptr;
};
+6
View File
@@ -0,0 +1,6 @@
#include <fart/Image.h>
Image::Image(size_t width, size_t height)
: width(width), height(height), pixels(width * height)
{
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <fart/Color.h>
#include <stddef.h>
struct Image
{
size_t width;
size_t height;
vector<Color> pixels;
Image(size_t width, size_t height);
Color & operator[](size_t x, size_t y)
{
return this->pixels[this->width * y + x];
}
};
+15
View File
@@ -0,0 +1,15 @@
#include <fart/ImageWriter.h>
#include <fart/PngImageWriter.h>
#include <stdio.h>
#include <filesystem>
ImageWriter * ImageWriter::build(const string & fname)
{
string ext = std::filesystem::path(fname).extension().string();
if (ext == ".png")
{
return new PngImageWriter(fname);
}
fprintf(stderr, "Unsupported output image format: '%s'\n", fname.c_str());
return nullptr;
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <fart/Image.h>
class ImageWriter
{
public:
static ImageWriter * build(const string & fname);
virtual ~ImageWriter() = default;
virtual bool write(const Image & image) = 0;
protected:
ImageWriter(const string & fname) : fname(fname) {}
string fname;
};
+143
View File
@@ -0,0 +1,143 @@
#include <fart/Parser.h>
#include <fart/propane.h>
#include <fart/read_file.h>
#include <stdint.h>
#include <stdio.h>
Scene * Parser::load_scene(const string_view & input_file)
{
opt<vector<uint8_t>> input = read_file(input_file);
if (!input)
{
return nullptr;
}
p_context_t * context = p_context_new(input->data(), input->size());
size_t result = p_parse(context);
p_position_t pos = p_position(context);
int fname_len = (int)input_file.size();
const char * fname = input_file.data();
bool success = false;
switch (result)
{
case P_SUCCESS:
success = true;
break;
case P_DECODE_ERROR:
fprintf(stderr, "%.*s:%u:%u: error: invalid UTF-8 encoding\n",
fname_len, fname, pos.row, pos.col);
break;
case P_UNEXPECTED_INPUT:
fprintf(stderr, "%.*s:%u:%u: error: unexpected input\n",
fname_len, fname, pos.row, pos.col);
break;
case P_UNEXPECTED_TOKEN:
{
p_token_t token = p_token(context);
if (token == TOKEN___EOF)
{
fprintf(stderr, "%.*s:%u:%u: error: unexpected end of input\n",
fname_len, fname, pos.row, pos.col);
}
else
{
fprintf(stderr, "%.*s:%u:%u: error: unexpected token '%s'\n",
fname_len, fname, pos.row, pos.col, p_token_names[token]);
}
break;
}
case P_USER_TERMINATED:
if (p_user_terminate_code(context) == PARSER_TERMINATE_NUMBER_RANGE)
{
fprintf(stderr, "%.*s:%u:%u: error: number out of range\n",
fname_len, fname, pos.row, pos.col);
}
else
{
fprintf(stderr, "%.*s:%u:%u: internal error: unknown terminate code %zu\n",
fname_len, fname, pos.row, pos.col, p_user_terminate_code(context));
}
break;
default:
fprintf(stderr, "%.*s:%u:%u: internal error: unknown parser result %zu\n",
fname_len, fname, pos.row, pos.col, result);
break;
}
if (!success)
{
p_context_delete(context);
return nullptr;
}
Scene * scene = build_scene(input_file, p_result(context));
p_context_delete(context);
return scene;
}
/* Convert a uint token value to a size_t, reporting an error if it does not
* fit. */
static bool token_to_size(const string_view & input_file, PToken token, opt<size_t> * out)
{
/* SIZE_MAX + 1, computed so that it is exactly representable as a double. */
const double size_limit = (double)(SIZE_MAX / 2u + 1u) * 2.0;
double value = token.pvalue();
if (value >= size_limit)
{
p_position_t pos = token.position();
fprintf(stderr, "%.*s:%u:%u: error: value out of range\n",
(int)input_file.size(), input_file.data(), pos.row, pos.col);
return false;
}
*out = (size_t)value;
return true;
}
static Color vector_to_color(PVector vector)
{
Color color{
vector.pNumber2().pToken().pvalue(),
vector.pNumber4().pToken().pvalue(),
vector.pNumber6().pToken().pvalue(),
};
if (PNumber a = vector.pNumber8(); p_node_valid(a))
{
color.a = a.pToken().pvalue();
}
return color;
}
Scene * Parser::build_scene(const string_view & input_file, PStart start)
{
Scene * scene = new Scene();
for (PRenderParams rps = start.pRenderParams(); p_node_valid(rps); rps = rps.pRenderParams())
{
PRenderParam rp = rps.pRenderParam();
bool ok = true;
if (PBackground background = rp.pBackground(); p_node_valid(background))
{
scene->params.background = vector_to_color(background.pVector());
}
else if (PHeight height = rp.pHeight(); p_node_valid(height))
{
ok = token_to_size(input_file, height.pToken2(), &scene->params.height);
}
else if (PWidth width = rp.pWidth(); p_node_valid(width))
{
ok = token_to_size(input_file, width.pToken2(), &scene->params.width);
}
if (!ok)
{
delete scene;
return nullptr;
}
}
return scene;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <fart/Scene.h>
struct PStart;
class Parser
{
public:
static Scene * load_scene(const string_view & input_file);
private:
static Scene * build_scene(const string_view & input_file, PStart start);
};
+38
View File
@@ -0,0 +1,38 @@
#include <fart/PngImageWriter.h>
#include <png.h>
#include <stdint.h>
#include <stdio.h>
#include <algorithm>
#include <cmath>
static uint8_t to_byte(double v)
{
return (uint8_t)std::lround(std::clamp(v, 0.0, 1.0) * 255.0);
}
bool PngImageWriter::write(const Image & image)
{
vector<uint8_t> buffer(image.width * image.height * 4u);
for (size_t i = 0u; i < image.pixels.size(); i++)
{
const Color & c = image.pixels[i];
buffer[i * 4u + 0u] = to_byte(c.r);
buffer[i * 4u + 1u] = to_byte(c.g);
buffer[i * 4u + 2u] = to_byte(c.b);
buffer[i * 4u + 3u] = to_byte(c.a);
}
png_image png = {};
png.version = PNG_IMAGE_VERSION;
png.width = image.width;
png.height = image.height;
png.format = PNG_FORMAT_RGBA;
if (!png_image_write_to_file(&png, fname.c_str(), 0, buffer.data(), 0, nullptr))
{
fprintf(stderr, "Error writing PNG file '%s': %s\n", fname.c_str(), png.message);
png_image_free(&png);
return false;
}
return true;
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <fart/ImageWriter.h>
class PngImageWriter : public ImageWriter
{
public:
PngImageWriter(const string & fname) : ImageWriter(fname) {}
bool write(const Image & image) override;
};
+44
View File
@@ -0,0 +1,44 @@
#include <fart/RenderParams.h>
#include <thread>
void RenderParams::merge(const RenderParams & other)
{
if (!width)
{
width = other.width;
}
if (!height)
{
height = other.height;
}
if (!threads)
{
threads = other.threads;
}
if (!background)
{
background = other.background;
}
}
void RenderParams::apply_defaults()
{
if (!width)
{
width = 800u;
}
if (!height)
{
height = 600u;
}
if (!threads)
{
/* hardware_concurrency() may return 0 if it cannot be determined. */
size_t n = std::thread::hardware_concurrency();
threads = n > 0u ? n : 1u;
}
if (!background)
{
background = Color{0.0, 0.0, 0.0, 1.0};
}
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <fart/Color.h>
#include <stddef.h>
struct RenderParams
{
opt<size_t> width;
opt<size_t> height;
opt<size_t> threads;
opt<Color> background;
/* Set each field that is not already set to the value from other. */
void merge(const RenderParams & other);
/* Fill in any unset fields with default values. */
void apply_defaults();
};
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <fart/RenderParams.h>
class Scene
{
public:
RenderParams params;
};
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <optional>
#include <string>
#include <string_view>
#include <vector>
using string = std::string;
using string_view = std::string_view;
template <typename T> using opt = std::optional<T>;
template <typename T> using vector = std::vector<T>;
+20
View File
@@ -0,0 +1,20 @@
#include <fart/Cli.h>
#include <fart/Engine.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
Cli cli;
int rc = cli.run(argc, argv);
if (rc > 0)
{
return rc - 1;
}
if (cli.input_file.empty())
{
fprintf(stderr, "Error: no input file specified\n");
return 1;
}
Engine engine(cli.params);
return engine.run(cli.input_file, cli.output_file);
}
+32
View File
@@ -0,0 +1,32 @@
#include <fart/read_file.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
opt<vector<uint8_t>> read_file(string_view fname)
{
string path(fname);
FILE * fh = fopen(path.c_str(), "rb");
if (fh == nullptr)
{
fprintf(stderr, "Error opening input file '%s': %s\n", path.c_str(), strerror(errno));
return std::nullopt;
}
vector<uint8_t> buffer;
uint8_t chunk[4096];
size_t n;
while ((n = fread(chunk, 1u, sizeof(chunk), fh)) > 0u)
{
buffer.insert(buffer.end(), chunk, chunk + n);
}
if (ferror(fh))
{
fprintf(stderr, "Error reading input file '%s': %s\n", path.c_str(), strerror(errno));
fclose(fh);
return std::nullopt;
}
fclose(fh);
return buffer;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
opt<vector<uint8_t>> read_file(string_view fname);
-130
View File
@@ -1,130 +0,0 @@
// BMP.cc
// extracts/inserts data from/into .bmp file
// Adapted by Josh Holtrop from the original access_bmp.c by gw
// Supports reading and creating 24-bit color BMP images
#include "BMP.h"
#include <stdio.h>
#include <iostream>
using namespace std;
BMP::BMP(const char * fileName)
{
open(fileName);
}
BMP::BMP(const char * fileName, int width, int height, unsigned char * data)
{
create(fileName, width, height, data);
}
BMP::~BMP()
{
if (m_fp != NULL)
close();
}
bool BMP::open(const char * fileName)
{
size_t bytes_read;
m_fp = fopen(fileName, "r+");
if (m_fp != NULL)
{
// read file header
bytes_read = fread(&m_header, sizeof(m_header), 1, m_fp);
if (m_header.id[0] != 'B' || m_header.id[1] != 'M')
{
cerr << fileName << " does not appear to be a BMP file." << endl;
fclose(m_fp);
m_fp = NULL;
}
// read image information
bytes_read = fread(&m_info, sizeof(m_info), 1, m_fp);
}
(void)bytes_read;
return (m_fp != NULL);
}
bool BMP::create(const char * fileName, int width, int height,
unsigned char * data)
{
size_t bytes_written;
m_fp = fopen(fileName, "w+");
if (m_fp != NULL)
{
/* Initialize the header structure */
m_header.id[0] = 'B';
m_header.id[1] = 'M';
int row_padding = (3 * width) & 0x3;
if (row_padding)
row_padding = 4 - row_padding;
int row_bytes = (3 * width) + row_padding;
m_header.file_size = sizeof(m_header) +
sizeof(m_info) +
row_bytes * height;
m_header.reserved = 0;
m_header.offset = sizeof(m_header) + sizeof(m_info);
/* Initialize the information structure */
m_info.header_size = 40;
m_info.width = width;
m_info.height = height;
m_info.color_planes = 1;
m_info.color_depth = 24;
m_info.compression = 0;
m_info.image_size = 0;
m_info.xresolution = 2835;
m_info.yresolution = 2835;
m_info.num_colors = 0;
m_info.num_important_colors = 0;
/* write them to the file */
bytes_written = fwrite(&m_header, sizeof(m_header), 1, m_fp);
bytes_written = fwrite(&m_info, sizeof(m_info), 1, m_fp);
unsigned int zero = 0;
unsigned char * data_ptr = data + (3 * width) * (height - 1);
for (int i = 0; i < height; i++)
{
bytes_written = fwrite(data_ptr, 3 * width, 1, m_fp);
if (row_padding)
bytes_written = fwrite(&zero, row_padding, 1, m_fp);
data_ptr -= 3 * width;
}
}
(void)bytes_written;
return (m_fp != NULL);
}
void BMP::read(unsigned char * buf)
{
size_t bytes_read;
if (m_fp != NULL)
{
int row_data_bytes = 3 * m_info.width;
int row_padding = row_data_bytes & 0x3;
if (row_padding)
row_padding = 4 - row_padding;
fseek(m_fp,
(m_header.offset != 0
? m_header.offset
: sizeof(m_header) + sizeof(m_info)),
SEEK_SET);
unsigned char * data_ptr = buf + row_data_bytes * (m_info.height - 1);
for (int i = 0; i < m_info.height; i++)
{
bytes_read = fread(data_ptr, row_data_bytes, 1, m_fp);
if (row_padding)
fseek(m_fp, row_padding, SEEK_CUR);
data_ptr -= row_data_bytes;
}
}
(void)bytes_read;
}
void BMP::close()
{
if (m_fp != NULL)
fclose(m_fp);
m_fp = NULL;
}
-54
View File
@@ -1,54 +0,0 @@
// BMP.h
// extracts/inserts data from/into .bmp file
// Adapted by Josh Holtrop from the original access_bmp.c by gw
// Supports reading and creating 24-bit color BMP images
#include <stdio.h>
#define BMP_RED 2
#define BMP_GREEN 1
#define BMP_BLUE 0
class BMP
{
public:
typedef struct
{
char id[2];
int file_size;
int reserved;
int offset;
} __attribute__ ((packed)) header_t;
typedef struct
{
int header_size;
int width;
int height;
unsigned short int color_planes;
unsigned short int color_depth;
unsigned int compression;
int image_size;
int xresolution;
int yresolution;
int num_colors;
int num_important_colors;
} __attribute__ ((packed)) info_t;
BMP(const char * fileName);
BMP(const char * fileName, int width, int height, unsigned char * data);
~BMP();
void read(unsigned char * buf);
int getWidth() { return m_info.width; }
int getHeight() { return m_info.height; }
protected:
FILE * m_fp;
header_t m_header;
info_t m_info;
bool open(const char * fileName);
bool create(const char * fileName, int width, int height,
unsigned char * data);
void close();
};
-20
View File
@@ -1,20 +0,0 @@
#include "Light.h"
#include "util/Vector.h"
#include "util/Ray.h"
Light::Light()
{
m_position = Vector(0, 0, 0);
m_diffuse_color = Color::white;
m_specular_color = Color::white;
m_jitter = 1;
m_radius = 1.0;
}
Vector Light::getJitterPosition(int index) const
{
if (index == 0)
return m_position;
return m_position + Vector::randomVector() * m_radius;
}
-48
View File
@@ -1,48 +0,0 @@
#ifndef LIGHT_H
#define LIGHT_H LIGHT_H
#include "util/Vector.h"
#include "util/Color.h"
class Light
{
public:
Light();
void setPosition(const Vector & position) { m_position = position; }
void setPosition(refptr<Vector> vec) { setPosition(*vec); }
const Vector & getPosition() const { return m_position; }
Vector getJitterPosition(int index) const;
void setDiffuseColor(const Color & diffuse)
{
m_diffuse_color = diffuse;
}
const Color & getDiffuseColor() const { return m_diffuse_color; }
void setSpecularColor(const Color & specular)
{
m_specular_color = specular;
}
const Color & getSpecularColor() const { return m_specular_color; }
void setJitter(int j) { m_jitter = j >= 1 ? j : 1; }
int getJitter() const { return m_jitter; }
void setRadius(double r) { m_radius = r; }
double getRadius() const { return m_radius; }
protected:
Vector m_position;
Color m_diffuse_color;
Color m_specular_color;
double m_radius;
int m_jitter;
};
#include "PointLight.h"
#endif
-7
View File
@@ -1,7 +0,0 @@
#include "PointLight.h"
PointLight::PointLight()
: Light()
{
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef POINTLIGHT_H
#define POINTLIGHT_H POINTLIGHT_H
#include "Light.h"
#include "util/Color.h"
class PointLight : public Light
{
public:
PointLight();
protected:
};
#endif
-839
View File
@@ -1,839 +0,0 @@
#include <math.h>
#include <typeinfo>
#include <iostream>
#include "Scene.h"
#include "Light.h"
#include "util/stb_image.h"
#include "parser/parser.h"
#include "parser/nodes.h"
#include "util/Polygon.h"
#include "util/Scope.h"
using namespace std;
typedef vector< refptr<Node> >::const_iterator Node_Iterator;
#ifdef NODE_TREE_DEBUG
static void dumpNodeTree(refptr<Node> node)
{
static int depth = 0;
for (int i = 0; i < depth; i++)
cerr << ' ';
cerr << typeid(*node).name() << endl;
depth++;
for (Node_Iterator it = node->getChildren().begin(); it != node->getChildren().end(); it++)
{
dumpNodeTree(*it);
}
depth--;
}
#endif
void Scene::load(const char * filename)
{
refptr<Scope> scope = new Scope();
refptr<Node> node = parse(filename, scope);
#ifdef NODE_TREE_DEBUG
dumpNodeTree(node);
#endif
if ( ! node.isNull() )
{
/* evaluate any scripting nodes in the node tree */
refptr<Node> processed_scene = node->evaluate();
node->evaluateChildren(processed_scene);
/* now we have a new node tree under 'dummy' with no scripting nodes */
processScene(processed_scene);
}
}
void Scene::processScene(refptr<Node> node)
{
/* first process any cameras present */
int cameras_found = 0;
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(CameraNode) )
{
cameras_found++;
if (cameras_found == 1)
processCamera(*it);
else
{
cerr << "Error: multiple camera definitions found!" << endl;
exit(5);
}
}
}
/* then any other scene-specific items */
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(LightNode) )
{
refptr<Light> light = processLight(*it);
if ( ! light.isNull() )
m_lights.push_back(light);
}
else if ( typeid(**it) == typeid(OptionsNode) )
{
processOptions(*it);
}
}
/* then any general items */
vector<ShapeRef> shapes = processGeneralItems(node);
for (vector<ShapeRef>::iterator it = shapes.begin();
it != shapes.end();
it++)
{
m_shapes.push_back(*it);
}
}
ShapeRef Scene::processShape(refptr<Node> node)
{
if ( typeid(*node) == typeid(BoxNode) )
{
return processBox(node);
}
else if ( typeid(*node) == typeid(PlaneNode) )
{
return processPlane(node);
}
else if ( typeid(*node) == typeid(SphereNode) )
{
return processSphere(node);
}
else if ( typeid(*node) == typeid(CylNode) )
{
return processCyl(node);
}
else if ( typeid(*node) == typeid(IntersectNode)
|| typeid(*node) == typeid(UnionNode)
|| typeid(*node) == typeid(SubtractNode) )
{
return processBool(node);
}
else if ( typeid(*node) == typeid(ExtrudeNode) )
{
return processExtrude(node);
}
else if ( typeid(*node) == typeid(ShapeRefNode) )
{
return processShapeRef(node);
}
else
{
cerr << "Error: Unknown shape!" << endl;
exit(3);
}
return ShapeRef(NULL);
}
void Scene::processCamera(refptr<Node> node)
{
Vector position(0, 0, 0);
Vector look_at(0, 1, 0);
Vector up(0, 0, 1);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(PositionNode) )
{
position = * (*it)->getVector();
}
else if ( typeid(**it) == typeid(LookAtNode) )
{
look_at = * (*it)->getVector();
}
else if ( typeid(**it) == typeid(UpNode) )
{
up = * (*it)->getVector();
}
else if ( typeid(**it) == typeid(VFOVNode) )
{
m_vfov = (*it)->getNumber();
}
}
m_transforms.top().lookAt(position, look_at, up);
}
void Scene::processOptions(refptr<Node> node)
{
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(WidthNode) )
{
m_width = (*it)->getInteger();
}
else if ( typeid(**it) == typeid(HeightNode) )
{
m_height = (*it)->getInteger();
}
else if ( typeid(**it) == typeid(ExposureNode) )
{
m_exposure = (*it)->getNumber();
if (m_exposure < 0.0)
m_exposure = 0.0;
}
else if ( typeid(**it) == typeid(MultisampleNode) )
{
m_multisample_level = (*it)->getInteger();
}
else if ( typeid(**it) == typeid(MaxDepthNode) )
{
m_max_depth = (*it)->getInteger();
}
else if ( typeid(**it) == typeid(AmbientNode) )
{
setAmbientLight(Color((*it)->getVector()));
}
else if ( typeid(**it) == typeid(AmbientOcclusionNode) )
{
m_ambient_occlusion_level = (*it)->getInteger();
}
}
}
vector<ShapeRef> Scene::processTransformBlock(refptr<Node> node)
{
if ( typeid(*node) == typeid(TranslateBlockNode) )
{
m_transforms.push(m_transforms.top());
m_transforms.top().translate(node->getVector());
}
else if ( typeid(*node) == typeid(RotateBlockNode) )
{
m_transforms.push(m_transforms.top());
m_transforms.top().rotate(node->getNumber(),
node->getVector());
}
else if ( typeid(*node) == typeid(ScaleBlockNode) )
{
m_transforms.push(m_transforms.top());
m_transforms.top().scale(node->getVector());
}
else
{
cerr << "Unknown transformation block node type!" << endl;
exit(4);
}
vector<ShapeRef> shapes = processGeneralItems(node);
m_transforms.pop();
return shapes;
}
vector<ShapeRef> Scene::processGeneralItems(refptr<Node> node)
{
vector<ShapeRef> shapes, incoming;
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ((*it)->isTransformBlock())
{
incoming = processTransformBlock(*it);
}
else if ( typeid(**it) == typeid(MaterialDefinitionNode) )
{
processMaterialDefinition(*it);
}
else if ( typeid(**it) == typeid(ShapeDefinitionNode) )
{
processShapeDefinition(*it);
}
else if ( (*it)->isShape() )
{
shapes.push_back(processShape(*it));
}
while (incoming.size() > 0)
{
shapes.push_back(incoming[0]);
incoming.erase(incoming.begin());
}
}
return shapes;
}
void Scene::processMaterialDefinition(refptr<Node> node)
{
map< string, refptr<Material> >::iterator it =
m_materials.find(node->getString());
if ( it == m_materials.end() )
{
m_materials[node->getString()] = processMaterial(node);
}
else
{
cerr << "Error: duplicate material definition for material '"
<< node->getString() << "'" << endl;
exit(4);
}
}
refptr<Material> Scene::processMaterial(refptr<Node> node)
{
if ( typeid(*node) == typeid(MaterialRefNode) )
{
map< string, refptr<Material> >::iterator it =
m_materials.find(node->getString());
if ( it == m_materials.end() )
{
cerr << "Undefined material '" << node->getString()
<< "' requested!" << endl;
exit(4);
}
return m_materials[node->getString()];
}
refptr<Material> material = new Material();
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(ColorNode) )
{
material->setDiffuseColor(Color((*it)->getVector()));
material->setAmbientColor(Color((*it)->getVector()));
}
else if ( typeid(**it) == typeid(AmbientNode) )
{
material->setAmbientColor(Color((*it)->getVector()));
}
else if ( typeid(**it) == typeid(DiffuseNode) )
{
material->setDiffuseColor(Color((*it)->getVector()));
}
else if ( typeid(**it) == typeid(SpecularNode) )
{
material->setSpecularColor(Color((*it)->getVector()));
}
else if ( typeid(**it) == typeid(ReflectanceNode) )
{
material->setReflectance((*it)->getNumber());
}
else if ( typeid(**it) == typeid(RefractionNode) )
{
material->setRefraction((*it)->getNumber());
}
else if ( typeid(**it) == typeid(ShininessNode) )
{
material->setShininess((*it)->getNumber());
}
else if ( typeid(**it) == typeid(TransparencyNode) )
{
material->setTransparency((*it)->getNumber());
}
else if ( typeid(**it) == typeid(TextureNode) )
{
string filename = (*it)->getString();
Texture * texture = NULL;
if (m_textures.find(filename) != m_textures.end())
{
/* texture already loaded */
texture = m_textures[filename];
}
else
{
texture = loadTexture(filename);
if (texture != NULL)
{
m_textures[filename] = texture;
}
}
if (texture != NULL)
{
material->setTexture(texture);
}
}
}
return material;
}
ShapeRef Scene::processBox(refptr<Node> node)
{
refptr<Vector> size = new Vector(1, 1, 1);
refptr<Material> material;
bool restore_transform = processTransforms(node);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(SizeNode) )
{
size = (*it)->getVector();
}
else if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
}
ShapeRef box = new Box(size);
if ( ! material.isNull() )
box->setMaterial(material);
box->setTransform(m_transforms.top());
if (restore_transform)
m_transforms.pop();
return box;
}
ShapeRef Scene::processCyl(refptr<Node> node)
{
double radius1 = 1.0;
double radius2 = 1.0;
double height = 1.0;
refptr<Material> material;
bool restore_transform = processTransforms(node);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(SizeNode) )
{
refptr<Vector> v = (*it)->getVector();
radius1 = (*v)[0];
radius2 = (*v)[1];
height = (*v)[2];
}
else if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
}
ShapeRef cyl = new Cyl(radius1, radius2, height);
if ( ! material.isNull() )
cyl->setMaterial(material);
cyl->setTransform(m_transforms.top());
if (restore_transform)
m_transforms.pop();
return cyl;
}
refptr<Light> Scene::processLight(refptr<Node> node)
{
refptr<Light> light = new PointLight();
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(PositionNode) )
{
refptr<Vector> v = (*it)->getVector();
light->setPosition(m_transforms.top().transform_point(*v));
}
else if ( typeid(**it) == typeid(DiffuseNode) )
{
Color c((*it)->getVector());
light->setDiffuseColor(c);
}
else if ( typeid(**it) == typeid(SpecularNode) )
{
Color c((*it)->getVector());
light->setSpecularColor(c);
}
else if ( typeid(**it) == typeid(ColorNode) )
{
Color c((*it)->getVector());
light->setDiffuseColor(c);
light->setSpecularColor(c);
}
else if ( typeid(**it) == typeid(RadiusNode) )
{
light->setRadius((*it)->getNumber());
}
else if ( typeid(**it) == typeid(JitterNode) )
{
light->setJitter((*it)->getInteger());
}
}
return light;
}
ShapeRef Scene::processPlane(refptr<Node> node)
{
Vector normal(0, 0, 1);
double dist = 0;
refptr<Material> material;
bool restore_transform = processTransforms(node);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(PlanePositionNode) )
{
normal = *(*it)->getVector();
dist = (*it)->getNumber();
}
else if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
}
ShapeRef plane = new Plane(normal[0],
normal[1],
normal[2],
dist);
if ( ! material.isNull() )
plane->setMaterial(material);
plane->setTransform(m_transforms.top());
if (restore_transform)
m_transforms.pop();
return plane;
}
ShapeRef Scene::processSphere(refptr<Node> node)
{
double radius = 1.0;
refptr<Material> material;
bool restore_transform = processTransforms(node);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(RadiusNode) )
{
radius = (*it)->getNumber();
}
else if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
}
ShapeRef sphere = new Sphere(radius);
if ( ! material.isNull() )
sphere->setMaterial(material);
sphere->setTransform(m_transforms.top());
if (restore_transform)
m_transforms.pop();
return sphere;
}
ShapeRef Scene::processBool(refptr<Node> node)
{
vector<ShapeRef> shapes;
refptr<Material> material;
bool restore_transform = processTransforms(node);
m_transforms.push(Transform());
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( (*it)->isShape() )
{
ShapeRef shape = processShape(*it);
if ( ! shape.isNull() )
shapes.push_back(shape);
}
else if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
else if ( (*it)->isTransformBlock() )
{
vector<ShapeRef> in = processTransformBlock(*it);
for (int i = 0, sz = in.size(); i < sz; i++)
{
shapes.push_back(in[i]);
}
}
else if ( typeid(**it) == typeid(ShapeDefinitionNode) )
{
processShapeDefinition(*it);
}
}
if (shapes.size() < 2)
{
cerr << "Error: boolean objects must have 2 or more sub-objects!"
<< endl;
exit(3);
}
ShapeRef shape;
if ( typeid(*node) == typeid(IntersectNode) )
shape = new Intersect(shapes);
else if ( typeid(*node) == typeid(UnionNode) )
shape = new Union(shapes);
else if ( typeid(*node) == typeid(SubtractNode) )
shape = new Subtract(shapes);
else
{
cerr << __FILE__ << ": " << __LINE__
<< ": error: bool object unrecognized" << endl;
exit(3);
}
if ( ! material.isNull() )
shape->setMaterial(material);
m_transforms.pop();
shape->setTransform(m_transforms.top());
if (restore_transform)
m_transforms.pop();
return shape;
}
ShapeRef Scene::processExtrude(refptr<Node> node)
{
refptr<Material> material;
Extrude * extrude = new Extrude();
bool restore_transform = processTransforms(node);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(PolygonNode) )
{
extrude->addPolygon(processPolygon(*it));
}
else if ( typeid(**it) == typeid(NGonNode) )
{
extrude->addPolygon(processNGon(*it));
}
else if ( typeid(**it) == typeid(OffsetNode) )
{
double distance = (*it)->getNumber();
if (distance <= 0.0)
{
cerr << "Error: extrude distance must be positive" << endl;
exit(3);
}
Vector scale(1, 1, 1);
Vector position(0, 0, 0);
for (Node_Iterator it2 = (*it)->getChildren().begin();
it2 != (*it)->getChildren().end();
it2++)
{
if ( typeid(**it2) == typeid(ScaleNode) )
{
scale = * (*it2)->getVector();
}
else if ( typeid(**it2) == typeid(PositionNode) )
{
position = * (*it2)->getVector();
}
}
if (scale[0] < 0.0 || scale[1] < 0.0)
{
cerr << "Error: extrude scale cannot be negative" << endl;
exit(3);
}
extrude->addOffset(distance, scale, position);
}
else if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
}
if ( ! material.isNull() )
extrude->setMaterial(material);
extrude->setTransform(m_transforms.top());
if (restore_transform)
m_transforms.pop();
return extrude;
}
refptr<Polygon> Scene::processPolygon(refptr<Node> node)
{
refptr<Polygon> p = new Polygon();
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(VectorNode) )
{
p->push_back((*it)->getVector());
}
else
{
cerr << "Error: Unknown polygon sub-object" << endl;
exit(3);
}
}
if (p->size() < 3)
{
cerr << "Error: Polygon with fewer than three points!" << endl;
exit(3);
}
return p;
}
refptr<Polygon> Scene::processNGon(refptr<Node> node)
{
refptr<Polygon> p = new Polygon();
double radius = node->getChildren()[0]->getNumber();
int n = node->getInteger();
int step = n < 0 ? -1 : 1;
n = abs(n);
if (n < 3)
n = 3;
int pos = 0;
double astep = 2.0 * M_PI / n;
for (int i = 0; i < n; i++)
{
p->push_back(new Vector(
radius * cos(pos * astep),
radius * sin(pos * astep),
0.0));
pos += step;
}
return p;
}
ShapeRef Scene::processShapeRef(refptr<Node> node)
{
if (m_shape_definitions.find(node->getString())
== m_shape_definitions.end())
{
cerr << "Error: no shape definition for '" << node->getString()
<< "' found!" << endl;
exit(3);
}
refptr<Material> material;
ShapeRef shape = m_shape_definitions[node->getString()]->clone();
bool restore_transform = processTransforms(node);
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( (*it)->isMaterial() )
{
material = processMaterial(*it);
}
}
if ( ! material.isNull() )
shape->setMaterial(material);
shape->setTransform(m_transforms.top() * shape->getTransform());
if (restore_transform)
m_transforms.pop();
return shape;
}
bool Scene::processTransforms(refptr<Node> node)
{
bool did_any = false;
for (Node_Iterator it = node->getChildren().begin();
it != node->getChildren().end();
it++)
{
if ( typeid(**it) == typeid(TranslateNode) )
{
if (did_any == false)
{
m_transforms.push(m_transforms.top());
did_any = true;
}
m_transforms.top().translate((*it)->getVector());
}
else if ( typeid(**it) == typeid(RotateNode) )
{
if (did_any == false)
{
m_transforms.push(m_transforms.top());
did_any = true;
}
m_transforms.top().rotate((*it)->getNumber(),
(*it)->getVector());
}
else if ( typeid(**it) == typeid(ScaleNode) )
{
if (did_any == false)
{
m_transforms.push(m_transforms.top());
did_any = true;
}
m_transforms.top().scale((*it)->getVector());
}
}
return did_any;
}
void Scene::processShapeDefinition(refptr<Node> node)
{
m_transforms.push(Transform());
m_shape_definitions[node->getString()]
= processShape(node->getChildren()[0]);
m_transforms.pop();
}
Texture * Scene::loadTexture(const std::string & filename)
{
int width = 0;
int height = 0;
int channels = 0;
/* request 4 channels so the data is always tightly packed RGBA */
unsigned char * data = stbi_load(filename.c_str(), &width, &height,
&channels, 4);
if (data == NULL)
{
cerr << "Error: image \"" << filename << "\" could not be loaded: "
<< stbi_failure_reason() << endl;
return NULL;
}
Texture * texture = new Texture;
texture->width = width;
texture->height = height;
texture->channels = 4;
texture->data = data;
return texture;
}
-406
View File
@@ -1,406 +0,0 @@
#include <math.h> /* exp(), pow(), M_PI */
#include <stdlib.h>
#include <string>
#include <vector>
#include <utility> /* pair */
#include <map>
#include <algorithm> /* sort() */
#include <typeinfo> /* typeid operator support */
#include "Scene.h"
#include "BMP.h"
#include "util/stb_image.h"
#include "util/Color.h"
#include "shapes/Shape.h"
#include "Light.h"
using namespace std;
#define MAX_AMBIENT_OCCLUSION_DISTANCE 50.0
Scene::Scene(const map<string, const char *> & options,
const char * filename)
{
m_width = 800;
m_height = 600;
m_multisample_level = 1;
m_vfov = 60.0;
m_ambient_light = Color(0.2, 0.2, 0.2);
m_max_depth = 10;
m_exposure = 1.0f;
m_ambient_occlusion_level = 0;
m_transforms.push(Transform());
load(filename);
/* after loading the scene file, apply any command-line render options */
for (map<const string, const char *>::const_iterator it = options.begin();
it != options.end();
it++)
{
if (it->first == "width")
{
m_width = atoi(it->second);
}
else if (it->first == "height")
{
m_height = atoi(it->second);
}
else if (it->first == "multisample")
{
m_multisample_level = atoi(it->second);
}
else if (it->first == "field-of-view")
{
m_vfov = atof(it->second);
}
else if (it->first == "max-depth")
{
m_max_depth = atoi(it->second);
}
else if (it->first == "ambient-occlusion")
{
m_ambient_occlusion_level = atoi(it->second);
}
}
/* view plane distance is calculated based on the field of view */
m_view_plane_dist = (m_height / 2.0) / tan(M_PI * m_vfov / 360.0);
m_sample_span = 1.0 / m_multisample_level;
m_half_sample_span = m_sample_span / 2.0;
m_multisample_level_squared = m_multisample_level * m_multisample_level;
}
Scene::~Scene()
{
/* clean up any textures loaded with stb_image */
for (std::map< std::string, Texture * >::iterator it = m_textures.begin();
it != m_textures.end();
it++)
{
stbi_image_free(it->second->data);
delete it->second;
}
}
void Scene::renderPixel(int x, int y, unsigned char * pixel)
{
/* calculate the ray going from the camera through this pixel */
Color finalColor;
for (int i = 0; i < m_multisample_level; i++)
{
for (int j = 0; j < m_multisample_level; j++)
{
double rx = (x + i * m_sample_span + m_half_sample_span)
- (m_width / 2.0);
double rz = (m_height / 2.0)
- (y + j * m_sample_span + m_half_sample_span);
Ray ray(Vector(0, 0, 0), Vector(rx, m_view_plane_dist, rz));
finalColor += traceRay(ray);
}
}
/* apply exposure formula so we aren't saturated */
finalColor.r = 1.0f -
exp(-m_exposure * finalColor.r / m_multisample_level_squared);
finalColor.g = 1.0f -
exp(-m_exposure * finalColor.g / m_multisample_level_squared);
finalColor.b = 1.0f -
exp(-m_exposure * finalColor.b / m_multisample_level_squared);
#if 0
/* gamma correct */
finalColor.r = pow(finalColor.r, 1 / 2.2);
finalColor.g = pow(finalColor.g, 1 / 2.2);
finalColor.b = pow(finalColor.b, 1 / 2.2);
#endif
/* take the average of all the samples as the final pixel value */
pixel[BMP_RED] = (unsigned char) (0xFF * finalColor.r);
pixel[BMP_GREEN] = (unsigned char) (0xFF * finalColor.g);
pixel[BMP_BLUE] = (unsigned char) (0xFF * finalColor.b);
}
Color Scene::traceRay(const Ray & ray)
{
return traceRayRecurse(ray, m_max_depth, 1.0, NULL);
}
/**
* factor: the proportion of the final color that this computation is worth
*/
Color Scene::traceRayRecurse(const Ray & ray, int depth, double factor,
refptr<Material> last_material)
{
static refptr<Material> air = new Material();
Color color(0, 0, 0);
if (last_material.isNull())
last_material = air;
Shape::Intersection hit = getRayClosestHit(ray);
if ( ! hit.shape.isNull() )
{
/* compute the Phong lighting for each hit */
refptr<Material> material = hit.shape->getMaterial();
/* check for backfaces */
bool frontface = ray.getDirection() % hit.normal < 0.0;
if (frontface)
{
color = computePhong(material,
ray,
hit.position,
hit.normal);
if (depth > 0 && factor > SCENE_FACTOR_THRESHOLD)
{
double reflectance = material->getReflectance();
if (factor * reflectance > SCENE_FACTOR_THRESHOLD)
{
color *= (1.0 - reflectance);
Vector reflected_direction =
ray.getDirection().reflect(hit.normal);
Ray newRay(hit.position, reflected_direction);
Vector jitter_surface_point = newRay[0.0001];
Ray jitterNewRay(jitter_surface_point, reflected_direction);
Color c = traceRayRecurse(jitterNewRay,
depth - 1,
factor * reflectance,
material);
color += c * reflectance;
}
double transparency = material->getTransparency();
if (factor * transparency > SCENE_FACTOR_THRESHOLD)
{
color *= (1.0 - transparency);
Vector jitter_surface_point = hit.position
+ ray.getDirection() * 0.0001;
Vector refracted_direction =
ray.getDirection().refract(hit.normal,
last_material->getRefraction(),
material->getRefraction());
Ray newRay(jitter_surface_point, refracted_direction);
Color c = traceRayRecurse(newRay,
depth - 1,
factor * transparency,
material);
color += c * transparency;
}
}
}
else
{
material = air;
Vector jitter_surface_point = hit.position
+ ray.getDirection() * 0.0001;
Vector refracted_direction =
ray.getDirection().refract(-hit.normal,
last_material->getRefraction(),
material->getRefraction());
Ray newRay(jitter_surface_point, refracted_direction);
color = traceRayRecurse(newRay, depth, factor, material);
}
}
return color;
}
Shape::Intersection Scene::getRayClosestHit(const Ray & ray)
{
Shape::Intersection hit;
double min_dist = 0.0;
bool foundOne = false;
/* loop through all shapes in the scene */
for (vector< refptr<Shape> >::iterator it = m_shapes.begin();
it != m_shapes.end();
it++)
{
Shape::IntersectionList intersections = (*it)->intersect(*it, ray);
for (int i = 0, num_results = intersections.size();
i < num_results;
i++)
{
refptr<Shape> shape = intersections[i].shape;
const Vector & isect_point = intersections[i].position;
double intersect_dist = ray.getOrigin().dist_to(isect_point);
if (foundOne == false || intersect_dist < min_dist)
{
hit = intersections[i];
min_dist = intersect_dist;
foundOne = true;
}
}
}
return hit;
}
Color Scene::computePhong(const refptr<Material> material,
const Ray & viewRay,
const Vector & surfacePoint,
const Vector & surfaceNormal)
{
Color result = m_ambient_light * material->getAmbientColor();
if (m_ambient_occlusion_level > 0)
{
result *= calculateAmbientOcclusion(
Ray(surfacePoint, surfaceNormal).shift(1e-7));
}
Vector viewDirection = -viewRay.getDirection();
double shininess = material->getShininess();
const Color & diffuseColor = material->getDiffuseColor();
const Color & specularColor = material->getSpecularColor();
for (std::vector< refptr<Light> >::const_iterator it = m_lights.begin();
it != m_lights.end();
it++)
{
Vector lightC = (*it)->getPosition();
double lightRadius = (*it)->getRadius();
Vector directionToLightC = lightC - surfacePoint;
Vector lightPlaneX = directionToLightC.getPerpendicular().normalize();
Vector lightPlaneY = (directionToLightC * lightPlaneX).normalize();
int jitter_samples = 0, jitter_level = (*it)->getJitter();;
Color jitterResult;
for (int jitter_index = 0; jitter_index < jitter_level; jitter_index++)
{
double jitterRadius = jitter_index * lightRadius
/ (jitter_level - 0.5);
for (int i = 0, num = (int) (M_PI * jitter_index) + 1; i < num; i++)
{
jitter_samples++;
double jitter_angle = i * 2.0 * M_PI / num;
Vector jitterPosition = lightC
+ lightPlaneX * jitterRadius * cos(jitter_angle)
+ lightPlaneY * jitterRadius * sin(jitter_angle);
Vector directionToLight = jitterPosition - surfacePoint;
directionToLight.normalize();
Vector reflectedLightDirection =
(-directionToLight).reflect(surfaceNormal);
Ray surfaceToLight(surfacePoint, directionToLight);
Color light_contribution =
calculateLightContribution(surfaceToLight.shift(0.0001),
jitterPosition);
if ( light_contribution.r > 0.0
|| light_contribution.g > 0.0
|| light_contribution.b > 0.0 )
{
/* calculate the diffuse term */
double diffuse_coef = directionToLight % surfaceNormal;
if (diffuse_coef > 0.0)
{
jitterResult += diffuseColor
* (*it)->getDiffuseColor()
* diffuse_coef
* light_contribution;
}
/* calculate the specular term */
double specular_coef = reflectedLightDirection % viewDirection;
if (specular_coef > 0.0)
{
jitterResult += specularColor
* (*it)->getSpecularColor()
* pow(specular_coef, shininess)
* light_contribution;
}
}
}
}
jitterResult /= jitter_samples;
result += jitterResult;
}
return result;
}
Color Scene::calculateLightContribution(const Ray & toLight,
const Vector & lightPosition)
{
Color contrib(1.0, 1.0, 1.0);
double dist_to_light = (lightPosition - toLight.getOrigin()).mag();
double dist_so_far = 0.0;
Ray currentRay = toLight;
for (;;)
{
Shape::Intersection hit = getRayClosestHit(currentRay);
if ( hit.shape.isNull() )
break;
double offset = currentRay.getOrigin().dist_to(hit.position) + 0.0001;
if ( dist_so_far + offset > dist_to_light )
break;
contrib *= hit.shape->getMaterial()->getTransparency();
contrib *= hit.shape->getMaterial()->getDiffuseColor();
if ( contrib.r < SCENE_FACTOR_THRESHOLD
&& contrib.g < SCENE_FACTOR_THRESHOLD
&& contrib.b < SCENE_FACTOR_THRESHOLD )
break;
dist_so_far += offset;
currentRay = currentRay.shift(offset);
}
return contrib;
}
Color Scene::calculateAmbientOcclusion(const Ray & surfaceNormal)
{
Color result(1, 1, 1);
const int nISteps = m_ambient_occlusion_level * 6;
const int nJSteps = nISteps / 2;
int nRays = 0;
Vector perpX = surfaceNormal.getDirection().getPerpendicular().normalize();
Vector perpY = (surfaceNormal.getDirection() * perpX).normalize();
double istep = 2.0 * M_PI / nISteps;
double jstep = M_PI_2 / nJSteps;
for (int i = 0; i < nISteps; i++)
{
int lim = i > 0 ? nJSteps : 1;
for (int j = 0; j < lim; j++)
{
Vector direction = cos(i * istep) * sin(j * jstep) * perpX
+ sin(i * istep) * sin(j * jstep) * perpY
+ cos(j * jstep) * surfaceNormal.getDirection();
Ray thisRay(surfaceNormal.getOrigin(), direction);
double dist = 0.0;
Color contrib(1, 1, 1);
while (contrib.r > 0.2 && contrib.g > 0.2 && contrib.b > 0.2)
{
Shape::Intersection hit = getRayClosestHit(thisRay);
if (hit.shape.isNull())
break;
double hitDist = (hit.position - thisRay.getOrigin()).mag();
dist += hitDist;
if (dist > MAX_AMBIENT_OCCLUSION_DISTANCE)
break;
contrib *= hit.shape->getMaterial()->getTransparency()
* hit.shape->getMaterial()->getDiffuseColor();
thisRay = thisRay.shift(hitDist + 1E-7);
}
result += contrib;
nRays++;
}
}
result /= nRays;
return result;
}
-109
View File
@@ -1,109 +0,0 @@
#ifndef SCENE_H
#define SCENE_H SCENE_H
#include <string>
#include <map>
#include <vector>
#include <utility>
#include <stack>
#include "util/refptr.h"
#include "util/Ray.h"
#include "util/Color.h"
#include "util/Material.h"
#include "util/Polygon.h"
#include "shapes/shapes.h"
#include "parser/parser.h"
#include "parser/nodes.h"
#include "Light.h"
#define SCENE_FACTOR_THRESHOLD 0.02
class Scene
{
public:
Scene(const std::map<std::string, const char *> & options,
const char * filename);
~Scene();
void setWidth(int width) { m_width = width; }
void setHeight(int height) { m_height = height; }
void setMultisampleLevel(int level) { m_multisample_level = level; }
void setVFOV(double vfov) { m_vfov = vfov; }
void setAmbientLight(const Color & al) { m_ambient_light = al; }
void renderPixel(int x, int y, unsigned char * pixel);
void setMaxDepth(int d) { m_max_depth = d; }
int getWidth() { return m_width; }
int getHeight() { return m_height; }
int getMultisampleLevel() { return m_multisample_level; }
int getAmbientOcclusionLevel() { return m_ambient_occlusion_level; }
double getVFOV() { return m_vfov; }
int getMaxDepth() { return m_max_depth; }
protected:
/* private methods */
Color traceRay(const Ray & ray);
Color traceRayRecurse(const Ray & ray, int depth, double factor,
refptr<Material> last_material);
Shape::Intersection getRayClosestHit(const Ray & ray);
Color computePhong(const refptr<Material> material,
const Ray & viewRay,
const Vector & surfacePoint,
const Vector & surfaceNormal);
Color calculateLightContribution(const Ray & toLight,
const Vector & lightPosition);
Color calculateAmbientOcclusion(const Ray & surfaceNormal);
/* In Scene-load.cc */
void load(const char * filename);
void processScene(refptr<Node> node);
refptr<Material> processMaterial(refptr<Node> node);
ShapeRef processBox(refptr<Node> node);
ShapeRef processCyl(refptr<Node> node);
refptr<Light> processLight(refptr<Node> node);
ShapeRef processPlane(refptr<Node> node);
ShapeRef processSphere(refptr<Node> node);
ShapeRef processShape(refptr<Node> node);
ShapeRef processBool(refptr<Node> node);
ShapeRef processExtrude(refptr<Node> node);
ShapeRef processShapeRef(refptr<Node> node);
refptr<Polygon> processPolygon(refptr<Node> node);
refptr<Polygon> processNGon(refptr<Node> node);
bool processTransforms(refptr<Node> node);
void processCamera(refptr<Node> node);
void processOptions(refptr<Node> node);
std::vector<ShapeRef> processTransformBlock(refptr<Node> node);
std::vector<ShapeRef> processGeneralItems(refptr<Node> node);
std::vector<ShapeRef> processForNode(refptr<Node> node);
void processMaterialDefinition(refptr<Node> node);
void processShapeDefinition(refptr<Node> node);
Texture * loadTexture(const std::string & filename);
/* rendering parameters */
int m_width;
int m_height;
int m_multisample_level;
double m_vfov;
Color m_ambient_light;
int m_max_depth;
double m_exposure;
int m_ambient_occlusion_level;
/* private data */
std::vector<ShapeRef> m_shapes;
std::map<std::string, ShapeRef> m_shape_definitions;
std::vector< refptr<Light> > m_lights;
std::stack<Transform> m_transforms;
double m_view_plane_dist;
int m_multisample_level_squared;
double m_sample_span;
double m_half_sample_span;
std::map< std::string, refptr<Material> > m_materials;
std::map< std::string, Texture * > m_textures;
};
#endif
-434
View File
@@ -1,434 +0,0 @@
#include <string.h>
#include <unistd.h> /* sysconf() */
#include <stdlib.h> /* rand(), srand() */
#include <time.h> /* time() */
#include <getopt.h>
#include <sys/time.h> /* gettimeofday() */
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <pthread.h>
#include <atomic>
#include "Scene.h"
#include "distrib/distrib.h"
#include "BMP.h"
using namespace std;
/*
* Number of contiguous pixels each worker thread claims at a time. Pulling
* work in small chunks (rather than whole rows) keeps the load balanced even
* when individual rows vary wildly in render cost, while keeping the claimed
* pixels contiguous so cache locality and dispatch overhead stay close to the
* row-based baseline. See benchmarks: 64 was the sweet spot.
*/
#define RENDER_CHUNK_SIZE 64
/*
* State shared by the local multithreaded renderer. Worker threads pull
* fixed-size chunks of pixels off a lock-free atomic cursor until the image
* is complete.
*/
typedef struct
{
Scene * scene;
unsigned char * data;
int width;
int total_pixels;
std::atomic<int> next_pixel; /* dispatch cursor: next unclaimed pixel */
std::atomic<int> pixels_done; /* pixels finished, for progress */
std::atomic<int> last_permille; /* last tenth-of-a-percent printed */
} render_thread_state_t;
static void * render_thread(void * varg)
{
render_thread_state_t * state = (render_thread_state_t *) varg;
const int width = state->width;
const int total_pixels = state->total_pixels;
for (;;)
{
/* atomically claim the next chunk of contiguous pixels */
int start = state->next_pixel.fetch_add(RENDER_CHUNK_SIZE,
std::memory_order_relaxed);
if (start >= total_pixels)
break;
int end = start + RENDER_CHUNK_SIZE;
if (end > total_pixels)
end = total_pixels;
for (int pixel = start; pixel < end; pixel++)
{
int y = pixel / width;
int x = pixel % width;
state->scene->renderPixel(x, y, &state->data[3 * pixel]);
}
/* report progress without locking: of the threads that advance the
* tenth-of-a-percent counter, only the one that wins the update prints,
* so the display stays monotonic and free of interleaved output */
int done = state->pixels_done.fetch_add(end - start,
std::memory_order_relaxed)
+ (end - start);
int permille = (int) (1000L * done / total_pixels);
int prev = state->last_permille.load(std::memory_order_relaxed);
while (permille > prev)
{
if (state->last_permille.compare_exchange_weak(
prev, permille, std::memory_order_relaxed))
{
printf("\e[8D%2.1f%%", permille / 10.0);
fflush(stdout);
break;
}
/* prev reloaded by compare_exchange_weak; re-test permille > prev */
}
}
return NULL;
}
static void renderThreaded(Scene & scene,
unsigned char * data,
int width,
int height,
int num_threads)
{
/* a non-positive override means auto-detect the available cores */
if (num_threads < 1)
{
num_threads = sysconf(_SC_NPROCESSORS_ONLN);
if (num_threads < 1)
num_threads = 1;
}
render_thread_state_t state;
state.scene = &scene;
state.data = data;
state.width = width;
state.total_pixels = width * height;
state.next_pixel.store(0, std::memory_order_relaxed);
state.pixels_done.store(0, std::memory_order_relaxed);
state.last_permille.store(0, std::memory_order_relaxed);
vector<pthread_t> threads(num_threads);
for (int i = 0; i < num_threads; i++)
pthread_create(&threads[i], NULL, render_thread, &state);
for (int i = 0; i < num_threads; i++)
pthread_join(threads[i], NULL);
printf("\e[8D");
}
void usage(const char * progname)
{
cout << "Usage: " << progname << " [options] <scene-file>" << endl;
cout << " Options:" << endl;
cout << " -o|--output-file <output-file-name>" << endl;
cout << " -w|--width <image-width>" << endl;
cout << " -h|--height <image-height>" << endl;
cout << " -m|--multisample <level>" << endl;
cout << " -f|--field-of-view <vertical-fov>" << endl;
cout << " -d|--max-depth <max-recursion-depth>" << endl;
cout << " -a|--ambient-occlusion <ambient-occlusion-level>" << endl;
cout << " -p|--preview (means -w400 -h300 -m1 -d8 -a0)" << endl;
cout << " -j|--threads <num-threads> (default: number of cores)" << endl;
cout << " --hosts <hosts-file>" << endl;
exit(42);
}
void startChildren(const string & servername,
int serverport,
const vector<string> options)
{
char server_port_str[15];
sprintf(server_port_str, "%d", serverport);
vector<string> args;
args.push_back("fart");
args.push_back("--child");
args.push_back("--host");
args.push_back(servername);
args.push_back("--port");
args.push_back(server_port_str);
for (int i = 0, sz = options.size(); i < sz; i++)
args.push_back(options[i]);
const char * char_star_args[args.size() + 1];
for (int i = 0, sz = args.size(); i < sz; i++)
char_star_args[i] = args[i].c_str();
char_star_args[args.size()] = (char *) NULL;
int num_children = sysconf(_SC_NPROCESSORS_CONF);
num_children--;
#if 0
/* debug */
cout << "executing: 'fart', ";
for (int i = 0, sz = args.size(); i < sz; i++)
cout << "'" << char_star_args[i] << "', ";
cout << endl;
#endif
for (int i = 0; i < num_children; i++)
{
int id = fork();
if (id == 0)
{
/* child process */
execvp("fart", (char * const *) char_star_args);
}
}
}
int main(int argc, char * argv[])
{
int opt;
int option_index;
map<string, const char *> scene_options;
vector<string> client_options;
bool server = true;
bool distributed = false;
const char * hosts_file = NULL;
const char * server_name = NULL;
int server_port = 0;
unsigned char * data = NULL;
const char * output_file_name = "fart.bmp";
bool child = false;
int num_threads = 0; /* 0 means auto-detect available cores */
static const struct option long_options[] = {
{ "ambient-occlusion", required_argument, NULL, 'a' },
{ "output-file", required_argument, NULL, 'o' },
{ "width", required_argument, NULL, 'w' },
{ "height", required_argument, NULL, 'h' },
{ "multisample", required_argument, NULL, 'm' },
{ "field-of-view", required_argument, NULL, 'f' },
{ "max-depth", required_argument, NULL, 'd' },
{ "preview", no_argument, NULL, 'p' },
{ "threads", required_argument, NULL, 'j' },
{ "help", no_argument, NULL, 256 },
{ "host", required_argument, NULL, 257 },
{ "port", required_argument, NULL, 258 },
{ "hosts", required_argument, NULL, 259 },
{ "child", no_argument, NULL, 260 },
{ NULL, 0, NULL, 0 }
};
while ((opt = getopt_long(argc, argv, "a:o:w:h:m:f:d:pj:",
long_options, &option_index)) != -1)
{
switch (opt)
{
case 'a':
scene_options["ambient-occlusion"] = optarg;
client_options.push_back("--ambient-occlusion");
client_options.push_back(optarg);
break;
case 'o':
output_file_name = optarg;
break;
case 'w':
scene_options["width"] = optarg;
client_options.push_back("--width");
client_options.push_back(optarg);
break;
case 'h':
scene_options["height"] = optarg;
client_options.push_back("--height");
client_options.push_back(optarg);
break;
case 'm':
scene_options["multisample"] = optarg;
client_options.push_back("--multisample");
client_options.push_back(optarg);
break;
case 'f':
scene_options["field-of-view"] = optarg;
client_options.push_back("--field-of-view");
client_options.push_back(optarg);
break;
case 'd':
scene_options["max-depth"] = optarg;
client_options.push_back("--max-depth");
client_options.push_back(optarg);
break;
case 'p':
scene_options["ambient-occlusion"] = "0";
scene_options["width"] = "400";
scene_options["height"] = "300";
scene_options["multisample"] = "1";
scene_options["max-depth"] = "8";
client_options.push_back("--preview");
break;
case 'j':
num_threads = atoi(optarg);
break;
case 256:
usage(argv[0]);
break;
case 257:
server_name = optarg;
server = false;
distributed = true;
break;
case 258:
server_port = atoi(optarg);
distributed = true;
break;
case 259:
hosts_file = optarg;
distributed = true;
break;
case 260:
child = true;
break;
default:
usage(argv[0]);
break;
}
}
if (optind >= argc)
{
usage(argv[0]);
}
srand(time(NULL));
const char * filename = argv[optind];
client_options.push_back(filename);
Scene scene(scene_options, filename);
const int width = scene.getWidth();
const int height = scene.getHeight();
if (server)
{
/* allocate data for the image */
data = new unsigned char[3 * width * height];
cout << " *** Beginning scene render ***" << endl;
cout << "Parameters:" << endl;
cout << "----------------------------------------" << endl;
cout << " Width: " << width << endl;
cout << " Height: " << height << endl;
cout << " Multisample Level: " << scene.getMultisampleLevel() << endl;
cout << " Ambient Occlusion Level: " << scene.getAmbientOcclusionLevel() << endl;
cout << " Vertical Field of View: " << scene.getVFOV() << endl;
cout << " Max Depth: " << scene.getMaxDepth() << endl;
cout << "----------------------------------------" << endl;
}
distrib the_distrib;
struct timeval before, after;
gettimeofday(&before, NULL); /* start timing */
if (distributed)
{
/* start the distribution infrastructure */
if (server)
{
if (strcmp(hosts_file, ""))
the_distrib.readHostFile(hosts_file);
int num_tasks = (width * height + (UNIT_TASK_SIZE - 1))
/ UNIT_TASK_SIZE;
the_distrib.set_num_tasks(num_tasks);
the_distrib.set_data(data, 3 * width * height);
the_distrib.startServer();
the_distrib.startClients(client_options);
/* wait until all tasks are complete */
the_distrib.waitAllTasks();
}
else
{
if (!child)
{
startChildren(server_name, server_port, client_options);
}
the_distrib.startClient(server_name, server_port);
unsigned char data[3 * UNIT_TASK_SIZE];
for (;;)
{
int task_id = the_distrib.getTask();
if (task_id < 0)
break;
int pixel = task_id * UNIT_TASK_SIZE;
int i = pixel / width;
int j = pixel % width;
for (int t = 0; t < UNIT_TASK_SIZE; t++)
{
scene.renderPixel(j, i, &data[3 * t]);
j++;
if (j >= width)
{
j = 0;
i++;
if (i >= height)
break;
}
}
int ret = the_distrib.send_data(task_id,
&data[0],
3 * UNIT_TASK_SIZE);
if (ret != 0)
break;
}
}
}
else
{
/* local multithreaded render; num_threads of 0 auto-detects cores */
renderThreaded(scene, data, width, height, num_threads);
}
gettimeofday(&after, NULL); /* stop timing */
if (server)
{
cout << " *** Ending scene render ***" << endl;
cout << "Writing output file '" << output_file_name << '\'' << endl;
/* write the image */
BMP outputImage(output_file_name, width, height, data);
/* print how much time has elapsed */
double time_before = before.tv_sec + before.tv_usec / 1000000.0;
double time_after = after.tv_sec + after.tv_usec / 1000000.0;
double total_seconds = time_after - time_before;
cout << "Render time: " << total_seconds << " seconds";
double seconds = total_seconds;
int days = (int) (seconds / (60.0 * 60.0 * 24.0));
seconds -= days * 60.0 * 60.0 * 24.0;
int hours = (int) (seconds / (60.0 * 60.0));
seconds -= hours * 60.0 * 60.0;
int minutes = (int) (seconds / 60.0);
seconds -= minutes * 60.0;
if (days || hours || minutes)
{
cout << " (";
if (days)
cout << days << (days == 1 ? " day, " : " days, ");
if (days || hours)
cout << hours << (hours == 1 ? " hour, " : " hours, ");
cout << minutes << (minutes == 1 ? " minute, " : " minutes, ");
cout << seconds << (seconds == 1 ? " second)" : " seconds)");
}
cout << endl;
}
exit(0);
return 0;
}
-283
View File
@@ -1,283 +0,0 @@
#include <stdlib.h>
#include <math.h>
#include <vector>
#include <iostream>
#include <typeinfo>
#include <map>
#include "nodes.h"
using namespace std;
typedef NodeRef (*Function)(NodeRef args);
NodeRef CosFunction(NodeRef args);
NodeRef SinFunction(NodeRef args);
NodeRef SqrtFunction(NodeRef args);
NodeRef AbsFunction(NodeRef args);
static const struct {
const char * fn_name;
Function fn;
} functions[] = {
{"cos", CosFunction},
{"sin", SinFunction},
{"sqrt", SqrtFunction},
{"abs", AbsFunction}
};
class FunctionMap : public map<string, Function>
{
public:
FunctionMap()
{
for (unsigned int i = 0;
i < sizeof(functions)/sizeof(functions[0]);
i++)
{
(*this)[functions[i].fn_name] = functions[i].fn;
}
}
bool contains(const string & key)
{
return find(key) != end();
}
};
static FunctionMap function_map;
void Node::evaluateChildren(refptr<Node> parent)
{
/* recursively evaluate all children nodes */
for (std::vector< refptr<Node> >::iterator it = m_children.begin();
it != m_children.end();
it++)
{
refptr<Node> evaluated = (*it)->evaluate();
if ( ! evaluated.isNull() )
{
if (typeid(*evaluated) == typeid(EvaluatePropagateNode))
{
for (vector<NodeRef>::iterator it2
= evaluated->getChildren().begin();
it2 != evaluated->getChildren().end();
it2++)
{
parent->addChild(*it2);
}
}
else
{
(*it)->evaluateChildren(evaluated);
parent->addChild(evaluated);
}
}
}
}
void Node::addChildren(NodeRef other)
{
if (other.isNull())
return;
for (vector<NodeRef>::const_iterator it = other->m_children.begin();
it != other->m_children.end();
it++)
{
addChild(*it);
}
}
Node::~Node()
{
}
NodeRef BinOpNode::evaluate()
{
double o = one->evaluate()->getNumber();
double t = two->evaluate()->getNumber();
double r = 0.0;
switch (m_op)
{
case '*':
r = o * t;
break;
case '/':
r = o / t;
break;
case '+':
r = o + t;
break;
case '-':
r = o - t;
break;
case '%':
r = (int) o % (int) t;
break;
case '^':
r = pow(o, t);
break;
default:
cerr << "Error: BinOpNode created with op '" << m_op << "'" << endl;
exit(-3);
}
return new NumberNode(r);
}
NodeRef BoolExpressionNode::evaluate()
{
double o = 0, t = 0;
double r = 0;
if (m_op != '!' && m_op != '&' && m_op != '|')
{
o = one->evaluate()->getNumber();
t = two->evaluate()->getNumber();
}
switch (m_op)
{
case '<':
r = o < t ? 1 : 0;
break;
case 'l':
r = o <= t ? 1 : 0;
break;
case '>':
r = o > t ? 1 : 0;
break;
case 'g':
r = o >= t ? 1 : 0;
break;
case '=':
r = o == t ? 1 : 0;
break;
case 'n':
r = o != t ? 1 : 0;
break;
case '&':
r = one->evaluate()->getInteger();
if (r != 0)
{
r = two->evaluate()->getInteger();
}
break;
case '|':
r = one->evaluate()->getInteger();
if (r == 0)
{
r = two->evaluate()->getInteger();
}
break;
case '!':
r = ! one->evaluate()->getInteger();
break;
case 'T':
r = 1;
break;
}
return new NumberNode(r);
}
NodeRef ForNode::evaluate()
{
NodeRef eval = new EvaluatePropagateNode();
if (!m_nodes[0].isNull())
{
m_nodes[0]->evaluate();
}
while (m_nodes[1]->evaluate()->getInteger() != 0)
{
evaluateChildren(eval);
if (!m_nodes[2].isNull())
{
m_nodes[2]->evaluate();
}
}
return eval;
}
NodeRef IfNode::evaluate()
{
NodeRef eval = new EvaluatePropagateNode();
int if_val = m_test_expr->evaluate()->getInteger();
if (if_val != 0)
{
evaluateChildren(eval);
}
else if ( ! m_elses.isNull() )
{
return m_elses->evaluate();
}
return eval;
}
NodeRef ElseNode::evaluate()
{
NodeRef eval = new EvaluatePropagateNode();
evaluateChildren(eval);
return eval;
}
NodeRef FunctionCallNode::evaluate()
{
if (function_map.contains(m_name->getString()))
{
return function_map[m_name->getString()](m_parameters);
}
cerr << "Error: no function \"" << m_name->getString() << "\" defined!"
<< endl;
exit(4);
}
/**************************************************************************
* Scene file functions *
*************************************************************************/
NodeRef CosFunction(NodeRef args)
{
if (args->getChildren().size() != 1)
{
cerr << "Error: cos function requires 1 argument" << endl;
exit(4);
}
return new NumberNode(cos(args->getChildren()[0]->evaluate()->getNumber()));
}
NodeRef SinFunction(NodeRef args)
{
if (args->getChildren().size() != 1)
{
cerr << "Error: sin function requires 1 argument" << endl;
exit(4);
}
return new NumberNode(sin(args->getChildren()[0]->evaluate()->getNumber()));
}
NodeRef SqrtFunction(NodeRef args)
{
if (args->getChildren().size() != 1)
{
cerr << "Error: sqrt function requires 1 argument" << endl;
exit(4);
}
return new NumberNode(sqrt(args->getChildren()[0]->evaluate()->getNumber()));
}
NodeRef AbsFunction(NodeRef args)
{
if (args->getChildren().size() != 1)
{
cerr << "Error: abs function requires 1 argument" << endl;
exit(4);
}
return new NumberNode(fabs(args->getChildren()[0]->evaluate()->getNumber()));
}
-791
View File
@@ -1,791 +0,0 @@
#ifndef NODES_H
#define NODES_H NODES_H
#include <stdlib.h> /* exit() */
#include <vector>
#include <string>
#include <iostream>
#include "util/refptr.h"
#include "util/Vector.h"
#include "util/Scope.h"
extern refptr<Scope> parser_scope;
class Node
{
public:
virtual ~Node();
void addChild(refptr<Node> child) { m_children.push_back(child); }
void addChildren(refptr<Node> other);
std::vector< refptr<Node> > & getChildren() { return m_children; }
virtual int getInteger() { return getNumber(); }
virtual double getNumber()
{
std::cerr << "Warning: Node::getNumber() called!" << std::endl;
return 0.0;
}
virtual refptr<Vector> getVector()
{
std::cerr << "Warning: Node::getVector() called!" << std::endl;
return refptr<Vector>(NULL);
}
virtual std::string getString()
{
std::cerr << "Warning: Node::getString() called!" << std::endl;
return "";
}
virtual refptr<Node> evaluate()
{
std::cerr << "Warning: Node::evaluate() called!" << std::endl;
return NULL;
}
virtual void evaluateChildren(refptr<Node> parent);
virtual bool isShape() { return false; }
virtual bool isMaterial() { return false; }
virtual bool isTransformBlock() { return false; }
virtual bool isExpression() { return false; }
protected:
std::vector< refptr<Node> > m_children;
};
typedef refptr<Node> NodeRef;
class NumberNode : public Node
{
public:
NumberNode(double number)
: m_number(number), m_direct(true)
{
}
NumberNode(NodeRef expr)
: m_direct(false)
{
m_expr = expr;
}
virtual double getNumber()
{
return m_direct ? m_number : m_expr->getNumber();
}
virtual refptr<Node> evaluate()
{
return m_direct
? new NumberNode(m_number)
: new NumberNode(m_expr->evaluate());
}
protected:
double m_number;
bool m_direct;
NodeRef m_expr;
};
class VectorNode : public Node
{
public:
VectorNode(NodeRef a, NodeRef b, NodeRef c)
: m_a(a), m_b(b), m_c(c), m_direct(true)
{
}
VectorNode(NodeRef node)
: m_vector(node), m_direct(false)
{
}
refptr<Vector> getVector()
{
return m_direct
? new Vector(m_a->getNumber(),
m_b->getNumber(),
m_c->getNumber())
: m_vector->getVector();
}
virtual NodeRef evaluate()
{
return m_direct
? new VectorNode(m_a->evaluate(),
m_b->evaluate(),
m_c->evaluate())
: m_vector->evaluate();
}
protected:
NodeRef m_a, m_b, m_c, m_vector;
bool m_direct;
};
class AmbientNode : public VectorNode
{
public:
AmbientNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new AmbientNode(m_vector->evaluate());
}
};
class AmbientOcclusionNode : public NumberNode
{
public:
AmbientOcclusionNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new AmbientOcclusionNode(m_expr->evaluate());
}
};
class BoxNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate()
{
return new BoxNode();
}
};
class CameraNode : public Node
{
public:
virtual NodeRef evaluate()
{
return new CameraNode();
}
};
class ColorNode : public VectorNode
{
public:
ColorNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new ColorNode(m_vector->evaluate());
}
};
class CylNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate()
{
return new CylNode();
}
};
class DiffuseNode : public VectorNode
{
public:
DiffuseNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new DiffuseNode(m_vector->evaluate());
}
};
class ExposureNode : public NumberNode
{
public:
ExposureNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new ExposureNode(m_expr->evaluate());
}
};
class ExtrudeNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate()
{
return new ExtrudeNode();
}
};
class HeightNode : public NumberNode
{
public:
HeightNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new HeightNode(m_expr->evaluate());
}
};
class IdentifierNode : public Node
{
public:
IdentifierNode(const std::string & str) { m_string = str; }
std::string getString() { return m_string; }
virtual NodeRef evaluate()
{
return new IdentifierNode(m_string);
}
protected:
std::string m_string;
};
class IntersectNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate()
{
return new IntersectNode();
}
};
class ItemsNode : public Node
{
public:
virtual NodeRef evaluate()
{
return new ItemsNode();
}
};
class JitterNode : public NumberNode
{
public:
JitterNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new JitterNode(m_expr->evaluate());
}
};
class LightNode : public Node
{
public:
virtual NodeRef evaluate() { return new LightNode(); }
};
class LookAtNode : public VectorNode
{
public:
LookAtNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new LookAtNode(m_vector->evaluate());
}
};
class MaterialNode : public Node
{
public:
bool isMaterial() { return true; }
virtual NodeRef evaluate() { return new MaterialNode(); }
};
class MaterialDefinitionNode : public IdentifierNode
{
public:
MaterialDefinitionNode(const std::string & str) : IdentifierNode(str) {}
virtual NodeRef evaluate()
{
return new MaterialDefinitionNode(m_string);
}
};
class MaterialRefNode : public IdentifierNode
{
public:
MaterialRefNode(const std::string & str) : IdentifierNode(str) {}
bool isMaterial() { return true; }
virtual NodeRef evaluate() { return new MaterialRefNode(m_string); }
};
class MaxDepthNode : public NumberNode
{
public:
MaxDepthNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new MaxDepthNode(m_expr->evaluate());
}
};
class MultisampleNode : public NumberNode
{
public:
MultisampleNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new MultisampleNode(m_expr->evaluate());
}
};
class NGonNode : public NumberNode
{
public:
NGonNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new NGonNode(m_expr->evaluate());
}
};
class OffsetNode : public NumberNode
{
public:
OffsetNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new OffsetNode(m_expr->evaluate());
}
};
class OptionsNode : public Node
{
public:
virtual NodeRef evaluate() { return new OptionsNode(); }
};
class PlaneNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate() { return new PlaneNode(); }
};
class PlanePositionNode : public VectorNode
{
public:
PlanePositionNode(NodeRef vec_node, NodeRef dist)
: VectorNode(vec_node)
{
m_dist = dist;
}
double getNumber() { return m_dist->getNumber(); }
virtual NodeRef evaluate()
{
return new PlanePositionNode(m_vector->evaluate(),
m_dist->evaluate());
}
protected:
NodeRef m_dist;
};
class PolygonNode : public Node
{
public:
virtual NodeRef evaluate() { return new PolygonNode(); }
};
class PositionNode : public VectorNode
{
public:
PositionNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new PositionNode(m_vector->evaluate());
}
};
class RadiusNode : public NumberNode
{
public:
RadiusNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new RadiusNode(m_expr->evaluate());
}
};
class ReflectanceNode : public NumberNode
{
public:
ReflectanceNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new ReflectanceNode(m_expr->evaluate());
}
};
class RefractionNode : public NumberNode
{
public:
RefractionNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new RefractionNode(m_expr->evaluate());
}
};
class RotateNode : public VectorNode
{
public:
RotateNode(NodeRef angle, NodeRef vec_node)
: VectorNode(vec_node)
{
m_angle = angle;
}
double getNumber() { return m_angle->getNumber(); }
virtual NodeRef evaluate()
{
return new RotateNode(m_angle->evaluate(), m_vector->evaluate());
}
protected:
NodeRef m_angle;
};
class RotateBlockNode : public RotateNode
{
public:
RotateBlockNode(NodeRef angle, NodeRef vec_node)
: RotateNode(angle, vec_node) {}
bool isTransformBlock() { return true; }
virtual NodeRef evaluate()
{
return new RotateBlockNode(m_angle->evaluate(), m_vector->evaluate());
}
};
class ScaleNode : public VectorNode
{
public:
ScaleNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new ScaleNode(m_vector->evaluate());
}
};
class ScaleBlockNode : public ScaleNode
{
public:
ScaleBlockNode(NodeRef vector) : ScaleNode(vector) {}
bool isTransformBlock() { return true; }
virtual NodeRef evaluate()
{
return new ScaleBlockNode(m_vector->evaluate());
}
};
class ScaleScalarNode : public Node
{
public:
ScaleScalarNode(NodeRef expr)
: m_expr(expr)
{
}
virtual NodeRef evaluate()
{
NodeRef n = m_expr->evaluate();
return new VectorNode(n, n, n);
}
protected:
NodeRef m_expr;
};
class SceneNode : public Node
{
public:
virtual NodeRef evaluate() { return new SceneNode(); }
};
class ShapeDefinitionNode : public IdentifierNode
{
public:
ShapeDefinitionNode(const std::string & str) : IdentifierNode(str) {}
virtual NodeRef evaluate() { return new ShapeDefinitionNode(m_string); }
};
class ShapeRefNode : public IdentifierNode
{
public:
ShapeRefNode(const std::string & str) : IdentifierNode(str) {}
bool isShape() { return true; }
virtual NodeRef evaluate() { return new ShapeRefNode(m_string); }
};
class ShininessNode : public NumberNode
{
public:
ShininessNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new ShininessNode(m_expr->evaluate());
}
};
class SizeNode : public VectorNode
{
public:
SizeNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new SizeNode(m_vector->evaluate());
}
};
class SpecularNode : public VectorNode
{
public:
SpecularNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new SpecularNode(m_vector->evaluate());
}
};
class SphereNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate() { return new SphereNode(); }
};
class SubtractNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate() { return new SubtractNode(); }
};
class TextureNode : public IdentifierNode
{
public:
TextureNode(const std::string & str) : IdentifierNode(str) {}
virtual NodeRef evaluate() { return new TextureNode(m_string); }
};
class TranslateNode : public VectorNode
{
public:
TranslateNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new TranslateNode(m_vector->evaluate());
}
};
class TranslateBlockNode : public TranslateNode
{
public:
TranslateBlockNode(NodeRef vector) : TranslateNode(vector) {}
bool isTransformBlock() { return true; }
virtual NodeRef evaluate()
{
return new TranslateBlockNode(m_vector->evaluate());
}
};
class TransparencyNode : public NumberNode
{
public:
TransparencyNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new TransparencyNode(m_expr->evaluate());
}
};
class UnionNode : public Node
{
public:
bool isShape() { return true; }
virtual NodeRef evaluate() { return new UnionNode(); }
};
class UpNode : public VectorNode
{
public:
UpNode(NodeRef vector) : VectorNode(vector) {}
virtual NodeRef evaluate()
{
return new UpNode(m_vector->evaluate());
}
};
class VFOVNode : public NumberNode
{
public:
VFOVNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new VFOVNode(m_expr->evaluate());
}
};
class WidthNode : public NumberNode
{
public:
WidthNode(NodeRef e) : NumberNode(e) {}
virtual NodeRef evaluate()
{
return new WidthNode(m_expr->evaluate());
}
};
/******** scripting nodes ********/
class ExpressionNode : public Node
{
public:
bool isExpression() { return true; }
virtual NodeRef evaluate() = 0;
};
class AssignmentNode : public ExpressionNode
{
public:
AssignmentNode(NodeRef varref, NodeRef expr)
: m_varref(varref), m_expr(expr)
{
}
std::string getString() { return m_varref->getString(); }
virtual NodeRef evaluate()
{
double n = m_expr->evaluate()->getNumber();
parser_scope->putGlobal(getString(), n);
return new NumberNode(n);
}
protected:
NodeRef m_varref;
NodeRef m_expr;
};
class LocalAssignmentNode : public ExpressionNode
{
public:
LocalAssignmentNode(NodeRef varref, NodeRef expr)
: m_varref(varref), m_expr(expr)
{
}
std::string getString() { return m_varref->getString(); }
virtual NodeRef evaluate()
{
double n = m_expr->evaluate()->getNumber();
parser_scope->putLocal(getString(), n);
return new NumberNode(n);
}
protected:
NodeRef m_varref;
NodeRef m_expr;
};
class LocalDeclNode : public ExpressionNode
{
public:
LocalDeclNode(NodeRef varref) : m_varref(varref) { }
virtual NodeRef evaluate()
{
parser_scope->putLocal(m_varref->getString(), 0.0);
return NULL;
}
protected:
NodeRef m_varref;
};
class BinOpNode : public ExpressionNode
{
public:
BinOpNode(char op, NodeRef one, NodeRef two)
: m_op(op), one(one), two(two)
{
}
virtual NodeRef evaluate();
protected:
char m_op;
NodeRef one;
NodeRef two;
};
class BoolExpressionNode : public Node
{
public:
BoolExpressionNode(char op, NodeRef one, NodeRef two)
: m_op(op), one(one), two(two)
{
}
virtual NodeRef evaluate();
protected:
char m_op;
NodeRef one;
NodeRef two;
};
class VarRefNode : public Node
{
public:
VarRefNode(const std::string & str) { m_string = str; }
std::string getString() { return m_string; }
virtual NodeRef evaluate()
{
if (parser_scope->contains(m_string))
{
return new NumberNode(parser_scope->get(m_string));
}
std::cerr << "Error: No identifier '" << m_string << "' in scope"
<< std::endl;
exit(4);
}
protected:
std::string m_string;
};
class ForNode : public Node
{
public:
ForNode(NodeRef e1, NodeRef e2, NodeRef e3)
{
m_nodes[0] = e1;
m_nodes[1] = e2;
m_nodes[2] = e3;
}
virtual NodeRef evaluate();
protected:
NodeRef m_nodes[3];
};
class IfNode : public Node
{
public:
IfNode(NodeRef test_expr, NodeRef elses)
{
m_test_expr = test_expr;
m_elses = elses;
}
virtual NodeRef evaluate();
protected:
NodeRef m_test_expr, m_elses;
};
class ElseNode : public Node
{
public:
virtual NodeRef evaluate();
};
class FunctionCallNode : public Node
{
public:
FunctionCallNode(NodeRef name, NodeRef parameters)
{
m_name = name;
m_parameters = parameters;
}
virtual NodeRef evaluate();
protected:
NodeRef m_name, m_parameters;
};
/* this class is only used to hold a set of items coming out of a class's
* evaluate() from above. the evaluateChildren() top-level method will
* propagate children of this class up to the level of their parent */
class EvaluatePropagateNode : public Node
{
};
#endif
-13
View File
@@ -1,13 +0,0 @@
#ifndef PARSER_H
#define PARSER_H PARSER_H
#include "nodes.h"
#include "util/refptr.h"
#include "util/Scope.h"
#define YYSTYPE refptr<Node>
refptr<Node> parse(const char * fileName, refptr<Scope> scope);
#endif
-157
View File
@@ -1,157 +0,0 @@
%option nounput
%option noyywrap
%option bison-locations
%{
#include <string>
#include "nodes.h"
#include "parser.h"
#include "parser.hh"
#define YY_USER_ACTION yylloc->first_column += yyleng;
static std::string build_string;
%}
%x str
%%
\+ return PLUS;
- return MINUS;
\* return TIMES;
\/ return DIVIDE;
% return MOD;
\^ return POW;
= return ASSIGN;
== return EQUALS;
!= return NOTEQUALS;
\< return LESS;
\<= return LESSEQ;
\> return GREATER;
\>= return GREATEREQ;
&& return AND;
\|\| return OR;
! return NOT;
; return SEMICOLON;
: return COLON;
\? return QUESTION;
\$ return DOLLAR;
\. return DOT;
, return COMMA;
\{ return LCURLY;
\} return RCURLY;
\[ return LBRACKET;
\] return RBRACKET;
\( return LPAREN;
\) return RPAREN;
[0-9]+ *yylval = new NumberNode(atof(yytext)); return REAL_NUMBER;
[0-9]*\.[0-9]+ *yylval = new NumberNode(atof(yytext)); return REAL_NUMBER;
ambient return AMBIENT;
ambient_occlusion return AMBIENT_OCCLUSION;
box return BOX;
camera return CAMERA;
color return COLOR;
cyl return CYL;
define return DEFINE;
diffuse return DIFFUSE;
exposure return EXPOSURE;
extrude return EXTRUDE;
height return HEIGHT;
intersect return INTERSECT;
jitter return JITTER;
light return LIGHT;
look_at return LOOKAT;
material return MATERIAL;
max_depth return MAXDEPTH;
multisample return MULTISAMPLE;
ngon return NGON;
offset return OFFSET;
options return OPTIONS;
plane return PLANE;
polygon return POLYGON;
position return POSITION;
radius return RADIUS;
reflectance return REFLECTANCE;
refraction return REFRACTION;
rotate return ROTATE;
scale return SCALE;
scene return SCENE;
shape return SHAPE;
shininess return SHININESS;
size return SIZE;
specular return SPECULAR;
sphere return SPHERE;
subtract return SUBTRACT;
texture return TEXTURE;
translate return TRANSLATE;
transparency return TRANSPARENCY;
union return UNION;
up return UP;
vfov return VFOV;
width return WIDTH;
else return ELSE;
elsif return ELSIF;
for return FOR;
if return IF;
while return WHILE;
local return LOCAL;
[a-zA-Z_][a-zA-Z_0-9]* {
*yylval = new IdentifierNode(yytext);
return IDENTIFIER;
}
\$[a-zA-Z_][a-zA-Z_0-9]* {
*yylval = new VarRefNode(yytext+1);
return VARREF;
}
#.*\n {
yylloc->first_line++; yylloc->last_line++;
yylloc->first_column = yylloc->last_column = 0;
}
\n {
yylloc->first_line++; yylloc->last_line++;
yylloc->first_column = yylloc->last_column = 0;
}
[ \t\v] /* ignore whitespace */
/* strings */
\" build_string = ""; BEGIN(str);
<str>{
\" {
/* end of the string literal */
BEGIN(INITIAL);
*yylval = new IdentifierNode(build_string);
return STRING;
}
\\x[0-9A-Fa-f]{2} {
/* hexadecimal escape code */
unsigned int val;
(void) sscanf(yytext + 2, "%x", &val);
build_string += (char) val;
}
\\n build_string += '\n';
\\t build_string += '\t';
\\r build_string += '\r';
\\b build_string += '\b';
\\f build_string += '\f';
\\(.|\n) build_string += yytext[1];
[^\\\"]+ build_string += yytext;
}
. return yytext[0];
%%
-511
View File
@@ -1,511 +0,0 @@
%{
#include <stdio.h>
#include <iostream>
#include "util/Vector.h"
#include "util/refptr.h"
#include "util/Scope.h"
#include "nodes.h"
#include "parser.h"
#include "parser.hh" /* bison-generated header with YY[SL]TYPE */
using namespace std;
#define yyerror(msg) errFunc(msg, &yylloc)
int yylex(YYSTYPE *, YYLTYPE *);
extern FILE * yyin;
void errFunc(const char * str, YYLTYPE * yyllocp);
static refptr<Node> parsed_scene_node;
refptr<Scope> parser_scope;
%}
%define api.pure
%locations
%define parse.error verbose
%initial-action {
/* Reference yynerrs so the generated parser does not warn that it is
set but never used; this grammar has no error-recovery rules. */
(void) yynerrs;
}
%token PLUS;
%token MINUS;
%token TIMES;
%token DIVIDE;
%token MOD;
%token POW;
%token ASSIGN;
%token EQUALS;
%token NOTEQUALS;
%token LESS;
%token LESSEQ;
%token GREATER;
%token GREATEREQ;
%token AND
%token OR
%token NOT;
%token SEMICOLON;
%token COLON;
%token QUESTION;
%token DOLLAR;
%token DOT;
%token COMMA;
%token LCURLY;
%token RCURLY;
%token LBRACKET;
%token RBRACKET;
%token LPAREN;
%token RPAREN;
%token REAL_NUMBER;
%token STRING;
%token AMBIENT;
%token AMBIENT_OCCLUSION;
%token BOX;
%token CAMERA;
%token COLOR;
%token CYL;
%token DEFINE;
%token DIFFUSE;
%token EXPOSURE;
%token EXTRUDE;
%token HEIGHT;
%token INTERSECT;
%token JITTER;
%token LIGHT;
%token LOOKAT;
%token MATERIAL;
%token MAXDEPTH;
%token MULTISAMPLE;
%token NGON;
%token OFFSET;
%token OPTIONS;
%token PLANE;
%token POLYGON;
%token POSITION;
%token RADIUS;
%token REFLECTANCE;
%token REFRACTION;
%token ROTATE;
%token SCALE;
%token SCENE;
%token SHAPE;
%token SHININESS;
%token SIZE;
%token SPECULAR;
%token SPHERE;
%token SUBTRACT;
%token TEXTURE;
%token TRANSLATE;
%token TRANSPARENCY;
%token UNION;
%token UP;
%token VFOV;
%token WIDTH;
%token IDENTIFIER;
%token VARREF;
%token ELSE;
%token ELSIF;
%token FOR;
%token IF;
%token WHILE;
%token LOCAL;
%right ASSIGN
%left AND OR NOT
%left PLUS MINUS
%left TIMES DIVIDE MOD
%right POW
%left UMINUS
%%
scene: SCENE LCURLY general_items RCURLY {
$$ = new SceneNode();
$$->addChildren($3);
parsed_scene_node = $$;
}
;
box: BOX LCURLY general_items RCURLY {
$$ = new BoxNode();
$$->addChildren($3);
}
;
camera: CAMERA LCURLY general_items RCURLY {
$$ = new CameraNode();
$$->addChildren($3);
}
;
cyl: CYL LCURLY general_items RCURLY {
$$ = new CylNode();
$$->addChildren($3);
}
;
extrude: EXTRUDE LCURLY general_items RCURLY {
$$ = new ExtrudeNode();
$$->addChildren($3);
}
;
general_items: /* empty */ { $$ = NULL; }
| general_item general_items {
$$ = new ItemsNode();
$$->addChild($1);
$$->addChildren($2);
}
;
general_item: AMBIENT vector3 { $$ = new AmbientNode($2); }
| AMBIENT_OCCLUSION expression {
$$ = new AmbientOcclusionNode($2);
}
| camera { $$ = $1; }
| COLOR vector3 { $$ = new ColorNode($2); }
| DIFFUSE vector3 { $$ = new DiffuseNode($2); }
| EXPOSURE expression { $$ = new ExposureNode($2); }
| for { $$ = $1; }
| HEIGHT expression { $$ = new HeightNode($2); }
| if { $$ = $1; }
| JITTER expression { $$ = new JitterNode($2); }
| light { $$ = $1; }
| LOOKAT vector3 { $$ = new LookAtNode($2); }
| material { $$ = $1; }
| material_definition { $$ = $1; }
| material_ref { $$ = $1; }
| MAXDEPTH expression { $$ = new MaxDepthNode($2); }
| MULTISAMPLE expression { $$ = new MultisampleNode($2); }
| ngon { $$ = $1; }
| offset { $$ = $1; }
| options { $$ = $1; }
| polygon { $$ = $1; }
| POSITION vector3 { $$ = new PositionNode($2); }
| POSITION vector3 COMMA expression {
$$ = new PlanePositionNode($2, $4);
}
| RADIUS expression { $$ = new RadiusNode($2); }
| REFLECTANCE expression { $$ = new ReflectanceNode($2); }
| REFRACTION expression { $$ = new RefractionNode($2); }
| ROTATE expression COMMA vector3 { $$ = new RotateNode($2, $4); }
| SCALE expression { $$ = new ScaleNode(new ScaleScalarNode($2)); }
| SCALE vector2 { $$ = new ScaleNode($2); }
| SCALE vector3 { $$ = new ScaleNode($2); }
| shape { $$ = $1; }
| shape_definition { $$ = $1; }
| SHININESS expression { $$ = new ShininessNode($2); }
| SIZE vector3 { $$ = new SizeNode($2); }
| SPECULAR vector3 { $$ = new SpecularNode($2); }
| stmt_expression { $$ = $1; }
| TEXTURE STRING { $$ = new TextureNode($2->getString()); }
| transform_block { $$ = $1; }
| TRANSLATE vector3 { $$ = new TranslateNode($2); }
| TRANSPARENCY expression { $$ = new TransparencyNode($2); }
| UP vector3 { $$ = new UpNode($2); }
| vector2 { $$ = $1; }
| VFOV expression { $$ = new VFOVNode($2); }
| while { $$ = $1; }
| WIDTH expression { $$ = new WidthNode($2); }
;
intersect: INTERSECT LCURLY general_items RCURLY {
$$ = new IntersectNode();
$$->addChildren($3);
}
;
light: LIGHT LCURLY general_items RCURLY {
$$ = new LightNode();
$$->addChildren($3);
}
;
material: MATERIAL LCURLY general_items RCURLY {
$$ = new MaterialNode();
$$->addChildren($3);
}
;
material_definition: DEFINE MATERIAL IDENTIFIER LCURLY general_items RCURLY {
$$ = new MaterialDefinitionNode($3->getString());
$$->addChildren($5);
}
;
material_ref: MATERIAL IDENTIFIER {
$$ = new MaterialRefNode($2->getString());
}
;
number: REAL_NUMBER { $$ = $1; }
;
ngon: NGON expression COMMA expression {
$$ = new NGonNode($2);
$$->addChild(new RadiusNode($4));
}
;
offset: OFFSET expression {
$$ = new OffsetNode($2);
}
| OFFSET expression LCURLY general_items RCURLY {
$$ = new OffsetNode($2);
$$->addChildren($4);
}
;
options: OPTIONS LCURLY general_items RCURLY {
$$ = new OptionsNode();
$$->addChildren($3);
}
;
plane: PLANE LCURLY general_items RCURLY {
$$ = new PlaneNode();
$$->addChildren($3);
}
;
polygon: POLYGON LCURLY general_items RCURLY {
$$ = new PolygonNode();
$$->addChildren($3);
}
;
shape: plane { $$ = $1; }
| sphere { $$ = $1; }
| box { $$ = $1; }
| cyl { $$ = $1; }
| union { $$ = $1; }
| intersect { $$ = $1; }
| subtract { $$ = $1; }
| extrude { $$ = $1; }
| shape_ref { $$ = $1; }
;
shape_definition: DEFINE SHAPE IDENTIFIER shape {
$$ = new ShapeDefinitionNode($3->getString());
$$->addChild($4);
}
;
shape_ref: SHAPE IDENTIFIER shape_ref_more {
$$ = new ShapeRefNode($2->getString());
$$->addChildren($3);
}
;
shape_ref_more: /* empty */ { $$ = NULL; }
| LCURLY general_items RCURLY { $$ = $2; }
;
sphere: SPHERE LCURLY general_items RCURLY {
$$ = new SphereNode();
$$->addChildren($3);
}
;
subtract: SUBTRACT LCURLY general_items RCURLY {
$$ = new SubtractNode();
$$->addChildren($3);
}
;
transform_block: TRANSLATE vector3 LCURLY general_items RCURLY {
$$ = new TranslateBlockNode($2);
$$->addChildren($4);
}
| ROTATE expression COMMA vector3 LCURLY general_items RCURLY {
$$ = new RotateBlockNode($2, $4);
$$->addChildren($6);
}
| SCALE vector3 LCURLY general_items RCURLY {
$$ = new ScaleBlockNode($2);
$$->addChildren($4);
}
| SCALE expression LCURLY general_items RCURLY {
$$ = new ScaleBlockNode(new ScaleScalarNode($2));
$$->addChildren($4);
}
;
union: UNION LCURLY general_items RCURLY {
$$ = new UnionNode();
$$->addChildren($3);
}
;
vector2: LESS expression COMMA expression GREATER {
$$ = new VectorNode($2, $4, new NumberNode(0.0));
}
;
vector3: LESS expression COMMA expression COMMA expression GREATER {
$$ = new VectorNode($2, $4, $6);
}
;
expression: expression TIMES expression { $$ = new BinOpNode('*', $1, $3); }
| expression DIVIDE expression { $$ = new BinOpNode('/', $1, $3); }
| expression PLUS expression { $$ = new BinOpNode('+', $1, $3); }
| expression MINUS expression { $$ = new BinOpNode('-', $1, $3); }
| expression MOD expression { $$ = new BinOpNode('%', $1, $3); }
| expression POW expression { $$ = new BinOpNode('^', $1, $3); }
| MINUS expression %prec UMINUS {
$$ = new BinOpNode('-', new NumberNode(0.0), $2);
}
| stmt_expression { $$ = $1; }
| function_call { $$ = $1; }
| number { $$ = $1; }
| VARREF { $$ = $1; }
| LPAREN expression RPAREN { $$ = $2; }
;
maybe_expression: /* empty */ { $$ = NULL; }
| expression { $$ = $1; }
;
stmt_expression: assignment { $$ = $1; }
| local_assignment { $$ = $1; }
| local_decl { $$ = $1; }
;
bool_expression: expression LESS expression {
$$ = new BoolExpressionNode('<', $1, $3);
}
| expression LESSEQ expression {
$$ = new BoolExpressionNode('l', $1, $3);
}
| expression GREATER expression {
$$ = new BoolExpressionNode('>', $1, $3);
}
| expression GREATEREQ expression {
$$ = new BoolExpressionNode('g', $1, $3);
}
| expression EQUALS expression {
$$ = new BoolExpressionNode('=', $1, $3);
}
| expression NOTEQUALS expression {
$$ = new BoolExpressionNode('n', $1, $3);
}
| bool_expression AND bool_expression {
$$ = new BoolExpressionNode('&', $1, $3);
}
| bool_expression OR bool_expression {
$$ = new BoolExpressionNode('|', $1, $3);
}
| NOT bool_expression {
$$ = new BoolExpressionNode('!', $2, NULL);
}
| LPAREN bool_expression RPAREN {
$$ = $2;
}
;
assignment: VARREF ASSIGN expression {
$$ = new AssignmentNode($1, $3);
}
;
local_assignment: LOCAL VARREF ASSIGN expression {
$$ = new LocalAssignmentNode($2, $4);
}
;
local_decl: LOCAL VARREF {
$$ = new LocalDeclNode($2);
}
;
for: FOR LPAREN maybe_expression SEMICOLON bool_expression SEMICOLON maybe_expression RPAREN LCURLY general_items RCURLY {
$$ = new ForNode($3, $5, $7);
$$->addChildren($10);
}
;
while: WHILE LPAREN bool_expression RPAREN LCURLY general_items RCURLY {
$$ = new ForNode(NULL, $3, NULL);
$$->addChildren($6);
}
;
if: IF LPAREN bool_expression RPAREN LCURLY general_items RCURLY if_more {
$$ = new IfNode($3, $8);
$$->addChildren($6);
}
;
if_more: /* empty */ { $$ = NULL; }
| ELSIF LPAREN bool_expression RPAREN LCURLY general_items RCURLY if_more {
$$ = new IfNode($3, $8);
$$->addChildren($6);
}
| ELSE LCURLY general_items RCURLY {
$$ = new ElseNode();
$$->addChildren($3);
}
;
function_call: IDENTIFIER LPAREN function_call_parameters RPAREN {
$$ = new FunctionCallNode($1, $3);
}
;
function_call_parameters: /* empty */ { $$ = NULL; }
| expression function_call_more_parameters {
$$ = new ItemsNode();
$$->addChild($1);
$$->addChildren($2);
}
;
function_call_more_parameters: /* empty */ { $$ = NULL; }
| COMMA expression function_call_more_parameters {
$$ = new ItemsNode();
$$->addChild($2);
$$->addChildren($3);
}
;
%%
refptr<Node> parse(const char * fileName, refptr<Scope> scope)
{
parser_scope = scope;
yyin = fopen(fileName, "r");
if (yyin == NULL)
{
cerr << "Failed to open file '" << fileName << "'" << endl;
return refptr<Node>(NULL);
}
if (yyparse())
{
cerr << "Aborting." << endl;
exit(1);
}
return parsed_scene_node;
}
void errFunc(const char * str, YYLTYPE * yyllocp)
{
fprintf(stderr, "error: %s: line %d, column %d\n",
str,
yyllocp->first_line,
yyllocp->first_column);
}
-34
View File
@@ -1,34 +0,0 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
using namespace std;
int preprocess(const char * fileName)
{
struct stat st;
if (stat(fileName, &st))
{
cerr << "Error accessing " << fileName << endl;
return -1;
}
ifstream ifs(fileName);
if ( ! ifs.is_open() )
{
cerr << "Error opening " << fileName << endl;
return -2;
}
char * buff = new char[st.st_size];
while ( ! ifs.eof() )
{
ifs.getline(buff, st.st_size);
}
delete[] buff;
return 0;
}
-9
View File
@@ -1,9 +0,0 @@
#include "BoolShape.h"
void BoolShape::setMaterial(refptr<Material> material)
{
m_material = material;
m_shape1->setMaterial(material);
m_shape2->setMaterial(material);
}
-19
View File
@@ -1,19 +0,0 @@
#ifndef BOOLSHAPE_H
#define BOOLSHAPE_H BOOLSHAPE_H
#include "Shape.h"
class BoolShape : public Shape
{
public:
virtual IntersectionList intersect(refptr<Shape> _this, const Ray & ray) = 0;
virtual void setMaterial(refptr<Material> material);
virtual refptr<Shape> clone() = 0;
protected:
refptr<Shape> m_shape1;
refptr<Shape> m_shape2;
};
#endif
-111
View File
@@ -1,111 +0,0 @@
#include "Box.h"
#include "util/Solver.h"
#include <math.h>
#include <iostream>
using namespace std;
#define FP_EQUAL(x,y) (fabs((x)-(y)) < 0.000001)
Box::Box(refptr<Vector> size)
{
m_size = *size;
m_size[0] = fabs(m_size[0]) / 2.0;
m_size[1] = fabs(m_size[1]) / 2.0;
m_size[2] = fabs(m_size[2]) / 2.0;
}
Shape::IntersectionList Box::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res;
/*** optimization ***/
if (ray_inv.getOrigin()[0] > m_size[0])
{
if (ray_inv.getDirection() % Vector::X > 0)
return res;
}
else if (ray_inv.getOrigin()[0] < -m_size[0])
{
if (ray_inv.getDirection() % Vector::X < 0)
return res;
}
if (ray_inv.getOrigin()[1] > m_size[1])
{
if (ray_inv.getDirection() % Vector::Y > 0)
return res;
}
else if (ray_inv.getOrigin()[1] < -m_size[1])
{
if (ray_inv.getDirection() % Vector::Y < 0)
return res;
}
if (ray_inv.getOrigin()[2] > m_size[2])
{
if (ray_inv.getDirection() % Vector::Z > 0)
return res;
}
else if (ray_inv.getOrigin()[2] < -m_size[2])
{
if (ray_inv.getDirection() % Vector::Z < 0)
return res;
}
/*** end optimization ***/
/*
* Ray equation: R = R0 + tRd
* x = R0x + tRdx
* y = R0y + tRdy
* z = R0z + tRdz
* Side equation: x - size_x = 0
* Combined: R0x + (t)Rdx - size_x = 0
*/
for (int dim = 0; dim < 3; dim++)
{
for (int side = -1; side <= 1; side += 2)
{
LinearSolver solver(ray_inv.getDirection()[dim],
ray_inv.getOrigin()[dim]
+ side * m_size[dim]);
Solver::Result solutions = solver.solve();
for (int i = 0; i < solutions.numResults; i++)
{
if (solutions.results[i] >= 0.0)
{
Vector isect_point = ray_inv[solutions.results[i]];
if ( (dim == 0 || fabs(isect_point[0]) <= m_size[0])
&& (dim == 1 || fabs(isect_point[1]) <= m_size[1])
&& (dim == 2 || fabs(isect_point[2]) <= m_size[2]) )
{
Vector normal(0, 0, -1);
if ( FP_EQUAL(isect_point[0], m_size[0]) )
normal = Vector(1, 0, 0);
else if ( FP_EQUAL(isect_point[0], -m_size[0]) )
normal = Vector(-1, 0, 0);
else if ( FP_EQUAL(isect_point[1], m_size[1]) )
normal = Vector(0, 1, 0);
else if ( FP_EQUAL(isect_point[1], -m_size[1]) )
normal = Vector(0, -1, 0);
else if ( FP_EQUAL(isect_point[2], m_size[2]) )
normal = Vector(0, 0, 1);
res.add(Intersection(_this,
m_transform.transform_point(isect_point),
m_transform.transform_normal(normal))
);
}
}
}
}
}
return res;
}
refptr<Shape> Box::clone()
{
return new Box(*this);
}
-19
View File
@@ -1,19 +0,0 @@
#ifndef BOX_H
#define BOX_H BOX_H
#include "Shape.h"
class Box : public Shape
{
public:
Box(refptr<Vector> size);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
virtual refptr<Shape> clone();
protected:
Vector m_size;
};
#endif
-141
View File
@@ -1,141 +0,0 @@
#include "Cyl.h"
#include "util/Solver.h"
#include <math.h>
#include <iostream>
using namespace std;
#define FP_EQUAL(x,y) (fabs((x)-(y)) < 0.000001)
Cyl::Cyl(double bottom_radius, double top_radius, double height)
{
m_bottom_radius = fabs(bottom_radius);
m_bottom_radius_2 = bottom_radius * bottom_radius;
m_top_radius = fabs(top_radius);
m_top_radius_2 = top_radius * top_radius;
m_height = fabs(height);
if (m_height == 0.0)
m_height = 1.0;
m_slope = (m_top_radius - m_bottom_radius) / m_height; /* rise over run */
if ( ! FP_EQUAL(m_slope, 0.0) )
m_inv_slope = -1.0 / m_slope;
}
Shape::IntersectionList Cyl::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res;
/* First intersect with the bottom plane, if it has positive area */
if (m_bottom_radius > 0.0)
{
LinearSolver solver(-ray_inv.getDirection()[2],
-ray_inv.getOrigin()[2]);
Solver::Result solutions = solver.solve();
if (solutions.numResults > 0 && solutions.results[0] > 0.0)
{
Vector isect_point = ray_inv[solutions.results[0]];
if (isect_point[0]*isect_point[0] + isect_point[1]*isect_point[1]
<= m_bottom_radius_2)
{
res.add(Intersection(_this,
m_transform.transform_point(isect_point),
m_transform.transform_normal(Vector(0, 0, -1))));
}
}
}
/* Same for the top plane */
if (m_top_radius > 0.0)
{
LinearSolver solver(ray_inv.getDirection()[2],
ray_inv.getOrigin()[2] - m_height);
Solver::Result solutions = solver.solve();
if (solutions.numResults > 0 && solutions.results[0] > 0.0)
{
Vector isect_point = ray_inv[solutions.results[0]];
if (isect_point[0]*isect_point[0] + isect_point[1]*isect_point[1]
<= m_top_radius_2)
{
res.add(Intersection(_this,
m_transform.transform_point(isect_point),
m_transform.transform_normal(Vector(0, 0, 1))));
}
}
}
/*
* Now see if the ray hit the side of the cylinder/cone thingy
* Ray equation: R = R0 + tRd
* x = R0x + tRdx
* y = R0y + tRdy
* z = R0z + tRdz
* Side equation: x^2 + y^2 = (m*z + b)^2
* Combined: (R0x+t*Rdx)^2 + (R0y+t*Rdy)^2 = (m*(R0z+t*Rdz) + b)^2
* Expanded: (R0x*R0x + (t*t)*Rdx*Rdx + 2*R0x*t*Rdx)
* + (R0y*R0y + (t*t)*Rdy*Rdy + 2*R0y*t*Rdy)
* = ((m*R0z+m*t*Rdz)^2 + b*b + 2*m*(R0z+t*Rdz)*b)
* = ((m*R0z*m*R0z + m*m*t*t*Rdz*Rdz + 2*m*R0z*m*t*Rdz)
* + b*b + 2*m*R0z*b + 2*m*t*Rdz*b)
* Quadratic form: (t*t)(Rdx*Rdx + Rdy*Rdy - m*m*Rdz*Rdz)
* + (t)(2*R0x*Rdx + 2*R0y*Rdy - 2*m*R0z*m*Rdz - 2*m*Rdz*b)
* + (R0x*R0x + R0y*R0y - m*m*R0z*R0z - b*b - 2*m*R0z*b)
* = 0
*/
double Rdx = ray_inv.getDirection()[0];
double Rdy = ray_inv.getDirection()[1];
double Rdz = ray_inv.getDirection()[2];
double R0x = ray_inv.getOrigin()[0];
double R0y = ray_inv.getOrigin()[1];
double R0z = ray_inv.getOrigin()[2];
double m = m_slope;
double m2 = m*m;
double b = m_bottom_radius;
QuadraticSolver solver(Rdx * Rdx + Rdy * Rdy - m2 * Rdz * Rdz,
2.0 * (R0x*Rdx + R0y*Rdy - m2*R0z*Rdz - m*Rdz*b),
R0x*R0x + R0y*R0y - m2*R0z*R0z - b*b - 2*m*R0z*b);
Solver::Result solutions = solver.solve();
for (int i = 0; i < solutions.numResults; i++)
{
if (solutions.results[i] >= 0.0)
{
Vector isect_point = ray_inv[solutions.results[i]];
if (isect_point[2] > 0.0 && isect_point[2] < m_height)
{
Vector normal;
if ( FP_EQUAL(m_slope, 0.0) )
{
normal = Vector(isect_point[0], isect_point[1], 0.0);
}
else
{
double x = isect_point[0];
double y = isect_point[1];
double dist = sqrt( isect_point[0] * isect_point[0]
+ isect_point[1] * isect_point[1] );
if (dist > 0.0)
{
double scale = 1.0 / dist;
x *= scale;
y *= scale;
}
normal = Vector(x, y, m_inv_slope);
}
normal.normalize();
res.add(Intersection(_this,
m_transform.transform_point(isect_point),
m_transform.transform_normal(normal)));
}
}
}
return res;
}
refptr<Shape> Cyl::clone()
{
return new Cyl(*this);
}
-25
View File
@@ -1,25 +0,0 @@
#ifndef CYL_H
#define CYL_H CYL_H
#include "Shape.h"
class Cyl : public Shape
{
public:
Cyl(double bottom_radius, double top_radius, double height);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
virtual refptr<Shape> clone();
protected:
double m_bottom_radius;
double m_bottom_radius_2;
double m_top_radius;
double m_top_radius_2;
double m_height;
double m_slope;
double m_inv_slope;
};
#endif
-267
View File
@@ -1,267 +0,0 @@
#include <math.h>
#include <iostream>
#include <algorithm> /* sort() */
#include "Extrude.h"
#include "util/Polygon.h"
#include "util/Solver.h"
using namespace std;
#define FP_EQUAL(x,y) (fabs((x)-(y)) < 0.000001)
Extrude::Extrude()
{
}
class IntersectListComparator
{
public:
IntersectListComparator(Vector start) : m_start(start) {}
bool operator()(const Shape::Intersection & i1,
const Shape::Intersection & i2) const
{
double d1 = (i1.position - m_start).mag2();
double d2 = (i2.position - m_start).mag2();
return d1 < d2;
}
protected:
Vector m_start;
};
Shape::IntersectionList Extrude::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res;
/*** optimization ***/
if (ray_inv.getOrigin()[0] > m_aabb.max[0])
{
if (ray_inv.getDirection() % Vector::X > 0)
return res;
}
else if (ray_inv.getOrigin()[0] < m_aabb.min[0])
{
if (ray_inv.getDirection() % Vector::X < 0)
return res;
}
if (ray_inv.getOrigin()[1] > m_aabb.max[1])
{
if (ray_inv.getDirection() % Vector::Y > 0)
return res;
}
else if (ray_inv.getOrigin()[1] < m_aabb.min[1])
{
if (ray_inv.getDirection() % Vector::Y < 0)
return res;
}
if (ray_inv.getOrigin()[2] > m_aabb.max[2])
{
if (ray_inv.getDirection() % Vector::Z > 0)
return res;
}
else if (ray_inv.getOrigin()[2] < m_aabb.min[2])
{
if (ray_inv.getDirection() % Vector::Z < 0)
return res;
}
/*** end optimization ***/
int n_polygons = m_polygons.size();
int n_offsets = m_offsets.size();
double distance = 0.0;
Vector scale(1.0, 1.0, 1.0);
Vector shift(0.0, 0.0, 0.0);
for (int p = 0; p < n_polygons; p++)
{
refptr<Polygon> polygon = m_polygons[p];
for (int o = 0; o < n_offsets; o++)
{
Offset & offset = m_offsets[o];
for (int pt = 0, n_pts = polygon->size(); pt < n_pts; pt++)
{
Vector p1 = scale.mult(*(*polygon)[pt]) + shift;
Vector p2 = scale.mult(*(*polygon)[(pt+1) % n_pts]) + shift;
Vector p3 = scale.mult(offset.scale)
.mult(*(*polygon)[(pt+1) % n_pts])
+ shift + offset.shift;
Vector p4 = scale.mult(offset.scale).mult(*(*polygon)[pt])
+ shift + offset.shift;
p1[2] += distance;
p2[2] += distance;
p3[2] += distance + offset.distance;
p4[2] += distance + offset.distance;
Vector e1 = p2 - p1;
Vector e2 = p3 - p2;
Vector normal = (e1 * e2).normalize();
double a = normal[0], b = normal[1], c = normal[2];
double d = -(a * p1[0] + b * p1[1] + c * p1[2]);
LinearSolver solver( a * ray_inv.getDirection()[0]
+ b * ray_inv.getDirection()[1]
+ c * ray_inv.getDirection()[2],
a * ray_inv.getOrigin()[0]
+ b * ray_inv.getOrigin()[1]
+ c * ray_inv.getOrigin()[2]
+ d);
Solver::Result solutions = solver.solve();
if (solutions.numResults > 0 && solutions.results[0] > 0.0)
{
Vector ipoint = ray_inv[solutions.results[0]];
Polygon quad;
quad.add(p1).add(p2).add(p3).add(p4);
if (quad.containsPointConvex(ipoint))
{
res.add(Intersection(_this,
m_transform.transform_point(ipoint),
m_transform.transform_normal(normal)));
}
}
}
distance += offset.distance;
scale = scale.mult(offset.scale);
shift += offset.shift;
}
}
double a = 0, b = 0, c = -1.0, d = 0.0;
LinearSolver solver( a * ray_inv.getDirection()[0]
+ b * ray_inv.getDirection()[1]
+ c * ray_inv.getDirection()[2],
a * ray_inv.getOrigin()[0]
+ b * ray_inv.getOrigin()[1]
+ c * ray_inv.getOrigin()[2]
+ d);
Solver::Result solutions = solver.solve();
if (solutions.numResults > 0 && solutions.results[0] > 0.0)
{
Vector ipoint = ray_inv[solutions.results[0]];
for (int p = 0; p < n_polygons; p++)
{
refptr<Polygon> polygon = m_polygons[p];
if (polygon->containsPoint2D(ipoint))
{
res.add(Intersection(_this,
m_transform.transform_point(ipoint),
m_transform.transform_normal(Vector(a, b, c))));
}
}
}
if (scale[0] > 0.0 && scale[1] > 0.0)
{
a = 0, b = 0, c = 1.0, d = -distance;
LinearSolver solver( a * ray_inv.getDirection()[0]
+ b * ray_inv.getDirection()[1]
+ c * ray_inv.getDirection()[2],
a * ray_inv.getOrigin()[0]
+ b * ray_inv.getOrigin()[1]
+ c * ray_inv.getOrigin()[2]
+ d);
Solver::Result solutions = solver.solve();
if (solutions.numResults > 0 && solutions.results[0] > 0.0)
{
Vector ipoint = ray_inv[solutions.results[0]];
for (int p = 0; p < n_polygons; p++)
{
Polygon tp = *m_polygons[p];
for (int i = 0, sz = tp.size(); i < sz; i++)
{
tp[i] = new Vector(tp[i]->mult(scale) + shift);
(*tp[i])[2] += distance;
}
if (tp.containsPoint2D(ipoint))
{
res.add(Intersection(_this,
m_transform.transform_point(ipoint),
m_transform.transform_normal(Vector(a, b, c))));
break;
}
}
}
}
sort(res.begin(), res.end(), IntersectListComparator(ray.getOrigin()));
return res;
}
void Extrude::addPolygon(refptr<Polygon> polygon)
{
m_polygons.push_back(polygon);
updateAABB();
}
void Extrude::addOffset(double distance,
const Vector & scale, const Vector & shift)
{
m_offsets.push_back(Offset(distance, scale, shift));
updateAABB();
}
void Extrude::updateAABB()
{
int n_polygons = m_polygons.size();
int n_offsets = m_offsets.size();
bool first = true;
double distance = 0.0;
Vector scale(1.0, 1.0, 1.0);
Vector shift(0.0, 0.0, 0.0);
for (int p = 0; p < n_polygons; p++)
{
refptr<Polygon> polygon = m_polygons[p];
for (int o = 0; o < n_offsets; o++)
{
Offset & offset = m_offsets[o];
for (int pt = 0, n_pts = polygon->size(); pt < n_pts; pt++)
{
Vector p1 = scale.mult(*(*polygon)[pt]) + shift;
Vector p2 = scale.mult(*(*polygon)[(pt+1) % n_pts]) + shift;
Vector p3 = scale.mult(offset.scale)
.mult(*(*polygon)[(pt+1) % n_pts])
+ shift + offset.shift;
Vector p4 = scale.mult(offset.scale).mult(*(*polygon)[pt])
+ shift + offset.shift;
p1[2] += distance;
p2[2] += distance;
p3[2] += distance + offset.distance;
p4[2] += distance + offset.distance;
if (first)
{
m_aabb.min = p1;
m_aabb.max = p1;
first = false;
}
expandAABB(p1);
expandAABB(p2);
expandAABB(p3);
expandAABB(p4);
}
distance += offset.distance;
scale = scale.mult(offset.scale);
shift += offset.shift;
}
}
}
void Extrude::expandAABB(const Vector & v)
{
if (v[0] < m_aabb.min[0])
m_aabb.min[0] = v[0];
if (v[0] > m_aabb.max[0])
m_aabb.max[0] = v[0];
if (v[1] < m_aabb.min[1])
m_aabb.min[1] = v[1];
if (v[1] > m_aabb.max[1])
m_aabb.max[1] = v[1];
if (v[2] < m_aabb.min[2])
m_aabb.min[2] = v[2];
if (v[2] > m_aabb.max[2])
m_aabb.max[2] = v[2];
}
refptr<Shape> Extrude::clone()
{
return new Extrude(*this);
}
-43
View File
@@ -1,43 +0,0 @@
#ifndef EXTRUDE_H
#define EXTRUDE_H EXTRUDE_H
#include <vector>
#include "util/Polygon.h"
#include "util/AABB.h"
#include "Shape.h"
class Extrude : public Shape
{
public:
Extrude();
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
void addPolygon(refptr<Polygon> polygon);
void addOffset(double distance,
const Vector & scale, const Vector & shift);
class Offset
{
public:
Offset(double d, const Vector & s, const Vector & p)
: distance(d), scale(s), shift(p)
{
}
double distance;
Vector scale;
Vector shift;
};
virtual refptr<Shape> clone();
protected:
std::vector< refptr<Polygon> > m_polygons;
std::vector<Offset> m_offsets;
AABB m_aabb;
void updateAABB();
void expandAABB(const Vector & v);
};
#endif
-115
View File
@@ -1,115 +0,0 @@
#include "Intersect.h"
#include <iostream>
using namespace std;
Intersect::Intersect(const vector< refptr<Shape> > & shapes)
{
int num_shapes = shapes.size();
if (num_shapes > 2)
{
m_shape2 = shapes[num_shapes - 1];
vector< refptr<Shape> > rest = shapes;
rest.pop_back();
m_shape1 = new Intersect(rest);
}
else if (num_shapes == 2)
{
m_shape1 = shapes[0];
m_shape2 = shapes[1];
}
else
{
cerr << __FILE__ << ": " << __LINE__
<< ": error: Intersect::Intersect() called with only "
<< num_shapes
<< " sub-shapes!"
<< endl;
exit(4);
}
}
Shape::IntersectionList Intersect::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res1 = m_shape1->intersect(m_shape1, ray_inv);
/*** optimization ***/
if (res1.size() == 0)
return res1;
/*** end optimization ***/
IntersectionList res2 = m_shape2->intersect(m_shape2, ray_inv);
BoolIntersectionList merged(res1, res2, ray_inv.getOrigin());
IntersectionList res;
bool in1 = false, in2 = false;
/* initially go through the merged intersections to see whether
* the ray started inside one of the sub-objects */
for (int i = 0, sz = merged.size(), saw1 = 0, saw2 = 0;
i < sz && (!saw1 || !saw2);
i++)
{
Vector normal = merged[i].intersection.normal;
double dot = - (ray_inv.getDirection() % normal);
bool back = dot < 0.0;
bool left = merged[i].left;
if (back)
{
if (left && !saw1)
in1 = true;
else if (!left && !saw2)
in2 = true;
}
if (left)
saw1 = 1;
else
saw2 = 1;
}
bool in_bool = in1 && in2;
for (int i = 0, sz = merged.size(); i < sz; i++)
{
Vector normal = merged[i].intersection.normal;
double dot = - (ray_inv.getDirection() % normal);
bool front = dot > 0.0;
bool left = merged[i].left;
if (front)
{
if (left)
in1 = true;
else
in2 = true;
if (!in_bool && in1 && in2)
{
/* we found an intersection point with the boolean object */
in_bool = true;
res.add( merged[i].intersection.transform(m_transform) );
}
}
else
{
if (in_bool && in1 && in2)
{
/* we found an intersection point with the boolean object */
res.add( merged[i].intersection.transform(m_transform) );
}
if (left)
in1 = false;
else
in2 = false;
in_bool = false;
}
}
return res;
}
refptr<Shape> Intersect::clone()
{
Intersect * s = new Intersect(*this);
s->m_shape1 = m_shape1->clone();
s->m_shape2 = m_shape2->clone();
return refptr<Shape>(s);
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef INTERSECT_H
#define INTERSECT_H INTERSECT_H
#include "BoolShape.h"
#include <vector>
class Intersect : public BoolShape
{
public:
Intersect(const std::vector< refptr<Shape> > & shapes);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
refptr<Shape> clone();
};
#endif
-57
View File
@@ -1,57 +0,0 @@
#include "Plane.h"
#include "util/Solver.h"
#include <math.h>
#include <iostream>
using namespace std;
Plane::Plane(double a, double b, double c, double d)
{
m_a = a;
m_b = b;
m_c = c;
m_d = d;
m_normal = Vector(m_a, m_b, m_c);
m_normal.normalize();
}
Shape::IntersectionList Plane::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res;
/*
* Plane equation: ax + by + cz + d = 0
* Ray equation: R = R0 + tRd
* x = R0x + tRdx
* y = R0y + tRdy
* z = R0z + tRdz
* Combined: a(R0x + tRdx) + b(R0y + tRdy) + c(R0z + tRdz) + d = 0
* aR0x + (t)aRdx + bR0y + (t)bRdy + cR0z + (t)cRdz + d = 0
* (t)(aRdx + bRdy + cRdz) + aR0x + bR0y + cR0z + d = 0
*/
LinearSolver solver( m_a * ray_inv.getDirection()[0]
+ m_b * ray_inv.getDirection()[1]
+ m_c * ray_inv.getDirection()[2],
m_a * ray_inv.getOrigin()[0]
+ m_b * ray_inv.getOrigin()[1]
+ m_c * ray_inv.getOrigin()[2]
+ m_d);
Solver::Result solutions = solver.solve();
if (solutions.numResults > 0)
{
if (solutions.results[0] > 0.0)
{
Vector isect_point = ray_inv[solutions.results[0]];
res.add(Intersection(_this,
m_transform.transform_point(isect_point),
m_transform.transform_normal(m_normal)));
}
}
return res;
}
refptr<Shape> Plane::clone()
{
return new Plane(*this);
}
-20
View File
@@ -1,20 +0,0 @@
#ifndef PLANE_H
#define PLANE_H PLANE_H
#include "Shape.h"
class Plane : public Shape
{
public:
Plane(double a, double b, double c, double d);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
virtual refptr<Shape> clone();
protected:
double m_a, m_b, m_c, m_d;
Vector m_normal;
};
#endif
-66
View File
@@ -1,66 +0,0 @@
#include "Shape.h"
#include <algorithm> /* sort() */
#include <utility>
using namespace std;
static refptr<Material> default_material;
static bool default_material_initialized = false;
Shape::Shape()
{
if (default_material_initialized == false)
{
default_material = new Material();
default_material_initialized = true;
}
m_material = default_material;
}
Shape::~Shape()
{
}
void Shape::setMaterial(refptr<Material> material)
{
m_material = material;
}
class BoolIntersectionComparator
{
public:
BoolIntersectionComparator(const Vector & refPoint)
{
m_refPoint = refPoint;
}
bool operator()(const Shape::BoolIntersection & i1,
const Shape::BoolIntersection & i2) const
{
return ( m_refPoint.dist_to(i1.intersection.position)
< m_refPoint.dist_to(i2.intersection.position) );
}
protected:
Vector m_refPoint;
};
Shape::BoolIntersectionList::BoolIntersectionList(const IntersectionList & l1,
const IntersectionList & l2,
const Vector & startPoint)
{
for (size_t i = 0, sz = l1.size();
i < sz;
i++)
{
m_intersections.push_back( BoolIntersection(l1[i], true) );
}
for (size_t i = 0, sz = l2.size();
i < sz;
i++)
{
m_intersections.push_back( BoolIntersection(l2[i], false) );
}
sort(m_intersections.begin(),
m_intersections.end(),
BoolIntersectionComparator(startPoint));
}
-131
View File
@@ -1,131 +0,0 @@
#ifndef SHAPE_H
#define SHAPE_H SHAPE_H
#include "util/Solver.h"
#include "util/Ray.h"
#include "util/Vector.h"
#include "util/Transform.h"
#include "util/Material.h"
#include "util/refptr.h"
#include <vector>
#include <utility>
class Shape
{
public:
class Intersection
{
public:
Intersection() {};
Intersection(refptr<Shape> shape,
const Vector & position,
const Vector & normal)
{
this->shape = shape;
this->position = position;
this->normal = normal;
}
refptr<Shape> shape;
Vector position;
Vector normal;
Intersection transform(Transform & t)
{
return Intersection(shape,
t.transform_point(position),
t.transform_normal(normal));
}
};
class BoolIntersection
{
public:
BoolIntersection() {};
BoolIntersection(const Intersection & intersection, bool left)
{
this->intersection = intersection;
this->left = left;
}
Intersection intersection;
bool left;
};
class IntersectionList
{
public:
void add(const Intersection & i)
{
m_intersections.push_back(i);
}
Intersection & operator[](int i)
{
return m_intersections[i];
}
const Intersection & operator[](int i) const
{
return m_intersections[i];
}
size_t size() const { return m_intersections.size(); }
std::vector<Intersection>::iterator begin()
{
return m_intersections.begin();
}
std::vector<Intersection>::iterator end()
{
return m_intersections.end();
}
protected:
std::vector< Intersection > m_intersections;
};
class BoolIntersectionList
{
public:
BoolIntersectionList(const IntersectionList & l1,
const IntersectionList & l2,
const Vector & startPoint);
BoolIntersection & operator[](int i)
{
return m_intersections[i];
}
const BoolIntersection & operator[](int i) const
{
return m_intersections[i];
}
size_t size() const { return m_intersections.size(); }
std::vector<BoolIntersection>::iterator begin()
{
return m_intersections.begin();
}
std::vector<BoolIntersection>::iterator end()
{
return m_intersections.end();
}
protected:
std::vector< BoolIntersection > m_intersections;
};
Shape();
virtual ~Shape();
virtual IntersectionList intersect(refptr<Shape> _this,
const Ray & ray) = 0;
virtual refptr<Shape> clone() = 0;
void setTransform(const Transform & t)
{
m_transform = t;
m_inverse = m_transform.getInverse();
}
Transform & getTransform() { return m_transform; }
virtual void setMaterial(refptr<Material> material);
refptr<Material> getMaterial() const { return m_material; }
protected:
Transform m_transform;
Transform m_inverse;
refptr<Material> m_material;
};
typedef refptr<Shape> ShapeRef;
#endif
-49
View File
@@ -1,49 +0,0 @@
#include "Sphere.h"
#include "util/Solver.h"
#include <math.h>
#include <iostream>
using namespace std;
Sphere::Sphere(double radius)
{
m_radius = radius;
m_radius2 = radius * radius;
}
Shape::IntersectionList Sphere::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res;
QuadraticSolver solver(1.0,
2 * ( ray_inv.getOrigin()[0] * ray_inv.getDirection()[0]
+ ray_inv.getOrigin()[1] * ray_inv.getDirection()[1]
+ ray_inv.getOrigin()[2] * ray_inv.getDirection()[2] ),
ray_inv.getOrigin()[0] * ray_inv.getOrigin()[0]
+ ray_inv.getOrigin()[1] * ray_inv.getOrigin()[1]
+ ray_inv.getOrigin()[2] * ray_inv.getOrigin()[2]
- m_radius2);
Solver::Result quadSolutions = solver.solve();
for (int i = 0; i < quadSolutions.numResults; i++)
{
if (quadSolutions.results[i] >= 0.0)
{
Vector isect_point = ray_inv[quadSolutions.results[i]];
Vector normal = isect_point;
normal.normalize();
res.add(
Intersection(_this,
m_transform.transform_point(isect_point),
m_transform.transform_normal(normal))
);
}
}
return res;
}
refptr<Shape> Sphere::clone()
{
return new Sphere(*this);
}
-20
View File
@@ -1,20 +0,0 @@
#ifndef SPHERE_H
#define SPHERE_H SPHERE_H
#include "Shape.h"
class Sphere : public Shape
{
public:
Sphere(double radius);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
virtual refptr<Shape> clone();
protected:
double m_radius;
double m_radius2;
};
#endif
-115
View File
@@ -1,115 +0,0 @@
#include "Subtract.h"
#include <iostream>
using namespace std;
Subtract::Subtract(const vector< refptr<Shape> > & shapes)
{
int num_shapes = shapes.size();
if (num_shapes > 2)
{
m_shape2 = shapes[num_shapes - 1];
vector< refptr<Shape> > rest = shapes;
rest.pop_back();
m_shape1 = new Subtract(rest);
}
else if (num_shapes == 2)
{
m_shape1 = shapes[0];
m_shape2 = shapes[1];
}
else
{
cerr << __FILE__ << ": " << __LINE__
<< ": error: Subtract::Subtract() called with only "
<< num_shapes
<< " sub-shapes!"
<< endl;
exit(4);
}
}
Shape::IntersectionList Subtract::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res1 = m_shape1->intersect(m_shape1, ray_inv);
/*** optimization ***/
if (res1.size() == 0)
return res1;
/*** end optimization ***/
IntersectionList res2 = m_shape2->intersect(m_shape2, ray_inv);
BoolIntersectionList merged(res1, res2, ray_inv.getOrigin());
IntersectionList res;
bool in1 = false, in2 = false;
/* initially go through the merged intersections to see whether
* the ray started inside one of the sub-objects */
for (int i = 0, sz = merged.size(), saw1 = 0, saw2 = 0;
i < sz && (!saw1 || !saw2);
i++)
{
Vector normal = merged[i].intersection.normal;
double dot = - (ray_inv.getDirection() % normal);
bool back = dot < 0.0;
bool left = merged[i].left;
if (back)
{
if (left && !saw1)
in1 = true;
else if (!left && !saw2)
in2 = true;
}
if (left)
saw1 = 1;
else
saw2 = 1;
}
bool in_bool = in1 && !in2;
for (int i = 0, sz = merged.size(); i < sz; i++)
{
Vector normal = merged[i].intersection.normal;
double dot = - (ray_inv.getDirection() % normal);
bool front = dot > 0.0;
bool left = merged[i].left;
if (left)
in1 = front;
else
in2 = front;
if (!in_bool && in1 && !in2)
{
/* we found an intersection point
* to get into the boolean object */
in_bool = true;
BoolIntersection bi = merged[i];
Intersection i = bi.intersection;
if ( ! left ) /* if this point came from object B (A - B) */
i.normal = - i.normal;
res.add(i.transform(m_transform));
}
else if (in_bool && !(in1 && !in2))
{
/* we found an intersection point
* to get out of the boolean object */
BoolIntersection bi = merged[i];
Intersection i = bi.intersection;
if ( ! left ) /* if this point came from object B (A - B) */
i.normal = - i.normal;
res.add(i.transform(m_transform));
in_bool = false;
}
}
return res;
}
refptr<Shape> Subtract::clone()
{
Subtract * s = new Subtract(*this);
s->m_shape1 = m_shape1->clone();
s->m_shape2 = m_shape2->clone();
return refptr<Shape>(s);
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef SUBTRACT_H
#define SUBTRACT_H SUBTRACT_H
#include "BoolShape.h"
#include <vector>
class Subtract : public BoolShape
{
public:
Subtract(const std::vector< refptr<Shape> > & shapes);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
refptr<Shape> clone();
};
#endif
-110
View File
@@ -1,110 +0,0 @@
#include "Union.h"
#include <iostream>
#include <vector>
using namespace std;
Union::Union(const vector< refptr<Shape> > & shapes)
{
int num_shapes = shapes.size();
if (num_shapes > 2)
{
m_shape2 = shapes[num_shapes - 1];
vector< refptr<Shape> > rest = shapes;
rest.pop_back();
m_shape1 = new Union(rest);
}
else if (num_shapes == 2)
{
m_shape1 = shapes[0];
m_shape2 = shapes[1];
}
else
{
cerr << __FILE__ << ": " << __LINE__
<< ": error: Union::Union() called with only "
<< num_shapes
<< " sub-shapes!"
<< endl;
exit(4);
}
}
Shape::IntersectionList Union::intersect(refptr<Shape> _this, const Ray & ray)
{
Ray ray_inv = m_inverse.transform_ray(ray);
IntersectionList res1 = m_shape1->intersect(m_shape1, ray_inv);
IntersectionList res2 = m_shape2->intersect(m_shape2, ray_inv);
BoolIntersectionList merged(res1, res2, ray_inv.getOrigin());
IntersectionList res;
bool in1 = false, in2 = false;
/* initially go through the merged intersections to see whether
* the ray started inside one of the sub-objects */
for (int i = 0, sz = merged.size(), saw1 = 0, saw2 = 0;
i < sz && (!saw1 || !saw2);
i++)
{
Vector normal = merged[i].intersection.normal;
double dot = - (ray_inv.getDirection() % normal);
bool back = dot < 0.0;
bool left = merged[i].left;
if (back)
{
if (left && !saw1)
in1 = true;
else if (!left && !saw2)
in2 = true;
}
if (left)
saw1 = 1;
else
saw2 = 1;
}
bool in_bool = in1 || in2;
for (int i = 0, sz = merged.size(); i < sz; i++)
{
Vector normal = merged[i].intersection.normal;
double dot = - (ray_inv.getDirection() % normal);
bool front = dot > 0.0;
bool left = merged[i].left;
if (front)
{
if (left)
in1 = true;
else
in2 = true;
if (!in_bool && (in1 || in2))
{
/* we found an intersection point with the boolean object */
in_bool = true;
res.add( merged[i].intersection.transform(m_transform) );
}
}
else
{
if (left)
in1 = false;
else
in2 = false;
if (in_bool && !(in1 || in2))
{
/* we found an intersection point with the boolean object */
res.add( merged[i].intersection.transform(m_transform) );
in_bool = false;
}
}
}
return res;
}
refptr<Shape> Union::clone()
{
Union * s = new Union(*this);
s->m_shape1 = m_shape1->clone();
s->m_shape2 = m_shape2->clone();
return refptr<Shape>(s);
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef UNION_H
#define UNION_H UNION_H
#include "BoolShape.h"
#include <vector>
class Union : public BoolShape
{
public:
Union(const std::vector< refptr<Shape> > & shapes);
IntersectionList intersect(refptr<Shape> _this, const Ray & ray);
refptr<Shape> clone();
};
#endif
-10
View File
@@ -1,10 +0,0 @@
#include "BoolShape.h"
#include "Box.h"
#include "Cyl.h"
#include "Extrude.h"
#include "Intersect.h"
#include "Plane.h"
#include "Sphere.h"
#include "Subtract.h"
#include "Union.h"
-21
View File
@@ -1,21 +0,0 @@
#ifndef AABB_H
#define AABB_H
class AABB
{
public:
Vector min;
Vector max;
AABB()
{
}
AABB(const Vector & min, const Vector & max)
: min(min), max(max)
{
}
};
#endif /* AABB_H */
-17
View File
@@ -1,17 +0,0 @@
#include "Color.h"
const Color Color::black = Color(0, 0, 0);
const Color Color::white = Color(1, 1, 1);
const Color Color::red = Color(1, 0, 0);
const Color Color::green = Color(0, 1, 0);
const Color Color::blue = Color(0, 0, 1);
const Color Color::yellow = Color(1, 1, 0);
const Color Color::cyan = Color(0, 1, 1);
const Color Color::magenta = Color(1, 0, 1);
std::ostream & operator<<(std::ostream & out, const Color & color)
{
out << "[" << color.r << ", " << color.g << ", " << color.b << "]";
return out;
}
-132
View File
@@ -1,132 +0,0 @@
#ifndef COLOR_H
#define COLOR_H COLOR_H
#include "refptr.h"
#include <iostream>
#include "util/Vector.h"
class Color
{
public:
double r, g, b;
Color()
{
r = g = b = 0.0;
}
Color(double r, double g, double b)
{
this->r = r;
this->g = g;
this->b = b;
}
Color(const Vector & v)
{
r = v[0];
g = v[1];
b = v[2];
}
Color(refptr<Vector> v)
{
r = (*v)[0];
g = (*v)[1];
b = (*v)[2];
}
Color operator*(const Color & other) const
{
Color result;
result.r = r * other.r;
result.g = g * other.g;
result.b = b * other.b;
return result;
}
Color operator*(double scale) const
{
return Color(r * scale, g * scale, b * scale);
}
Color operator/(double scale) const
{
return Color(r / scale, g / scale, b / scale);
}
Color & operator+=(const Color & other)
{
r += other.r;
g += other.g;
b += other.b;
return *this;
}
Color & operator-=(const Color & other)
{
r += other.r;
g += other.g;
b += other.b;
return *this;
}
Color & operator*=(double scale)
{
r *= scale;
g *= scale;
b *= scale;
return *this;
}
Color & operator*=(const Color & other)
{
r *= other.r;
g *= other.g;
b *= other.b;
return *this;
}
Color & operator/=(double scale)
{
r /= scale;
g /= scale;
b /= scale;
return *this;
}
Color & operator/=(const Color & other)
{
r /= other.r;
g /= other.g;
b /= other.b;
return *this;
}
Color operator+(const Color & c2)
{
return Color(r + c2.r, g + c2.g, b + c2.b);
}
Color operator-(const Color & c2)
{
return Color(r - c2.r, g - c2.g, b - c2.b);
}
static const Color black;
static const Color white;
static const Color red;
static const Color green;
static const Color blue;
static const Color yellow;
static const Color cyan;
static const Color magenta;
};
static inline Color operator*(double d, const Color & c) { return c * d; }
static inline Color operator/(double d, const Color & c) { return c / d; }
std::ostream & operator<<(std::ostream & out, const Color & color);
#endif
-72
View File
@@ -1,72 +0,0 @@
#ifndef MATERIAL_H
#define MATERIAL_H MATERIAL_H
#include "Texture.h"
#include "Color.h"
class Material
{
public:
Material()
{
m_ambient_color = Color::white;
m_diffuse_color = Color::white;
m_specular_color = Color::white;
m_shininess = 50.0;
m_reflectance = 0.0;
m_transparency = 0.0;
m_refraction = 1.0;
m_texture = NULL;
}
void setAmbientColor(const Color & ambient)
{
m_ambient_color = ambient;
}
const Color & getAmbientColor() const { return m_ambient_color; }
void setDiffuseColor(const Color & diffuse)
{
m_diffuse_color = diffuse;
}
const Color & getDiffuseColor() const { return m_diffuse_color; }
void setSpecularColor(const Color & specular)
{
m_specular_color = specular;
}
const Color & getSpecularColor() const { return m_specular_color; }
void setShininess(double shininess) { m_shininess = shininess; }
double getShininess() const { return m_shininess; }
void setReflectance(double reflectance) { m_reflectance = reflectance; }
double getReflectance() const { return m_reflectance; }
void setRefraction(double refraction)
{
if (refraction > 0.0)
m_refraction = refraction;
}
double getRefraction() const { return m_refraction; }
void setTransparency(double t) { m_transparency = t; }
double getTransparency() const { return m_transparency; }
void setTexture(Texture * texture) { m_texture = texture; }
Texture * getTexture() const { return m_texture; }
protected:
Color m_ambient_color;
Color m_diffuse_color;
Color m_specular_color;
double m_shininess;
double m_reflectance;
double m_refraction;
double m_transparency;
Texture * m_texture;
};
#endif
-241
View File
@@ -1,241 +0,0 @@
#include "Matrix.h"
#include <math.h> /* fabs() */
#include <iostream>
#include <iomanip> /* setprecision() */
#define FP_EQ(x,y) (fabs((x)-(y)) < 0.00001)
Matrix::Matrix()
{
/* set matrix to the identity matrix */
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
m_matrix[i][j] = i == j ? 1.0 : 0.0;
}
}
m_inverse_calculated = false;
}
Matrix Matrix::identity()
{
Matrix res;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
res[i][j] = i == j ? 1.0 : 0.0;
}
}
return res;
}
/* Formulas from http://www.cvl.iis.u-tokyo.ac.jp/~miyazaki/tech/teche23.html */
double Matrix::determinant()
{
return m_matrix[0][0] * m_matrix[1][1] * m_matrix[2][2] * m_matrix[3][3]
+ m_matrix[0][0] * m_matrix[1][2] * m_matrix[2][3] * m_matrix[3][1]
+ m_matrix[0][0] * m_matrix[1][3] * m_matrix[2][1] * m_matrix[3][2]
+ m_matrix[0][1] * m_matrix[1][0] * m_matrix[2][3] * m_matrix[3][2]
+ m_matrix[0][1] * m_matrix[1][2] * m_matrix[2][0] * m_matrix[3][3]
+ m_matrix[0][1] * m_matrix[1][3] * m_matrix[2][2] * m_matrix[3][0]
+ m_matrix[0][2] * m_matrix[1][0] * m_matrix[2][1] * m_matrix[3][3]
+ m_matrix[0][2] * m_matrix[1][1] * m_matrix[2][3] * m_matrix[3][0]
+ m_matrix[0][2] * m_matrix[1][3] * m_matrix[2][0] * m_matrix[3][1]
+ m_matrix[0][3] * m_matrix[1][0] * m_matrix[2][2] * m_matrix[3][1]
+ m_matrix[0][3] * m_matrix[1][1] * m_matrix[2][0] * m_matrix[3][2]
+ m_matrix[0][3] * m_matrix[1][2] * m_matrix[2][1] * m_matrix[3][0]
- m_matrix[0][0] * m_matrix[1][1] * m_matrix[2][3] * m_matrix[3][2]
- m_matrix[0][0] * m_matrix[1][2] * m_matrix[2][1] * m_matrix[3][3]
- m_matrix[0][0] * m_matrix[1][3] * m_matrix[2][2] * m_matrix[3][1]
- m_matrix[0][1] * m_matrix[1][0] * m_matrix[2][2] * m_matrix[3][3]
- m_matrix[0][1] * m_matrix[1][2] * m_matrix[2][3] * m_matrix[3][0]
- m_matrix[0][1] * m_matrix[1][3] * m_matrix[2][0] * m_matrix[3][2]
- m_matrix[0][2] * m_matrix[1][0] * m_matrix[2][3] * m_matrix[3][1]
- m_matrix[0][2] * m_matrix[1][1] * m_matrix[2][0] * m_matrix[3][3]
- m_matrix[0][2] * m_matrix[1][3] * m_matrix[2][1] * m_matrix[3][0]
- m_matrix[0][3] * m_matrix[1][0] * m_matrix[2][1] * m_matrix[3][2]
- m_matrix[0][3] * m_matrix[1][1] * m_matrix[2][2] * m_matrix[3][0]
- m_matrix[0][3] * m_matrix[1][2] * m_matrix[2][0] * m_matrix[3][1];
}
/* Formulas from http://www.cvl.iis.u-tokyo.ac.jp/~miyazaki/tech/teche23.html */
void Matrix::calculateInverse()
{
if (m_inverse_calculated)
return;
m_inverse_calculated = true;
m_inverse_valid = false;
double det = determinant();
if (det == 0.0)
return;
m_inverse[0][0] = m_matrix[1][1] * m_matrix[2][2] * m_matrix[3][3]
+ m_matrix[1][2] * m_matrix[2][3] * m_matrix[3][1]
+ m_matrix[1][3] * m_matrix[2][1] * m_matrix[3][2]
- m_matrix[1][1] * m_matrix[2][3] * m_matrix[3][2]
- m_matrix[1][2] * m_matrix[2][1] * m_matrix[3][3]
- m_matrix[1][3] * m_matrix[2][2] * m_matrix[3][1];
m_inverse[0][1] = m_matrix[0][1] * m_matrix[2][3] * m_matrix[3][2]
+ m_matrix[0][2] * m_matrix[2][1] * m_matrix[3][3]
+ m_matrix[0][3] * m_matrix[2][2] * m_matrix[3][1]
- m_matrix[0][1] * m_matrix[2][2] * m_matrix[3][3]
- m_matrix[0][2] * m_matrix[2][3] * m_matrix[3][1]
- m_matrix[0][3] * m_matrix[2][1] * m_matrix[3][2];
m_inverse[0][2] = m_matrix[0][1] * m_matrix[1][2] * m_matrix[3][3]
+ m_matrix[0][2] * m_matrix[1][3] * m_matrix[3][1]
+ m_matrix[0][3] * m_matrix[1][1] * m_matrix[3][2]
- m_matrix[0][1] * m_matrix[1][3] * m_matrix[3][2]
- m_matrix[0][2] * m_matrix[1][1] * m_matrix[3][3]
- m_matrix[0][3] * m_matrix[1][2] * m_matrix[3][1];
m_inverse[0][3] = m_matrix[0][1] * m_matrix[1][3] * m_matrix[2][2]
+ m_matrix[0][2] * m_matrix[1][1] * m_matrix[2][3]
+ m_matrix[0][3] * m_matrix[1][2] * m_matrix[2][1]
- m_matrix[0][1] * m_matrix[1][2] * m_matrix[2][3]
- m_matrix[0][2] * m_matrix[1][3] * m_matrix[2][1]
- m_matrix[0][3] * m_matrix[1][1] * m_matrix[2][2];
m_inverse[1][0] = m_matrix[1][0] * m_matrix[2][3] * m_matrix[3][2]
+ m_matrix[1][2] * m_matrix[2][0] * m_matrix[3][3]
+ m_matrix[1][3] * m_matrix[2][2] * m_matrix[3][0]
- m_matrix[1][0] * m_matrix[2][2] * m_matrix[3][3]
- m_matrix[1][2] * m_matrix[2][3] * m_matrix[3][0]
- m_matrix[1][3] * m_matrix[2][0] * m_matrix[3][2];
m_inverse[1][1] = m_matrix[0][0] * m_matrix[2][2] * m_matrix[3][3]
+ m_matrix[0][2] * m_matrix[2][3] * m_matrix[3][0]
+ m_matrix[0][3] * m_matrix[2][0] * m_matrix[3][2]
- m_matrix[0][0] * m_matrix[2][3] * m_matrix[3][2]
- m_matrix[0][2] * m_matrix[2][0] * m_matrix[3][3]
- m_matrix[0][3] * m_matrix[2][2] * m_matrix[3][0];
m_inverse[1][2] = m_matrix[0][0] * m_matrix[1][3] * m_matrix[3][2]
+ m_matrix[0][2] * m_matrix[1][0] * m_matrix[3][3]
+ m_matrix[0][3] * m_matrix[1][2] * m_matrix[3][0]
- m_matrix[0][0] * m_matrix[1][2] * m_matrix[3][3]
- m_matrix[0][2] * m_matrix[1][3] * m_matrix[3][0]
- m_matrix[0][3] * m_matrix[1][0] * m_matrix[3][2];
m_inverse[1][3] = m_matrix[0][0] * m_matrix[1][2] * m_matrix[2][3]
+ m_matrix[0][2] * m_matrix[1][3] * m_matrix[2][0]
+ m_matrix[0][3] * m_matrix[1][0] * m_matrix[2][2]
- m_matrix[0][0] * m_matrix[1][3] * m_matrix[2][2]
- m_matrix[0][2] * m_matrix[1][0] * m_matrix[2][3]
- m_matrix[0][3] * m_matrix[1][2] * m_matrix[2][0];
m_inverse[2][0] = m_matrix[1][0] * m_matrix[2][1] * m_matrix[3][3]
+ m_matrix[1][1] * m_matrix[2][3] * m_matrix[3][0]
+ m_matrix[1][3] * m_matrix[2][0] * m_matrix[3][1]
- m_matrix[1][0] * m_matrix[2][3] * m_matrix[3][1]
- m_matrix[1][1] * m_matrix[2][0] * m_matrix[3][3]
- m_matrix[1][3] * m_matrix[2][1] * m_matrix[3][0];
m_inverse[2][1] = m_matrix[0][0] * m_matrix[2][3] * m_matrix[3][1]
+ m_matrix[0][1] * m_matrix[2][0] * m_matrix[3][3]
+ m_matrix[0][3] * m_matrix[2][1] * m_matrix[3][0]
- m_matrix[0][0] * m_matrix[2][1] * m_matrix[3][3]
- m_matrix[0][1] * m_matrix[2][3] * m_matrix[3][0]
- m_matrix[0][3] * m_matrix[2][0] * m_matrix[3][1];
m_inverse[2][2] = m_matrix[0][0] * m_matrix[1][1] * m_matrix[3][3]
+ m_matrix[0][1] * m_matrix[1][3] * m_matrix[3][0]
+ m_matrix[0][3] * m_matrix[1][0] * m_matrix[3][1]
- m_matrix[0][0] * m_matrix[1][3] * m_matrix[3][1]
- m_matrix[0][1] * m_matrix[1][0] * m_matrix[3][3]
- m_matrix[0][3] * m_matrix[1][1] * m_matrix[3][0];
m_inverse[2][3] = m_matrix[0][0] * m_matrix[1][3] * m_matrix[2][1]
+ m_matrix[0][1] * m_matrix[1][0] * m_matrix[2][3]
+ m_matrix[0][3] * m_matrix[1][1] * m_matrix[2][0]
- m_matrix[0][0] * m_matrix[1][1] * m_matrix[2][3]
- m_matrix[0][1] * m_matrix[1][3] * m_matrix[2][0]
- m_matrix[0][3] * m_matrix[1][0] * m_matrix[2][1];
m_inverse[3][0] = m_matrix[1][0] * m_matrix[2][2] * m_matrix[3][1]
+ m_matrix[1][1] * m_matrix[2][0] * m_matrix[3][2]
+ m_matrix[1][2] * m_matrix[2][1] * m_matrix[3][0]
- m_matrix[1][0] * m_matrix[2][1] * m_matrix[3][2]
- m_matrix[1][1] * m_matrix[2][2] * m_matrix[3][0]
- m_matrix[1][2] * m_matrix[2][0] * m_matrix[3][1];
m_inverse[3][1] = m_matrix[0][0] * m_matrix[2][1] * m_matrix[3][2]
+ m_matrix[0][1] * m_matrix[2][2] * m_matrix[3][0]
+ m_matrix[0][2] * m_matrix[2][0] * m_matrix[3][1]
- m_matrix[0][0] * m_matrix[2][2] * m_matrix[3][1]
- m_matrix[0][1] * m_matrix[2][0] * m_matrix[3][2]
- m_matrix[0][2] * m_matrix[2][1] * m_matrix[3][0];
m_inverse[3][2] = m_matrix[0][0] * m_matrix[1][2] * m_matrix[3][1]
+ m_matrix[0][1] * m_matrix[1][0] * m_matrix[3][2]
+ m_matrix[0][2] * m_matrix[1][1] * m_matrix[3][0]
- m_matrix[0][0] * m_matrix[1][1] * m_matrix[3][2]
- m_matrix[0][1] * m_matrix[1][2] * m_matrix[3][0]
- m_matrix[0][2] * m_matrix[1][0] * m_matrix[3][1];
m_inverse[3][3] = m_matrix[0][0] * m_matrix[1][1] * m_matrix[2][2]
+ m_matrix[0][1] * m_matrix[1][2] * m_matrix[2][0]
+ m_matrix[0][2] * m_matrix[1][0] * m_matrix[2][1]
- m_matrix[0][0] * m_matrix[1][2] * m_matrix[2][1]
- m_matrix[0][1] * m_matrix[1][0] * m_matrix[2][2]
- m_matrix[0][2] * m_matrix[1][1] * m_matrix[2][0];
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
m_inverse[i][j] /= det;
}
}
m_inverse_valid = true;
}
Matrix Matrix::getInverse()
{
calculateInverse();
Matrix m;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
m.m_matrix[i][j] = m_inverse[i][j];
m.m_inverse[i][j] = m_matrix[i][j];
}
}
m.m_inverse_calculated = true;
m.m_inverse_valid = m_inverse_valid;
return m;
}
Matrix & Matrix::operator*=(const Matrix & other)
{
Matrix temp = (*this) * other;
(*this) = temp;
return (*this);
}
bool operator==(const Matrix & m1, const Matrix & m2)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (! FP_EQ(m1[i][j], m2[i][j]))
return false;
}
}
return true;
}
std::ostream & operator<<(std::ostream & out, const Matrix & m)
{
out << std::setprecision(3);
for (int i = 0; i < 4; i++)
{
out << (i == 0 ? "[ " : " ");
out << "[ ";
for (int j = 0; j < 4; j++)
{
out << m[i][j];
if (j < 3)
out << ", ";
}
out << " ]";
if (i == 3)
out << " ]";
out << std::endl;
}
return out;
}
-75
View File
@@ -1,75 +0,0 @@
#ifndef MATRIX_H
#define MATRIX_H MATRIX_H
#include "Vector.h"
#include <iostream>
class Matrix
{
public:
Matrix();
typedef double Matrix_row_t[4];
Matrix_row_t & operator[](int idx) { return m_matrix[idx]; }
const Matrix_row_t & operator[](int idx) const { return m_matrix[idx]; }
static Matrix identity();
double determinant();
Matrix getInverse();
Matrix & operator*=(const Matrix & other);
Matrix operator*(const Matrix & m2) const
{
Matrix res;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
res[i][j] = m_matrix[i][0] * m2.m_matrix[0][j]
+ m_matrix[i][1] * m2.m_matrix[1][j]
+ m_matrix[i][2] * m2.m_matrix[2][j]
+ m_matrix[i][3] * m2.m_matrix[3][j];
}
}
return res;
}
/* transform a point */
Vector operator*(const Vector & v)
{
Vector res;
for (int i = 0; i < 3; i++)
{
res[i] = m_matrix[i][0] * v[0]
+ m_matrix[i][1] * v[1]
+ m_matrix[i][2] * v[2]
+ m_matrix[i][3]; /* v[3] is implicitly 1.0 */
}
return res;
}
/* transform a direction */
Vector operator%(const Vector & v)
{
Vector res;
for (int i = 0; i < 3; i++)
{
res[i] = m_matrix[i][0] * v[0]
+ m_matrix[i][1] * v[1]
+ m_matrix[i][2] * v[2];
}
return res;
}
protected:
double m_matrix[4][4];
double m_inverse[4][4];
bool m_inverse_calculated;
bool m_inverse_valid;
void calculateInverse();
};
bool operator==(const Matrix & m1, const Matrix & m2);
std::ostream & operator<<(std::ostream & out, const Matrix & m);
#endif
-95
View File
@@ -1,95 +0,0 @@
#include <math.h> /* acos(), M_PI */
#include "Polygon.h"
#define FP_EQUAL(x,y) (fabs((x)-(y)) < 1E-3)
/*
* from http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
* Return the angle between two vectors on a plane
* The angle is from vector 1 to vector 2, positive anticlockwise
* The result is between -pi -> pi
*/
static double angle2D(double x1, double y1, double x2, double y2)
{
double dtheta, theta1, theta2;
theta1 = atan2(y1,x1);
theta2 = atan2(y2,x2);
dtheta = theta2 - theta1;
while (dtheta > M_PI)
dtheta -= 2.0 * M_PI;
while (dtheta < -M_PI)
dtheta += 2.0 * M_PI;
return dtheta;
}
static bool similarPoint(const Vector & v, const Vector & w)
{
return FP_EQUAL(v[0], w[0]) && FP_EQUAL(v[1], w[1]) && FP_EQUAL(v[2], w[2]);
}
Polygon & Polygon::add(const Vector & v)
{
int sz = size();
if (sz < 1 || !similarPoint(v, *(*this)[sz-1]))
{
push_back(new Vector(v));
}
return *this;
}
Polygon & Polygon::add(refptr<Vector> v)
{
int sz = size();
if (sz < 1 || !similarPoint(*v, *(*this)[sz-1]))
{
push_back(v);
}
return *this;
}
bool Polygon::containsPoint2D(const Vector & v)
{
if (size() < 3)
{
return false;
}
if (similarPoint(*(*this)[0], *(*this)[size()-1]))
{
pop_back();
}
double angle_sum = 0.0;
for (int i = 0, sz = size(); i < sz; i++)
{
Vector v1 = *(*this)[i] - v;
Vector v2 = *(*this)[(i+1) % sz] - v;
double angle = angle2D(v1[0], v1[1], v2[0], v2[1]);
angle_sum += angle;
}
return FP_EQUAL(angle_sum, 2.0 * M_PI);
}
bool Polygon::containsPointConvex(const Vector & v)
{
if (size() < 3)
{
return false;
}
if (similarPoint(*(*this)[0], *(*this)[size()-1]))
{
pop_back();
}
double angle_sum = 0.0;
for (int i = 0, sz = size(); i < sz; i++)
{
Vector v1 = *(*this)[i] - v;
Vector v2 = *(*this)[(i+1) % sz] - v;
double cosine = (v1 % v2) / (v1.mag() * v2.mag());
double angle = acos(cosine);
angle_sum += angle;
}
return FP_EQUAL(angle_sum, 2.0 * M_PI);
}
-19
View File
@@ -1,19 +0,0 @@
#ifndef POLYGON_H
#define POLYGON_H
#include <vector>
#include "refptr.h"
#include "Vector.h"
class Polygon : public std::vector< refptr<Vector> >
{
public:
Polygon & add(const Vector & v);
Polygon & add(refptr<Vector> v);
bool containsPoint2D(const Vector & v);
bool containsPointConvex(const Vector & v);
};
#endif
-17
View File
@@ -1,17 +0,0 @@
#include <stdlib.h> /* rand() */
#include <iostream>
#include "Ray.h"
Ray Ray::randomRay()
{
return Ray(Vector(0, 0, 0), Vector::randomVector());
}
std::ostream & operator<<(std::ostream & out, const Ray & r)
{
out << "(" << r.getOrigin() << " -> " << r.getDirection() << ")";
return out;
}
-58
View File
@@ -1,58 +0,0 @@
#ifndef RAY_H
#define RAY_H RAY_H
#include "Vector.h"
#include <iostream>
class Ray
{
public:
Ray()
{
}
Ray(const Vector & origin, const Vector & direction)
: m_origin(origin), m_direction(direction)
{
m_direction.normalize();
}
/*
* return a vector for the point at distance dist
* from the ray's origin point, along its direction.
*/
Vector getPositionAt(double dist) const
{
return Vector(
m_origin[0] + dist * m_direction[0],
m_origin[1] + dist * m_direction[1],
m_origin[2] + dist * m_direction[2]);
}
Ray shift(double amt)
{
return Ray(getPositionAt(amt), m_direction, false);
}
const Vector & getOrigin() const { return m_origin; }
const Vector & getDirection() const { return m_direction; }
static Ray randomRay();
Vector operator[](double dist) const { return getPositionAt(dist); }
protected:
Ray(const Vector & origin, const Vector & direction, bool no_norm)
: m_origin(origin), m_direction(direction)
{
/* no normalize version */
}
Vector m_origin;
Vector m_direction;
};
std::ostream & operator<<(std::ostream & out, const Ray & r);
#endif
-73
View File
@@ -1,73 +0,0 @@
#ifndef SCOPE_H
#define SCOPE_H
#include <list>
#include <map>
#include <string>
class Scope
{
public:
Scope() { push(); }
bool contains(const std::string & key)
{
for (m_list_type::const_reverse_iterator it = m_list.rbegin();
it != m_list.rend();
it++)
{
if (it->find(key) != it->end())
{
return true;
}
}
return false;
}
double get(const std::string & key)
{
for (m_list_type::reverse_iterator it = m_list.rbegin();
it != m_list.rend();
it++)
{
if (it->find(key) != it->end())
{
return (*it)[key];
}
}
return 0.0;
}
void putLocal(const std::string & key, double val)
{
(*m_list.rbegin())[key] = val;
}
void putGlobal(const std::string & key, double val)
{
for (m_list_type::reverse_iterator it = m_list.rbegin();
it != m_list.rend();
it++)
{
if (it->find(key) != it->end())
{
(*it)[key] = val;
return;
}
}
putLocal(key, val);
}
void push()
{
m_list.push_back(std::map< std::string, double >());
}
void pop()
{
if (m_list.size() > 1)
{
m_list.pop_back();
}
}
protected:
typedef std::list< std::map< std::string, double > > m_list_type;
m_list_type m_list;
};
#endif
-127
View File
@@ -1,127 +0,0 @@
#include "Solver.h"
#include <math.h>
/* Generic Solver constructor */
Solver::Solver(double a, double b, double c, double d, double e)
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
this->e = e;
}
Solver::~Solver()
{
}
Solver::Result::Result()
{
numResults = 0;
}
/**************************************************************************
* LinearSolver methods *
*************************************************************************/
LinearSolver::LinearSolver(double a, double b)
: Solver(a, b)
{
}
/* solve a linear equation */
Solver::Result LinearSolver::solve()
{
/* equation ax + b = 0 */
Result res;
if (a == 0.0)
{
if (b == 0.0)
{
res.numResults = 1;
res.results[0] = 0.0;
}
else
{
res.numResults = 0;
}
}
else
{
res.numResults = 1;
res.results[0] = -b / a;
}
return res;
}
/**************************************************************************
* QuadraticSolver methods *
*************************************************************************/
QuadraticSolver::QuadraticSolver(double a, double b, double c)
: Solver(a, b, c)
{
}
/* solve a quadratic equation */
Solver::Result QuadraticSolver::solve()
{
Result res;
double discriminant = b * b - 4 * a * c;
if (discriminant < 0.0)
{
res.numResults = 0;
}
else
{
double sqrt_discriminant = sqrt(discriminant);
double two_a = 2.0 * a;
if (sqrt_discriminant == 0.0)
{
res.numResults = 1;
res.results[0] = (-b) / two_a;
}
else
{
res.numResults = 2;
res.results[0] = (-b - sqrt_discriminant) / two_a;
res.results[1] = (-b + sqrt_discriminant) / two_a;
}
}
return res;
}
/**************************************************************************
* CubicSolver methods *
*************************************************************************/
CubicSolver::CubicSolver(double a, double b, double c, double d)
: Solver(a, b, c, d)
{
}
Solver::Result CubicSolver::solve()
{
Result res;
/* TODO: fill in */
return res;
}
/**************************************************************************
* QuarticSolver methods *
*************************************************************************/
QuarticSolver::QuarticSolver(double a, double b, double c, double d, double e)
: Solver(a, b, c, d, e)
{
}
Solver::Result QuarticSolver::solve()
{
Result res;
/* TODO: fill in */
return res;
}
-57
View File
@@ -1,57 +0,0 @@
#ifndef SOLVER_H
#define SOLVER_H SOLVER_H
class Solver
{
public:
class Result
{
public:
Result();
int numResults;
double results[4];
};
Solver(double a = 0.0,
double b = 0.0,
double c = 0.0,
double d = 0.0,
double e = 0.0);
virtual ~Solver();
virtual Result solve() = 0;
protected:
double a, b, c, d, e;
};
class LinearSolver : public Solver
{
public:
LinearSolver(double a, double b);
Result solve();
};
class QuadraticSolver : public Solver
{
public:
QuadraticSolver(double a, double b, double c);
Result solve();
};
class CubicSolver : public Solver
{
public:
CubicSolver(double a, double b, double c, double d);
Result solve();
};
class QuarticSolver : public Solver
{
public:
QuarticSolver(double a, double b, double c, double d, double e);
Result solve();
};
#endif
-16
View File
@@ -1,16 +0,0 @@
#ifndef TEXTURE_H
#define TEXTURE_H TEXTURE_H
/* A loaded image texture. Pixel data is stored as tightly packed RGBA
* bytes (4 channels) and is owned by this struct -- free it with
* stbi_image_free(). */
struct Texture
{
int width;
int height;
int channels;
unsigned char * data;
};
#endif
-72
View File
@@ -1,72 +0,0 @@
#include "Transform.h"
#include <math.h>
void Transform::lookAt(const Vector & eye,
const Vector & focus,
const Vector & up)
{
Vector forward = focus - eye;
forward.normalize();
Vector perpendicular_up = (up - up.proj(forward)).normalize();
Vector right = forward * perpendicular_up;
Matrix mult;
mult[0][0] = right[0];
mult[0][1] = right[1];
mult[0][2] = right[2];
mult[1][0] = forward[0];
mult[1][1] = forward[1];
mult[1][2] = forward[2];
mult[2][0] = perpendicular_up[0];
mult[2][1] = perpendicular_up[1];
mult[2][2] = perpendicular_up[2];
m_matrix *= mult;
translate(-eye[0], -eye[1], -eye[2]);
}
void Transform::translate(double x, double y, double z)
{
Matrix t = Matrix::identity();
t[0][3] = x;
t[1][3] = y;
t[2][3] = z;
m_matrix *= t;
}
void Transform::rotate(double angle, double xv, double yv, double zv)
{
/* formula from http://en.wikipedia.org/wiki/Rotation_matrix */
Vector l(xv, yv, zv);
l.normalize();
double c = cos(M_PI * angle / 180.0);
double s = sin(M_PI * angle / 180.0);
double lx2 = l[0] * l[0];
double ly2 = l[1] * l[1];
double lz2 = l[2] * l[2];
Matrix t = Matrix::identity();
t[0][0] = lx2 + (1 - lx2) * c;
t[0][1] = l[0] * l[1] * (1 - c) - l[2] * s;
t[0][2] = l[0] * l[2] * (1 - c) + l[1] * s;
t[1][0] = l[0] * l[1] * (1 - c) + l[2] * s;
t[1][1] = ly2 + (1 - ly2) * c;
t[1][2] = l[1] * l[2] * (1 - c) - l[0] * s;
t[2][0] = l[0] * l[2] * (1 - c) - l[1] * s;
t[2][1] = l[1] * l[2] * (1 - c) + l[0] * s;
t[2][2] = lz2 + (1 - lz2) * c;
m_matrix *= t;
}
void Transform::scale(double xs, double ys, double zs)
{
Matrix t = Matrix::identity();
t[0][0] = xs;
t[1][1] = ys;
t[2][2] = zs;
m_matrix *= t;
}
-72
View File
@@ -1,72 +0,0 @@
#ifndef TRANSFORM_H
#define TRANSFORM_H TRANSFORM_H
#include "refptr.h"
#include "Matrix.h"
#include "Ray.h"
#include "Vector.h"
#include <stack>
class Transform
{
public:
Transform getInverse()
{
Transform inv;
inv.m_matrix = m_matrix.getInverse();
return inv;
}
void lookAt(const Vector & eye,
const Vector & focus,
const Vector & up);
void translate(double x, double y, double z);
void translate(refptr<Vector> vec)
{
translate((*vec)[0], (*vec)[1], (*vec)[2]);
}
void rotate(double angle, double xv, double yv, double zv);
void rotate(double angle, refptr<Vector> vec)
{
rotate(angle, (*vec)[0], (*vec)[1], (*vec)[2]);
}
void scale(double xs, double ys, double zs);
void scale(refptr<Vector> vec)
{
scale((*vec)[0], (*vec)[1], (*vec)[2]);
}
Matrix & getMatrix() { return m_matrix; }
Vector transform_point(const Vector & v)
{
return m_matrix * v;
}
Vector transform_direction(const Vector & v)
{
return m_matrix % v;
}
Vector transform_normal(const Vector & v)
{
return (m_matrix % v).normalize();
}
Ray transform_ray(const Ray & r)
{
return Ray(m_matrix * r.getOrigin(), m_matrix % r.getDirection());
}
Transform operator*(const Transform & other) const
{
Transform t;
t.m_matrix = m_matrix * other.m_matrix;
return t;
}
protected:
Matrix m_matrix;
};
#endif
-29
View File
@@ -1,29 +0,0 @@
#include <stdlib.h> /* rand() */
#include <math.h>
#include <iostream>
#include "Vector.h"
const Vector Vector::X(1, 0, 0);
const Vector Vector::Y(0, 1, 0);
const Vector Vector::Z(0, 0, 1);
Vector Vector::randomVector()
{
double x, y, z;
do
{
x = 2.0 * rand() / RAND_MAX - 1.0;
y = 2.0 * rand() / RAND_MAX - 1.0;
z = 2.0 * rand() / RAND_MAX - 1.0;
} while (x*x + y*y + z*z > 1.0);
return Vector(x, y, z).normalize();
}
std::ostream & operator<<(std::ostream & out, const Vector & v)
{
out << "[" << v[0] << ", " << v[1] << ", " << v[2] << "]";
return out;
}
-203
View File
@@ -1,203 +0,0 @@
#ifndef VECTOR_H
#define VECTOR_H VECTOR_H
#include <math.h> /* sqrt() */
#include <iostream>
class Vector
{
public:
Vector()
{
m_array[0] = 0.0;
m_array[1] = 0.0;
m_array[2] = 0.0;
}
Vector(double x, double y, double z)
{
m_array[0] = x;
m_array[1] = y;
m_array[2] = z;
}
Vector & normalize()
{
double length = mag();
m_array[0] /= length;
m_array[1] /= length;
m_array[2] /= length;
return *this;
}
double mag() const
{
return sqrt(m_array[0] * m_array[0]
+ m_array[1] * m_array[1]
+ m_array[2] * m_array[2]);
}
double mag2() const
{
return m_array[0] * m_array[0]
+ m_array[1] * m_array[1]
+ m_array[2] * m_array[2];
}
double dist_to(const Vector & other) const
{
return (other - *this).mag();
}
Vector proj(const Vector & target) const
{
Vector target_normalized = target;
target_normalized.normalize();
return target_normalized * ((*this) % target_normalized);
}
Vector reflect(const Vector & target) const
{
return (*this) - target * (2 * dot(target));
}
/*
* from http://www.flipcode.com/archives/
* Reflections_and_Refraction_in_Raytracing.shtml
* target: normal vector of surface
* n1: refraction index of object we're coming from
* n2: refraction index of object we're going into
*/
Vector refract(const Vector & target, double n1, double n2) const
{
const double n = n1 / n2;
const double cosI = -dot(target);
const double sinT2 = n * n * (1.0 - cosI * cosI);
if (sinT2 > 1.0)
return Vector(0.0, 0.0, 0.0);
return (*this) * n + target * (n * cosI - sqrt(1.0 - sinT2));
}
Vector getPerpendicular() const
{
Vector t = *this;
t.normalize();
Vector p = t * Vector(0, 0, 1);
if (p.mag() <= 0.1)
{
p = t * Vector(1, 0, 0);
}
return p;
}
Vector mult(const Vector & v2) const
{
return Vector(
m_array[0] * v2.m_array[0],
m_array[1] * v2.m_array[1],
m_array[2] * v2.m_array[2]);
}
Vector div(const Vector & v2) const
{
return Vector(
m_array[0] / v2.m_array[0],
m_array[1] / v2.m_array[1],
m_array[2] / v2.m_array[2]);
}
Vector operator-() const
{
return Vector(
-m_array[0],
-m_array[1],
-m_array[2]);
}
/* Compute the dot-product of two vectors */
double dot(const Vector & v2) const
{
return m_array[0] * v2.m_array[0]
+ m_array[1] * v2.m_array[1]
+ m_array[2] * v2.m_array[2];
}
double operator%(const Vector & v2) const { return dot(v2); }
/* Compute the cross-product of two vectors */
Vector cross(const Vector & v2) const
{
return Vector(
m_array[1] * v2.m_array[2] - m_array[2] * v2.m_array[1],
m_array[2] * v2.m_array[0] - m_array[0] * v2.m_array[2],
m_array[0] * v2.m_array[1] - m_array[1] * v2.m_array[0]);
}
Vector operator*(const Vector & v2) const { return cross(v2); }
Vector operator+(const Vector & v2) const
{
return Vector(
m_array[0] + v2.m_array[0],
m_array[1] + v2.m_array[1],
m_array[2] + v2.m_array[2]);
}
Vector operator-(const Vector & v2) const
{
return Vector(
m_array[0] - v2.m_array[0],
m_array[1] - v2.m_array[1],
m_array[2] - v2.m_array[2]);
}
Vector operator*(double scale) const
{
return Vector(
m_array[0] * scale,
m_array[1] * scale,
m_array[2] * scale);
}
Vector operator/(double scale) const
{
return Vector(
m_array[0] / scale,
m_array[1] / scale,
m_array[2] / scale);
}
Vector & operator+=(const Vector & v2)
{
m_array[0] += v2.m_array[0];
m_array[1] += v2.m_array[1];
m_array[2] += v2.m_array[2];
return *this;
}
Vector & operator-=(const Vector & v2)
{
m_array[0] -= v2.m_array[0];
m_array[1] -= v2.m_array[1];
m_array[2] -= v2.m_array[2];
return *this;
}
double & operator[](int idx) { return m_array[idx]; }
double operator[](int idx) const { return m_array[idx]; }
static Vector randomVector();
static const Vector X;
static const Vector Y;
static const Vector Z;
protected:
double m_array[3];
};
std::ostream & operator<<(std::ostream & out, const Vector & v);
static inline Vector operator*(double d, const Vector & v) { return v * d; }
static inline Vector operator/(double d, const Vector & v) { return v / d; }
#endif
-115
View File
@@ -1,115 +0,0 @@
#ifndef REFPTR_H
#define REFPTR_H REFPTR_H
#include <stdlib.h> /* NULL */
#include <atomic> /* std::atomic */
template <typename T>
class refptr
{
public:
refptr();
refptr(T * ptr);
refptr(const refptr<T> & orig);
refptr & operator=(const refptr<T> & orig);
refptr & operator=(T * ptr);
~refptr();
T & operator*() const;
T * operator->() const;
bool isNull() const { return m_ptr == NULL; }
bool operator==(const refptr<T> & right) const;
bool operator!=(const refptr<T> & right) const;
private:
void cloneFrom(const refptr<T> & orig);
void destroy();
T * m_ptr;
/* reference count is atomic so that refptr copies may be made
* concurrently from multiple threads (e.g. the multithreaded
* renderer) without corrupting the count */
std::atomic<int> * m_refCount;
};
template <typename T> refptr<T>::refptr()
{
m_ptr = NULL;
m_refCount = NULL;
}
template <typename T> refptr<T>::refptr(T * ptr)
{
m_ptr = ptr;
m_refCount = new std::atomic<int>(1);
}
template <typename T> refptr<T>::refptr(const refptr<T> & orig)
{
cloneFrom(orig);
}
template <typename T> refptr<T> & refptr<T>::operator=(const refptr<T> & orig)
{
destroy();
cloneFrom(orig);
return *this;
}
template <typename T> refptr<T> & refptr<T>::operator=(T * ptr)
{
destroy();
m_ptr = ptr;
m_refCount = new std::atomic<int>(1);
return *this;
}
template <typename T> void refptr<T>::cloneFrom(const refptr<T> & orig)
{
this->m_ptr = orig.m_ptr;
this->m_refCount = orig.m_refCount;
if (m_refCount != NULL)
m_refCount->fetch_add(1, std::memory_order_relaxed);
}
template <typename T> refptr<T>::~refptr()
{
destroy();
}
template <typename T> void refptr<T>::destroy()
{
if (m_refCount != NULL)
{
/* fetch_sub returns the value prior to the decrement; if it was 1
* then this was the last reference and we own the cleanup */
if (m_refCount->fetch_sub(1, std::memory_order_acq_rel) == 1)
{
delete m_ptr;
delete m_refCount;
}
}
}
template <typename T> T & refptr<T>::operator*() const
{
return *m_ptr;
}
template <typename T> T * refptr<T>::operator->() const
{
return m_ptr;
}
template <typename T> bool refptr<T>::operator==(const refptr<T> & right) const
{
return m_ptr == right.m_ptr;
}
template <typename T> bool refptr<T>::operator!=(const refptr<T> & right) const
{
return m_ptr != right.m_ptr;
}
#endif
-5
View File
@@ -1,5 +0,0 @@
/* Single translation unit that compiles the stb_image implementation. */
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more