Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e188a0594 | ||
|
|
c1f41cb6f6 | ||
|
|
50143076ab | ||
|
|
c33cdabdec | ||
|
|
ad16643111 | ||
|
|
45f5f8098e | ||
|
|
28b619c566 | ||
|
|
4f512405b2 | ||
|
|
fe80488c5e | ||
|
|
f8f4d7b21a | ||
|
|
5651570c2b | ||
|
|
71c236abb0 | ||
|
|
c0fc061834 | ||
|
|
61ce9d3543 | ||
|
|
9849c16f66 | ||
|
|
aacd4f6c9e | ||
|
|
5b6b627a43 | ||
|
|
4f2e327533 | ||
|
|
c858b4dcde | ||
|
|
7a43a98816 | ||
|
|
47e31714f3 | ||
|
|
424d58c59b | ||
|
|
5996e38e6e | ||
|
|
b00bb75189 | ||
|
|
0aa949c60b | ||
|
|
420203e95d | ||
|
|
c667ce26d7 | ||
|
|
13cc2c0ff9 | ||
|
|
1c4018e47a | ||
|
|
083a5b600f | ||
|
|
3a2d21b787 | ||
|
|
441d758845 | ||
|
|
5da0c10182 | ||
|
|
b89f9ec331 | ||
|
|
add3d7c861 | ||
|
|
4e241a1871 | ||
|
|
15a861802a | ||
|
|
66a8b98aeb | ||
|
|
0f945b69cc | ||
|
|
314da11005 | ||
|
|
8437ab8753 | ||
|
|
d514219ae6 | ||
|
|
efa47f28ca | ||
|
|
e653855556 | ||
|
|
1510de3b36 | ||
|
|
8550d4068f | ||
|
|
cd93b67274 | ||
|
|
18965fe1bd | ||
|
|
57eff4830e | ||
|
|
b5ee92c544 |
44
.github/workflows/cmake.yml
vendored
Normal file
44
.github/workflows/cmake.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: CMake
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
continue-on-error: ${{ matrix.allow_failure }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-latest ]
|
||||||
|
allow_failure: [false]
|
||||||
|
# include:
|
||||||
|
# - os: macos-latest
|
||||||
|
# allow_failure: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Test round 1
|
||||||
|
working-directory: ${{github.workspace}}/build
|
||||||
|
run: make test
|
||||||
|
|
||||||
|
- name: Test round 2
|
||||||
|
working-directory: ${{github.workspace}}/build/tests
|
||||||
|
run: make test
|
||||||
|
|
||||||
69
.travis.yml
69
.travis.yml
@@ -1,17 +1,13 @@
|
|||||||
dist: bionic
|
|
||||||
language: c
|
language: c
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- lcov
|
- lcov
|
||||||
|
- imagemagick
|
||||||
os:
|
homebrew:
|
||||||
- linux
|
packages:
|
||||||
- osx
|
- imagemagick
|
||||||
compiler:
|
|
||||||
- clang
|
|
||||||
- gcc
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- mkdir build
|
- mkdir build
|
||||||
@@ -22,10 +18,58 @@ script:
|
|||||||
- cd tests
|
- cd tests
|
||||||
- ./testMyRays
|
- ./testMyRays
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python@2 ; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install imagemagick ; fi
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
- os: linux
|
||||||
|
dist: bionic
|
||||||
|
arch: amd64
|
||||||
|
compiler: gcc
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
dist: bionic
|
||||||
|
arch: amd64
|
||||||
|
compiler: clang
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
dist: focal
|
||||||
|
arch: amd64
|
||||||
|
compiler: gcc
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
dist: focal
|
||||||
|
arch: amd64
|
||||||
|
compiler: clang
|
||||||
|
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
compiler: clang
|
||||||
|
osx_image: xcode12.2
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
dist: focal
|
||||||
|
arch: arm64
|
||||||
|
compiler: gcc
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
compiler: clang
|
||||||
|
osx_image: xcode10.3
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
compiler: clang
|
||||||
|
osx_image: xcode11.6
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
compiler: clang
|
||||||
|
osx_image: xcode12
|
||||||
|
|
||||||
- stage: "Coverage"
|
- stage: "Coverage"
|
||||||
os: linux
|
os: linux
|
||||||
|
dist: bionic
|
||||||
|
arch: amd64
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
script:
|
script:
|
||||||
- mkdir coverage
|
- mkdir coverage
|
||||||
@@ -36,7 +80,8 @@ jobs:
|
|||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
before_install:
|
allow_failures:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
- os: linux
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python@2 ; fi
|
arch: arm64
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install imagemagick ; fi
|
|
||||||
|
- os: osx
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.11)
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
@@ -12,11 +12,17 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/external/covera
|
|||||||
option(PACKAGE_TESTS "Build the tests" ON)
|
option(PACKAGE_TESTS "Build the tests" ON)
|
||||||
option(ENABLE_COVERAGE "Build for code coverage" OFF)
|
option(ENABLE_COVERAGE "Build for code coverage" OFF)
|
||||||
|
|
||||||
option(SHOW_STATS "Show rendering stat" ON)
|
option(SHOW_STATS "Show rendering stat" OFF)
|
||||||
if (SHOW_STATS)
|
if (SHOW_STATS)
|
||||||
add_compile_options(-DRENDER_STATS)
|
add_compile_options(-DRENDER_STATS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(USE_GPROF "Enable profiling" OFF)
|
||||||
|
if (USE_GPROF)
|
||||||
|
add_compile_options(-pg)
|
||||||
|
add_link_options(-pg)
|
||||||
|
endif()
|
||||||
|
|
||||||
option(USE_LUA "Enable the use of Lua" ON)
|
option(USE_LUA "Enable the use of Lua" ON)
|
||||||
if (USE_LUA)
|
if (USE_LUA)
|
||||||
add_compile_options(-DENABLE_LUA_SUPPORT)
|
add_compile_options(-DENABLE_LUA_SUPPORT)
|
||||||
|
|||||||
79
README.md
79
README.md
@@ -1,4 +1,4 @@
|
|||||||
[](https://codecov.io/gh/Godzil/DoRayMe) [](https://www.codacy.com/manual/Godzil/DoRayMe?utm_source=github.com&utm_medium=referral&utm_content=Godzil/DoRayMe&utm_campaign=Badge_Grade) [](https://coveralls.io/github/Godzil/DoRayMe?branch=master) [](https://travis-ci.org/Godzil/DoRayMe)
|
[](https://codecov.io/gh/Godzil/DoRayMe) [](https://www.codacy.com/manual/Godzil/DoRayMe?utm_source=github.com&utm_medium=referral&utm_content=Godzil/DoRayMe&utm_campaign=Badge_Grade) [](https://coveralls.io/github/Godzil/DoRayMe?branch=master) [](https://github.com/Godzil/DoRayMe/actions/workflows/cmake.yml)
|
||||||
|
|
||||||
DoRayMe
|
DoRayMe
|
||||||
=======
|
=======
|
||||||
@@ -17,101 +17,86 @@ as texture, also use [NanoJPEG](https://keyj.emphy.de/nanojpeg/) to use jpeg fil
|
|||||||
Examples outputs
|
Examples outputs
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
**From chapter 05 - Sphere intersections:**
|
#### From chapter 05 - Sphere intersections:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 06 - Phong shading:**
|
#### From Chapter 06 - Phong shading:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 07 - World / Camera / Scenes:**
|
#### From Chapter 07 - World / Camera / Scenes:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 08 - Shadows:**
|
#### From Chapter 08 - Shadows:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 09 - Planes:**
|
#### From Chapter 09 - Planes:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 10 - Patterns:**
|
#### From Chapter 10 - Patterns:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 11 - Reflections, Transparency & Refractions:**
|
#### From Chapter 11 - Reflections, Transparency & Refractions:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Bonus: Zooming on a reflective ball:
|
###### Bonus: Zooming on a reflective ball:
|
||||||

|

|
||||||
|
|
||||||
Zooming on a reflection on that ball:
|
###### Zooming on a reflection on that ball:
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 12 - Cubes:**
|
#### From Chapter 12 - Cubes:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 13 - Cylinders:**
|
#### From Chapter 13 - Cylinders:
|
||||||
|
|
||||||

|

|
||||||
Bonus:
|
###### Bonus:
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 14 - Groups & Bounding boxes:**
|
#### From Chapter 14 - Groups & Bounding boxes:
|
||||||

|

|
||||||
|
|
||||||
**From Chapter 15 - Triangles, Wavefrom OBJ files - Smooth trianges:**
|
#### From Chapter 15 - Triangles, Wavefrom OBJ files - Smooth trianges:
|
||||||

|

|
||||||
|
|
||||||
**Bonus (from the forum):**
|
#### From Chapter 16 - Constructive Solid Geomety:
|
||||||
|

|
||||||
|
|
||||||
|
#### Bonus (from the forum):
|
||||||
[Merry Christmas](https://forum.raytracerchallenge.com/thread/16/merry-christmas-scene-description)
|
[Merry Christmas](https://forum.raytracerchallenge.com/thread/16/merry-christmas-scene-description)
|
||||||

|

|
||||||
|
|
||||||
(about 1min render time using OpenMP on a 2.6Ghz Core i7 3720QM)
|
(about 1min render time using OpenMP on a 2.6Ghz Core i7 3720QM)
|
||||||
|
|
||||||
**Bonus chapter - Soft shadow / Area light**
|
#### Bonus chapter - Soft shadow / Area light
|
||||||
|
###### Without jitter:
|
||||||
Without jitter:
|
|
||||||

|

|
||||||
|
###### With jitter:
|
||||||
With jitter:
|
|
||||||

|

|
||||||
|
|
||||||
**Bonus chapter - Texture mapping**
|
#### Bonus chapter - Texture mapping
|
||||||
|
###### Spherical mapping:
|
||||||
Spherical mapping:
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Planar mapping:
|
###### Planar mapping:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Cylindrical mapping:
|
###### Cylindrical mapping:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Aligncheck plane:
|
###### Aligncheck plane:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Cubical mapping:
|
###### Cubical mapping:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Image mapping:
|
###### Image mapping:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Skybox:
|
###### Skybox:
|
||||||
|

|
||||||
|
|
||||||

|
###### Large OBJ file:
|
||||||
|

|
||||||
|
|||||||
2
external/googletest
vendored
2
external/googletest
vendored
Submodule external/googletest updated: 23b2a3b1cf...c9461a9b55
BIN
output/ch16_test.png
Normal file
BIN
output/ch16_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
BIN
output/dragon_scene.png
Normal file
BIN
output/dragon_scene.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
685
samplescenes/orrery.yml
Normal file
685
samplescenes/orrery.yml
Normal file
@@ -0,0 +1,685 @@
|
|||||||
|
# ======================================================
|
||||||
|
# orrery.yml
|
||||||
|
#
|
||||||
|
# This file describes the title image for the "Texture
|
||||||
|
# Mapping" bonus chapter at:
|
||||||
|
#
|
||||||
|
# http://www.raytracerchallenge.com/bonus/texture-mapping.html
|
||||||
|
#
|
||||||
|
# It requires several additional resources, provided as a
|
||||||
|
# separate download. The resources were found on the following
|
||||||
|
# sites:
|
||||||
|
#
|
||||||
|
# * https://www.bittbox.com/freebies/free-hi-resolution-wood-textures
|
||||||
|
# : the wooden texture for the table
|
||||||
|
# * https://astrogeology.usgs.gov/search/map/Mercury/Messenger/Global/Mercury_MESSENGER_MDIS_Basemap_LOI_Mosaic_Global_166m
|
||||||
|
# : the map of Mercury
|
||||||
|
# * http://planetpixelemporium.com/planets.html
|
||||||
|
# : maps of Earth, Mars, Jupiter, Saturn, Uranus, and Neptune
|
||||||
|
# * https://hdrihaven.com/hdri/?c=indoor&h=artist_workshop
|
||||||
|
# : the "artist workshop" environment map
|
||||||
|
#
|
||||||
|
# by Jamis Buck <jamis@jamisbuck.org>
|
||||||
|
# ======================================================
|
||||||
|
|
||||||
|
- add: camera
|
||||||
|
width: 800
|
||||||
|
height: 400
|
||||||
|
field-of-view: 1.2
|
||||||
|
from: [ 2, 4, -10]
|
||||||
|
to: [ -1, -1, 0 ]
|
||||||
|
up: [ 0, 1, 0 ]
|
||||||
|
|
||||||
|
# The scene as shown in the bonus chapter is rendered using an area light,
|
||||||
|
# precisely as described in the "Rendering soft shadows" bonus chapter,
|
||||||
|
# here: http://www.raytracerchallenge.com/bonus/area-light.html
|
||||||
|
#
|
||||||
|
# if you haven't implemented area lights, you can replace this with a point
|
||||||
|
# light located at [0, 2.5, -10].
|
||||||
|
|
||||||
|
- add: light
|
||||||
|
corner: [-5, 0, -10 ]
|
||||||
|
uvec: [ 10, 0, 0 ]
|
||||||
|
vvec: [ 0, 5, 0 ]
|
||||||
|
usteps: 10
|
||||||
|
vsteps: 5
|
||||||
|
jitter: true
|
||||||
|
intensity: [ 1, 1, 1 ]
|
||||||
|
|
||||||
|
# -------------------------------------------
|
||||||
|
# some common textures
|
||||||
|
# -------------------------------------------
|
||||||
|
|
||||||
|
- define: GOLD
|
||||||
|
value:
|
||||||
|
color: [ 1, 0.8, 0.1 ]
|
||||||
|
ambient: 0.1
|
||||||
|
diffuse: 0.6
|
||||||
|
specular: 0.3
|
||||||
|
shininess: 15
|
||||||
|
|
||||||
|
- define: SILVER
|
||||||
|
value:
|
||||||
|
color: [ 1, 1, 1 ]
|
||||||
|
ambient: 0.1
|
||||||
|
diffuse: 0.7
|
||||||
|
specular: 0.3
|
||||||
|
shininess: 15
|
||||||
|
|
||||||
|
# -----------------------------------------------
|
||||||
|
# CSG definition for the gears used to construct
|
||||||
|
# the orrery.
|
||||||
|
#
|
||||||
|
# NOTCH is a helper object used to create the
|
||||||
|
# teeth for the gears.
|
||||||
|
#
|
||||||
|
# GEAR is the actual gear object itself.
|
||||||
|
# -----------------------------------------------
|
||||||
|
|
||||||
|
- define: NOTCH
|
||||||
|
value:
|
||||||
|
add: csg
|
||||||
|
operation: difference
|
||||||
|
left:
|
||||||
|
type: cube
|
||||||
|
transform:
|
||||||
|
- [ scale, 1, 0.25, 1 ]
|
||||||
|
- [ translate, 1, 0, 1 ]
|
||||||
|
- [ rotate-y, 0.7854 ]
|
||||||
|
- [ scale, 1, 1, 0.1 ]
|
||||||
|
right:
|
||||||
|
type: cylinder
|
||||||
|
min: -0.26
|
||||||
|
max: 0.26
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.8, 1, 0.8 ]
|
||||||
|
|
||||||
|
- define: GEAR
|
||||||
|
value:
|
||||||
|
add: csg
|
||||||
|
operation: difference
|
||||||
|
left:
|
||||||
|
type: cylinder
|
||||||
|
min: -0.025
|
||||||
|
max: 0.025
|
||||||
|
closed: true
|
||||||
|
right:
|
||||||
|
type: group
|
||||||
|
children:
|
||||||
|
# center hole
|
||||||
|
- add: cylinder
|
||||||
|
min: -0.06
|
||||||
|
max: 0.06
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.1, 1, 0.1 ]
|
||||||
|
# crescents
|
||||||
|
- add: csg
|
||||||
|
operation: difference
|
||||||
|
left:
|
||||||
|
type: cylinder
|
||||||
|
min: -0.06
|
||||||
|
max: 0.06
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.7, 1, 0.7 ]
|
||||||
|
right:
|
||||||
|
type: cube
|
||||||
|
transform:
|
||||||
|
- [ scale, 1, 0.1, 0.2 ]
|
||||||
|
# teeth
|
||||||
|
- add: NOTCH
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 0.31415 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 0.6283 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 0.94245 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 1.2566 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 1.57075 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 1.8849 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 2.19905 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 2.5132 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 2.82735 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 3.1415 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -0.31415 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -0.6283 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -0.94245 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -1.2566 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -1.57075 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -1.8849 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -2.19905 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -2.5132 ]
|
||||||
|
- add: NOTCH
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -2.82735 ]
|
||||||
|
|
||||||
|
# mechanism: top plate
|
||||||
|
- add: csg
|
||||||
|
operation: difference
|
||||||
|
material: GOLD
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -1 ]
|
||||||
|
left:
|
||||||
|
type: cylinder
|
||||||
|
min: -1.51
|
||||||
|
max: -1.5
|
||||||
|
closed: true
|
||||||
|
right:
|
||||||
|
type: group
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -1.52
|
||||||
|
max: -1.49
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.1, 1, 0.1 ]
|
||||||
|
- add: csg
|
||||||
|
operation: difference
|
||||||
|
left:
|
||||||
|
type: cylinder
|
||||||
|
min: -1.52
|
||||||
|
max: -1.49
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.75, 1, 0.75 ]
|
||||||
|
right:
|
||||||
|
type: cube
|
||||||
|
transform:
|
||||||
|
- [ scale, 1, 0.1, 0.2 ]
|
||||||
|
- [ translate, 0, -1.5, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.5, 0.5, 0.5 ]
|
||||||
|
- [ translate, 0.4, -1.45, -0.4 ]
|
||||||
|
|
||||||
|
# mechanism: gear
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 0.8 ]
|
||||||
|
- [ scale, 0.4, 0.4, 0.4 ]
|
||||||
|
- [ translate, -0.4, -1.45, 0.2 ]
|
||||||
|
|
||||||
|
# sun
|
||||||
|
- add: group
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
shadow: false
|
||||||
|
material:
|
||||||
|
color: [1, 1, 0]
|
||||||
|
ambient: 0.1
|
||||||
|
diffuse: 0.6
|
||||||
|
specular: 0 # count on the skybox reflection being the specular highlight
|
||||||
|
reflective: 0.2
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -4
|
||||||
|
max: -0.5
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
|
||||||
|
# base
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ translate, 0, -4, 0 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: checkers
|
||||||
|
width: 16
|
||||||
|
height: 8
|
||||||
|
colors:
|
||||||
|
- [ 0, 0, 0 ]
|
||||||
|
- [ 0.5, 0.5, 0.5 ]
|
||||||
|
diffuse: 0.6
|
||||||
|
specular: 0 # count on the skybox reflection being the specular highlight
|
||||||
|
ambient: 0.1
|
||||||
|
reflective: 0.2
|
||||||
|
|
||||||
|
# table
|
||||||
|
- add: cube
|
||||||
|
transform:
|
||||||
|
- [ scale, 5, 0.1, 5 ]
|
||||||
|
- [ translate, 0, -4, 0 ]
|
||||||
|
material:
|
||||||
|
diffuse: 0.9
|
||||||
|
ambient: 0.1
|
||||||
|
specular: 0
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: planar
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/wood.ppm
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.5, 0.5, 0.5 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between top & mercury
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -0.4 ]
|
||||||
|
- [ scale, 0.9, 0.9, 0.9 ]
|
||||||
|
- [ translate, 0, -1.75, 0 ]
|
||||||
|
|
||||||
|
# mercury
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 2, 0, 0 ]
|
||||||
|
- [ rotate-y, 0.7 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.25, 0.25, 0.25 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/mercury-small.ppm
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 2
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between mercury & venus
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, 1.3 ]
|
||||||
|
- [ translate, 0, -2.05, 0 ]
|
||||||
|
|
||||||
|
# venus
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 3, 0, 0 ]
|
||||||
|
- [ rotate-y, 0.3 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.25, 0.25, 0.25 ]
|
||||||
|
material:
|
||||||
|
color: [ 1, 1, 0.8 ]
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.1
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.1, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 3
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.1, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between venus & earth
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.9, 0.9, 0.9 ]
|
||||||
|
- [ rotate-y, -2.2 ]
|
||||||
|
- [ translate, 0, -2.15, 0 ]
|
||||||
|
|
||||||
|
# earth
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 4, 0, 0 ]
|
||||||
|
- [ rotate-y, 2 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.25, 0.25, 0.25 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/earthmap-small.ppm
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.2
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.2, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 4
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.2, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between earth & mars
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.8, 0.8, 0.8 ]
|
||||||
|
- [ rotate-y, 1.7 ]
|
||||||
|
- [ translate, 0, -2.25, 0 ]
|
||||||
|
|
||||||
|
# mars
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 5, 0, 0 ]
|
||||||
|
- [ rotate-y, -2 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.25, 0.25, 0.25 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/mars-small.ppm
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.3
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.3, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 5
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.3, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between mars & jupiter
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -0.9 ]
|
||||||
|
- [ translate, 0, -2.35, 0 ]
|
||||||
|
|
||||||
|
# jupiter
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 6.5, 0, 0 ]
|
||||||
|
- [ rotate-y, -0.75 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.67, 0.67, 0.67 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/jupitermap-small.ppm
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.4
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.4, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 6.5
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.4, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between jupiter & saturn
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.95, 0.95, 0.95 ]
|
||||||
|
- [ rotate-y, -1.1 ]
|
||||||
|
- [ translate, 0, -2.45, 0 ]
|
||||||
|
|
||||||
|
# saturn
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 8, 0, 0 ]
|
||||||
|
- [ rotate-y, -2.5 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.5, 0.5, 0.5 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/saturnmap-small.ppm
|
||||||
|
# rings
|
||||||
|
- add: csg
|
||||||
|
operation: difference
|
||||||
|
transform:
|
||||||
|
- [ rotate-z, 0.2 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: rings
|
||||||
|
colors:
|
||||||
|
- [ 1, 1, 0.5 ]
|
||||||
|
- [ 1, 1, 0 ]
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.05, 1, 0.05 ]
|
||||||
|
left:
|
||||||
|
type: cylinder
|
||||||
|
min: -0.01
|
||||||
|
max: 0.01
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 1.2, 1, 1.2 ]
|
||||||
|
right:
|
||||||
|
type: cylinder
|
||||||
|
min: -0.02
|
||||||
|
max: 0.02
|
||||||
|
closed: true
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.75, 1, 0.75 ]
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.5
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.5, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 8
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.5, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between saturn & uranus
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.9, 0.9, 0.9 ]
|
||||||
|
- [ rotate-y, 1 ]
|
||||||
|
- [ translate, 0, -2.55, 0 ]
|
||||||
|
|
||||||
|
# uranus
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 9, 0, 0 ]
|
||||||
|
- [ rotate-y, -3 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.4, 0.4, 0.4 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/uranusmap-small.ppm
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.6
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.6, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 9
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.6, 0 ]
|
||||||
|
|
||||||
|
# mechanism: gear-plate between uranus & neptune
|
||||||
|
- add: GEAR
|
||||||
|
material: SILVER
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -1 ]
|
||||||
|
- [ translate, 0, -2.65, 0 ]
|
||||||
|
|
||||||
|
# neptune
|
||||||
|
- add: group
|
||||||
|
transform:
|
||||||
|
- [ translate, 10, 0, 0 ]
|
||||||
|
- [ rotate-y, -1.25 ]
|
||||||
|
children:
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.4, 0.4, 0.4 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/neptunemap-small.ppm
|
||||||
|
- add: group
|
||||||
|
material: GOLD
|
||||||
|
children:
|
||||||
|
- add: cylinder
|
||||||
|
min: -2.7
|
||||||
|
max: 0
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 0.025, 0.025 ]
|
||||||
|
- [ translate, 0, -2.7, 0 ]
|
||||||
|
- add: cylinder
|
||||||
|
min: 0
|
||||||
|
max: 10
|
||||||
|
transform:
|
||||||
|
- [ scale, 0.025, 1, 0.025 ]
|
||||||
|
- [ rotate-z, 1.5708 ]
|
||||||
|
- [ translate, 0, -2.7, 0 ]
|
||||||
|
|
||||||
|
# outer sphere as the surrounding environment
|
||||||
|
- add: sphere
|
||||||
|
transform:
|
||||||
|
- [ scale, 1000, 1000, 1000 ]
|
||||||
|
material:
|
||||||
|
pattern:
|
||||||
|
type: map
|
||||||
|
mapping: spherical
|
||||||
|
uv_pattern:
|
||||||
|
type: image
|
||||||
|
file: res/artist_workshop.ppm
|
||||||
|
transform:
|
||||||
|
- [ rotate-y, -2.7 ]
|
||||||
|
diffuse: 0
|
||||||
|
specular: 0
|
||||||
|
ambient: 1
|
||||||
@@ -12,7 +12,7 @@ file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SO
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/uvpattern/*.h)
|
${CMAKE_CURRENT_SOURCE_DIR}/uvpattern/*.h)
|
||||||
|
|
||||||
file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp
|
file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp)
|
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/worldoptimiser/*.cpp)
|
||||||
|
|
||||||
target_include_directories(rayonnement PUBLIC include pattern)
|
target_include_directories(rayonnement PUBLIC include pattern)
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,11 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <renderstat.h>
|
#include <renderstat.h>
|
||||||
|
|
||||||
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize), horizontalSize(hsize), fieldOfView(fov)
|
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize),
|
||||||
|
horizontalSize(hsize), fieldOfView(fov), focalDistance(1), apertureSize(0), rayCount(1)
|
||||||
{
|
{
|
||||||
double aspectRatio = (double)hsize / (double)vsize;
|
double aspectRatio = (double)hsize / (double)vsize;
|
||||||
double halfView = tan(fov / 2.0);
|
double halfView = tan(fov / 2.0) * this->focalDistance;
|
||||||
|
|
||||||
if (aspectRatio >= 1)
|
if (aspectRatio >= 1)
|
||||||
{
|
{
|
||||||
@@ -42,7 +43,7 @@ void Camera::setTransform(Matrix transform)
|
|||||||
this->inverseTransform = transform.inverse();
|
this->inverseTransform = transform.inverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
|
Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY, double horzOffset, double vertOffset)
|
||||||
{
|
{
|
||||||
double xOffset = ((double)pixelX + 0.5) * this->pixelSize;
|
double xOffset = ((double)pixelX + 0.5) * this->pixelSize;
|
||||||
double yOffset = ((double)pixelY + 0.5) * this->pixelSize;
|
double yOffset = ((double)pixelY + 0.5) * this->pixelSize;
|
||||||
@@ -50,10 +51,11 @@ Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
|
|||||||
double worldX = this->halfWidth - xOffset;
|
double worldX = this->halfWidth - xOffset;
|
||||||
double worldY = this->halfHeight - yOffset;
|
double worldY = this->halfHeight - yOffset;
|
||||||
|
|
||||||
Tuple pixel = this->inverseTransform * Point(worldX, worldY, -1);
|
Tuple pixel = this->inverseTransform * Point(worldX, worldY, -this->focalDistance);
|
||||||
Tuple origin = this->inverseTransform * Point(0, 0, 0);
|
Tuple origin = this->inverseTransform * Point(horzOffset, vertOffset, 0);
|
||||||
Tuple direction = (pixel - origin).normalise();
|
Tuple direction = (pixel - origin).normalise();
|
||||||
|
|
||||||
|
stats.addCastedRay();
|
||||||
return Ray(origin, direction);
|
return Ray(origin, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,16 +64,32 @@ Canvas Camera::render(World world, uint32_t depth)
|
|||||||
uint32_t x, y;
|
uint32_t x, y;
|
||||||
Canvas image = Canvas(this->horizontalSize, this->verticalSize);
|
Canvas image = Canvas(this->horizontalSize, this->verticalSize);
|
||||||
|
|
||||||
#pragma omp parallel private(x, y) shared(image, stats)
|
#pragma omp parallel default(shared) private(x, y) shared(image, stats)
|
||||||
{
|
{
|
||||||
#pragma omp for
|
#pragma omp for schedule(dynamic, 5)
|
||||||
for (y = 0 ; y < this->verticalSize ; y++)
|
for (y = 0 ; y < this->verticalSize ; y++)
|
||||||
{
|
{
|
||||||
for (x = 0 ; x < this->horizontalSize ; x++)
|
for (x = 0 ; x < this->horizontalSize ; x++)
|
||||||
{
|
{
|
||||||
Ray r = this->rayForPixel(x, y);
|
Tuple colour;
|
||||||
Tuple colour = world.colourAt(r, depth);
|
if (this->apertureSize > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0 ; i < this->rayCount ; i++)
|
||||||
|
{
|
||||||
|
double horz = frandclip(-this->apertureSize/2, this->apertureSize / 2);
|
||||||
|
double vert = frandclip(-this->apertureSize/2, this->apertureSize / 2);
|
||||||
|
Ray r = this->rayForPixel(x, y, horz, vert);
|
||||||
|
colour = colour + world.colourAt(r, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
colour = colour / this->rayCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Ray r = this->rayForPixel(x, y);
|
||||||
|
colour = world.colourAt(r, depth);
|
||||||
|
}
|
||||||
stats.addPixel();
|
stats.addPixel();
|
||||||
image.putPixel(x, y, colour);
|
image.putPixel(x, y, colour);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,12 +132,12 @@ Canvas::~Canvas()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::putPixel(uint32_t x, uint32_t y, Tuple colour)
|
void Canvas::putPixel(uint32_t x, uint32_t y, Tuple c)
|
||||||
{
|
{
|
||||||
uint32_t offset = y * this->stride + x * BytePP;
|
uint32_t offset = y * this->stride + x * BytePP;
|
||||||
this->bitmap[offset + 0] = MAX(MIN(colour.x * 255, 255), 0);
|
this->bitmap[offset + 0] = MAX(MIN(c.x * 255, 255), 0);
|
||||||
this->bitmap[offset + 1] = MAX(MIN(colour.y * 255, 255), 0);
|
this->bitmap[offset + 1] = MAX(MIN(c.y * 255, 255), 0);
|
||||||
this->bitmap[offset + 2] = MAX(MIN(colour.z * 255, 255), 0);
|
this->bitmap[offset + 2] = MAX(MIN(c.z * 255, 255), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Colour Canvas::getPixel(uint32_t x, uint32_t y)
|
Colour Canvas::getPixel(uint32_t x, uint32_t y)
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ private:
|
|||||||
double halfWidth;
|
double halfWidth;
|
||||||
double halfHeight;
|
double halfHeight;
|
||||||
public:
|
public:
|
||||||
|
double focalDistance;
|
||||||
|
double apertureSize;
|
||||||
|
uint32_t rayCount;
|
||||||
|
|
||||||
uint32_t verticalSize;
|
uint32_t verticalSize;
|
||||||
uint32_t horizontalSize;
|
uint32_t horizontalSize;
|
||||||
double fieldOfView;
|
double fieldOfView;
|
||||||
@@ -30,8 +34,13 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Camera(uint32_t hsize, uint32_t vsize, double fov);
|
Camera(uint32_t hsize, uint32_t vsize, double fov);
|
||||||
|
void setFocal(double focal, double aperture, uint32_t rayCount) {
|
||||||
|
this->focalDistance = focal;
|
||||||
|
this->apertureSize = aperture;
|
||||||
|
this->rayCount = rayCount;
|
||||||
|
}
|
||||||
void setTransform(Matrix transform);
|
void setTransform(Matrix transform);
|
||||||
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY);
|
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY, double horzOffset = 0, double vertOffset = 0);
|
||||||
Canvas render(World w, uint32_t depth = 5);
|
Canvas render(World w, uint32_t depth = 5);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
class Cone : public Shape {
|
class Cone : public Shape {
|
||||||
protected:
|
protected:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
|
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public:
|
|||||||
double minCap;
|
double minCap;
|
||||||
double maxCap;
|
double maxCap;
|
||||||
|
|
||||||
Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CONE) { stats.addCone(); };
|
Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(Shape::CONE) { stats.addCone(); };
|
||||||
BoundingBox getLocalBounds();
|
BoundingBox getLocalBounds();
|
||||||
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
|
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
|
||||||
|
|
||||||
|
|||||||
56
source/include/csg.h
Normal file
56
source/include/csg.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* CSG header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_CSG_H
|
||||||
|
#define DORAYME_CSG_H
|
||||||
|
|
||||||
|
#include <shape.h>
|
||||||
|
|
||||||
|
class CSG : public Shape
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum OperationType
|
||||||
|
{
|
||||||
|
UNION,
|
||||||
|
DIFFERENCE,
|
||||||
|
INTERSECTION
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Shape *left;
|
||||||
|
Shape *right;
|
||||||
|
OperationType operation;
|
||||||
|
BoundingBox bounds;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
BoundingBox getLocalBounds();
|
||||||
|
|
||||||
|
bool intersectionAllowed(bool leftHit, bool inLeft, bool inRight);
|
||||||
|
|
||||||
|
void filterIntersections(Intersect &xs, Intersect &ret);
|
||||||
|
|
||||||
|
void updateBoundingBox();
|
||||||
|
BoundingBox getBounds();
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSG(OperationType operation, Shape *left, Shape *right);
|
||||||
|
|
||||||
|
void intersect(Ray &r, Intersect &xs);
|
||||||
|
|
||||||
|
bool includes(Shape *b);
|
||||||
|
|
||||||
|
void updateTransform();
|
||||||
|
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
void dumpMe(FILE *fp);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_CSG_H */
|
||||||
@@ -19,12 +19,12 @@ class Cube : public Shape {
|
|||||||
protected:
|
protected:
|
||||||
void checkAxis(double axeOrigin, double axeDirection, double *axeMin, double *axeMax);
|
void checkAxis(double axeOrigin, double axeDirection, double *axeMin, double *axeMax);
|
||||||
|
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
|
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Cube() : Shape(SHAPE_CUBE) { stats.addCube(); };
|
Cube() : Shape(Shape::CUBE) { stats.addCube(); };
|
||||||
|
|
||||||
void dumpMe(FILE *fp);
|
void dumpMe(FILE *fp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
class Cylinder : public Shape {
|
class Cylinder : public Shape {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
|
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
double minCap;
|
double minCap;
|
||||||
double maxCap;
|
double maxCap;
|
||||||
|
|
||||||
Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CYLINDER) { stats.addCylinder(); };
|
Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(Shape::CYLINDER) { stats.addCylinder(); };
|
||||||
|
|
||||||
BoundingBox getLocalBounds();
|
BoundingBox getLocalBounds();
|
||||||
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
|
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ private:
|
|||||||
Shape* *unboxableObjectList;
|
Shape* *unboxableObjectList;
|
||||||
uint32_t unboxableObjectCount;
|
uint32_t unboxableObjectCount;
|
||||||
|
|
||||||
|
char name[32 + 1];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
BoundingBox bounds;
|
BoundingBox bounds;
|
||||||
@@ -35,16 +37,31 @@ public:
|
|||||||
bool isEmpty();
|
bool isEmpty();
|
||||||
|
|
||||||
void addObject(Shape *s);
|
void addObject(Shape *s);
|
||||||
Shape *operator[](const int p) { return this->objectList[p]; }
|
void removeObject(Shape *s);
|
||||||
|
|
||||||
Intersect intersect(Ray r);
|
Shape *operator[](const int p) { return this->getObject(p); };
|
||||||
|
Shape *getObject(const int p) { return this->objectList[p]; };
|
||||||
|
Shape *getUnboxable(const int p) { return this->unboxableObjectList[p]; };
|
||||||
|
|
||||||
|
void intersect(Ray &r, Intersect &xs);
|
||||||
BoundingBox getLocalBounds();
|
BoundingBox getLocalBounds();
|
||||||
BoundingBox getBounds();
|
BoundingBox getBounds();
|
||||||
|
|
||||||
void updateBoundingBox();
|
void updateBoundingBox();
|
||||||
void updateTransform();
|
void updateTransform();
|
||||||
|
|
||||||
Group();
|
bool includes(Shape *b);
|
||||||
|
|
||||||
|
uint32_t getObjectCount() { return this->objectCount; };
|
||||||
|
uint32_t getUnboxableCount() { return this->unboxableObjectCount; };
|
||||||
|
|
||||||
|
Group(const char *name = nullptr);
|
||||||
|
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
void setBounds(BoundingBox &bb) { this->bounds | bb; };
|
||||||
|
|
||||||
|
const char *getName() { return this->name; };
|
||||||
|
|
||||||
void dumpMe(FILE * fp);
|
void dumpMe(FILE * fp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,31 +25,7 @@ struct Computation
|
|||||||
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
|
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
|
||||||
overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2), material(objMat) { };
|
overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2), material(objMat) { };
|
||||||
|
|
||||||
double schlick()
|
double schlick();
|
||||||
{
|
|
||||||
/* Find the cos of the angle betzeen the eye and normal vector */
|
|
||||||
double cos = this->eyeVector.dot(this->normalVector);
|
|
||||||
double r0;
|
|
||||||
/* Total internal reflection can only occur when n1 > n2 */
|
|
||||||
if (this->n1 > this->n2)
|
|
||||||
{
|
|
||||||
double n, sin2_t;
|
|
||||||
n = this->n1 / this->n2;
|
|
||||||
sin2_t = (n * n) * (1.0 - (cos * cos));
|
|
||||||
if (sin2_t > 1.0)
|
|
||||||
{
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
/* Compute the cos of theta */
|
|
||||||
cos = sqrt(1.0 - sin2_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
r0 = ((this->n1 - this->n2) / (this->n1 + this->n2));
|
|
||||||
r0 = r0 * r0;
|
|
||||||
|
|
||||||
return r0 + (1 - r0) * ((1 - cos)*(1 - cos)*(1 - cos)*(1 - cos)*(1 - cos));
|
|
||||||
};
|
|
||||||
|
|
||||||
Shape *object;
|
Shape *object;
|
||||||
double t;
|
double t;
|
||||||
@@ -73,7 +49,6 @@ class Intersection
|
|||||||
public:
|
public:
|
||||||
double t;
|
double t;
|
||||||
Shape *object;
|
Shape *object;
|
||||||
|
|
||||||
double u, v;
|
double u, v;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ double min3(double a, double b, double c);
|
|||||||
double max3(double a, double b, double c);
|
double max3(double a, double b, double c);
|
||||||
|
|
||||||
double frand();
|
double frand();
|
||||||
|
double frandclip(double min, double max);
|
||||||
|
|
||||||
static double modulo(double a, double b)
|
static double modulo(double a, double b)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
#undef minor
|
#undef minor
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FastGet4(_x, _y) (this->data[4 * (_x) + (_y)])
|
||||||
|
|
||||||
class Matrix
|
class Matrix
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -46,7 +48,12 @@ public:
|
|||||||
bool isInvertible() { return this->determinant() != 0; }
|
bool isInvertible() { return this->determinant() != 0; }
|
||||||
|
|
||||||
Matrix operator*(const Matrix &b) const;
|
Matrix operator*(const Matrix &b) const;
|
||||||
Tuple operator*(const Tuple &b) const;
|
Tuple operator*(const Tuple &b) const {
|
||||||
|
return Tuple(b.x * FastGet4(0, 0) + b.y * FastGet4(0, 1) + b.z * FastGet4(0, 2) + b.w * FastGet4(0, 3),
|
||||||
|
b.x * FastGet4(1, 0) + b.y * FastGet4(1, 1) + b.z * FastGet4(1, 2) + b.w * FastGet4(1, 3),
|
||||||
|
b.x * FastGet4(2, 0) + b.y * FastGet4(2, 1) + b.z * FastGet4(2, 2) + b.w * FastGet4(2, 3),
|
||||||
|
b.x * FastGet4(3, 0) + b.y * FastGet4(3, 1) + b.z * FastGet4(3, 2) + b.w * FastGet4(3, 3));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Matrix4: public Matrix
|
class Matrix4: public Matrix
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
class OBJFile : public Shape
|
class OBJFile : public Shape
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
uint32_t allocatedFaceGroupCount;
|
Group *baseGroup;
|
||||||
Group* *faceGroupList;
|
|
||||||
uint32_t faceGroupCount;
|
Group *currentGroup;
|
||||||
|
|
||||||
uint32_t allocatedVertexCount;
|
uint32_t allocatedVertexCount;
|
||||||
Point* *vertexList;
|
Point* *vertexList;
|
||||||
@@ -31,7 +31,7 @@ private:
|
|||||||
uint32_t vertexNormalCount;
|
uint32_t vertexNormalCount;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -51,20 +51,30 @@ public:
|
|||||||
OBJFile();
|
OBJFile();
|
||||||
OBJFile(const char *filepath);
|
OBJFile(const char *filepath);
|
||||||
|
|
||||||
|
~OBJFile();
|
||||||
|
|
||||||
int parseOBJFile(const char *content);
|
int parseOBJFile(const char *content);
|
||||||
|
|
||||||
/* OBJ file expect the first vertice to be 1 and not 0 */
|
/* OBJ file expect the first vertice to be 1 and not 0 */
|
||||||
Point vertices(uint32_t i) { return *this->vertexList[i - 1]; };
|
Point vertices(uint32_t i) { return *this->vertexList[i - 1]; };
|
||||||
Vector verticesNormal(uint32_t i) { return *this->vertexNormalList[i - 1]; };
|
Vector verticesNormal(uint32_t i) { return *this->vertexNormalList[i - 1]; };
|
||||||
Group *groups(uint32_t i) { return this->faceGroupList[i]; };
|
Group *groups(const char *groupName);
|
||||||
Intersect intersect(Ray r);
|
void intersect(Ray &r, Intersect &xs);
|
||||||
BoundingBox getLocalBounds();
|
BoundingBox getLocalBounds();
|
||||||
BoundingBox getBounds();
|
BoundingBox getBounds();
|
||||||
|
|
||||||
|
Shape *getBaseGroup() { return this->baseGroup; };
|
||||||
|
|
||||||
|
bool includes(Shape *b);
|
||||||
|
|
||||||
void updateBoundingBox();
|
void updateBoundingBox();
|
||||||
void updateTransform();
|
void updateTransform();
|
||||||
|
|
||||||
|
void lock();
|
||||||
|
|
||||||
void dumpMe(FILE * fp);
|
void dumpMe(FILE * fp);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define OBJ_DEFAULT_GROUP "_DefaultObjGroup_"
|
||||||
|
|
||||||
#endif /* DORAYME_OBJFILE_H */
|
#endif /* DORAYME_OBJFILE_H */
|
||||||
|
|||||||
@@ -14,11 +14,11 @@
|
|||||||
class Plane : public Shape
|
class Plane : public Shape
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Plane() : Shape(SHAPE_PLANE) { stats.addPlane(); };
|
Plane() : Shape(Shape::PLANE) { stats.addPlane(); };
|
||||||
BoundingBox getLocalBounds();
|
BoundingBox getLocalBounds();
|
||||||
bool haveFiniteBounds() { return false; };
|
bool haveFiniteBounds() { return false; };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,9 +24,11 @@ private:
|
|||||||
uint64_t triangleCount; /* Total number of triangle */
|
uint64_t triangleCount; /* Total number of triangle */
|
||||||
uint64_t smoothTriangleCount; /* Total number of smooth triangle */
|
uint64_t smoothTriangleCount; /* Total number of smooth triangle */
|
||||||
uint64_t objfileCount; /* Total number of OBJ File */
|
uint64_t objfileCount; /* Total number of OBJ File */
|
||||||
|
uint64_t csgCount; /* Total number of CSG */
|
||||||
|
|
||||||
uint64_t pixelCount; /* Total number of rendered pixels */
|
uint64_t pixelCount; /* Total number of rendered pixels */
|
||||||
uint64_t rayCount; /* Total number of rays */
|
uint64_t rayCount; /* Total number of rays object created */
|
||||||
|
uint64_t rayCasted; /* Total number of rays actually casted */
|
||||||
uint64_t lightRayEmitedCount; /* Total number of ray launched for light tests */
|
uint64_t lightRayEmitedCount; /* Total number of ray launched for light tests */
|
||||||
uint64_t reflectionRayCount; /* Total number of reflection ray launched */
|
uint64_t reflectionRayCount; /* Total number of reflection ray launched */
|
||||||
uint64_t refractedRayCount; /* Total number of refracted ray launched */
|
uint64_t refractedRayCount; /* Total number of refracted ray launched */
|
||||||
@@ -42,7 +44,8 @@ public:
|
|||||||
RenderStats() : coneCount(0), cylinderCount(0), cubeCount(0), groupCount(0), lightCount(0), planeCount(0), sphereCount(0), triangleCount(0),
|
RenderStats() : coneCount(0), cylinderCount(0), cubeCount(0), groupCount(0), lightCount(0), planeCount(0), sphereCount(0), triangleCount(0),
|
||||||
pixelCount(0), rayCount(0), lightRayEmitedCount(0), reflectionRayCount(0), refractedRayCount(0),
|
pixelCount(0), rayCount(0), lightRayEmitedCount(0), reflectionRayCount(0), refractedRayCount(0),
|
||||||
intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0), smoothTriangleCount(0),
|
intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0), smoothTriangleCount(0),
|
||||||
discardedIntersectCount(0), maxDepthAttained(UINT64_MAX), maxIntersectOnARay(0), objfileCount(0) {};
|
discardedIntersectCount(0), maxDepthAttained(UINT64_MAX), maxIntersectOnARay(0), objfileCount(0),
|
||||||
|
csgCount(0), rayCasted(0) {};
|
||||||
#ifdef RENDER_STATS
|
#ifdef RENDER_STATS
|
||||||
void addCone();
|
void addCone();
|
||||||
void addCylinder();
|
void addCylinder();
|
||||||
@@ -51,12 +54,14 @@ public:
|
|||||||
void addLight();
|
void addLight();
|
||||||
void addPlane();
|
void addPlane();
|
||||||
void addSphere();
|
void addSphere();
|
||||||
|
void addCsg();
|
||||||
void addOBJFile();
|
void addOBJFile();
|
||||||
void addTriangle();
|
void addTriangle();
|
||||||
void addSmoothTriangle();
|
void addSmoothTriangle();
|
||||||
void printStats();
|
void printStats();
|
||||||
void addPixel();
|
void addPixel();
|
||||||
void addRay();
|
void addRay();
|
||||||
|
void addCastedRay();
|
||||||
void addLightRay();
|
void addLightRay();
|
||||||
void addReflectRay();
|
void addReflectRay();
|
||||||
void addRefractRay();
|
void addRefractRay();
|
||||||
@@ -80,6 +85,7 @@ public:
|
|||||||
static void printStats() {};
|
static void printStats() {};
|
||||||
static void addPixel() {};
|
static void addPixel() {};
|
||||||
static void addRay() {};
|
static void addRay() {};
|
||||||
|
static void addCastedRay() {};
|
||||||
static void addLightRay() {};
|
static void addLightRay() {};
|
||||||
static void addReflectRay() {};
|
static void addReflectRay() {};
|
||||||
static void addRefractRay() {};
|
static void addRefractRay() {};
|
||||||
@@ -90,7 +96,8 @@ public:
|
|||||||
static void addMalloc() {};
|
static void addMalloc() {};
|
||||||
static void addRealloc() {};
|
static void addRealloc() {};
|
||||||
static void setMaxIntersect(uint32_t count) {};
|
static void setMaxIntersect(uint32_t count) {};
|
||||||
static void void RenderStats::addOBJFile() {};
|
static void addOBJFile() {};
|
||||||
|
static void addCsg() {};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,31 +19,37 @@ class Shape;
|
|||||||
#include <material.h>
|
#include <material.h>
|
||||||
#include <boundingbox.h>
|
#include <boundingbox.h>
|
||||||
|
|
||||||
enum ShapeType
|
|
||||||
{
|
|
||||||
SHAPE_NONE,
|
|
||||||
SHAPE_SPHERE,
|
|
||||||
SHAPE_PLANE,
|
|
||||||
SHAPE_CUBE,
|
|
||||||
SHAPE_CYLINDER,
|
|
||||||
SHAPE_CONE,
|
|
||||||
SHAPE_GROUP,
|
|
||||||
SHAPE_TRIANGLE,
|
|
||||||
SHAPE_OBJFILE,
|
|
||||||
SHAPE_SMOOTHTRIANGLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Base class for all object that can be presented in the world */
|
/* Base class for all object that can be presented in the world */
|
||||||
class Shape
|
class Shape
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
enum ShapeType
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
SPHERE,
|
||||||
|
PLANE,
|
||||||
|
CUBE,
|
||||||
|
CYLINDER,
|
||||||
|
CONE,
|
||||||
|
GROUP,
|
||||||
|
TRIANGLE,
|
||||||
|
OBJFILE,
|
||||||
|
SMOOTHTRIANGLE,
|
||||||
|
CSG,
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ShapeType type;
|
ShapeType type;
|
||||||
Matrix localTransformMatrix;
|
Matrix localTransformMatrix;
|
||||||
|
bool locked;
|
||||||
|
uint64_t objectId;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Intersect localIntersect(Ray r) = 0;
|
virtual void localIntersect(Ray r, Intersect &xs) = 0;
|
||||||
virtual Tuple localNormalAt(Tuple point, Intersection *hit) = 0;
|
virtual Tuple localNormalAt(Tuple point, Intersection *hit) = 0;
|
||||||
|
|
||||||
|
static uint64_t newObjectId();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Matrix transformMatrix;
|
Matrix transformMatrix;
|
||||||
Matrix inverseTransform;
|
Matrix inverseTransform;
|
||||||
@@ -55,12 +61,16 @@ public:
|
|||||||
bool materialSet;
|
bool materialSet;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Shape(ShapeType = SHAPE_NONE);
|
Shape(ShapeType = Shape::NONE);
|
||||||
|
|
||||||
virtual Intersect intersect(Ray r);
|
ShapeType getType() { return this->type; };
|
||||||
virtual Intersect intersectOOB(Ray r) { return this->intersect(r); };
|
|
||||||
|
virtual void intersect(Ray &r, Intersect &xs) { this->localIntersect(this->invTransform(r), xs); };
|
||||||
Tuple normalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple normalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
|
uint64_t getObjectId() { return this->objectId; };
|
||||||
|
void setObjectId(uint64_t oid) { this->objectId = oid; };
|
||||||
|
|
||||||
/* Bounding box points are always world value */
|
/* Bounding box points are always world value */
|
||||||
virtual BoundingBox getLocalBounds();
|
virtual BoundingBox getLocalBounds();
|
||||||
virtual BoundingBox getBounds();
|
virtual BoundingBox getBounds();
|
||||||
@@ -68,16 +78,29 @@ public:
|
|||||||
|
|
||||||
virtual void updateTransform();
|
virtual void updateTransform();
|
||||||
|
|
||||||
|
virtual bool includes(Shape *b) { return this == b; };
|
||||||
|
|
||||||
virtual void dumpMe(FILE *fp);
|
virtual void dumpMe(FILE *fp);
|
||||||
|
|
||||||
|
/* When an object is locked, the matrix transformation and bounding box can't be updated. This is
|
||||||
|
* usefull to move object between group without changing the real hierarchy between them.
|
||||||
|
* It will also not change the parent member.
|
||||||
|
* This is supposed to be used only before a render is going to start so we can optimise the
|
||||||
|
* way the object are stored to prefer lots of un-needed intersections.
|
||||||
|
*/
|
||||||
|
virtual void lock() { this->locked = true; };
|
||||||
|
|
||||||
Tuple worldToObject(Tuple point) { return this->inverseTransform * point; };
|
Tuple worldToObject(Tuple point) { return this->inverseTransform * point; };
|
||||||
Tuple objectToWorld(Tuple point) { return this->transformMatrix * point; };
|
Tuple objectToWorld(Tuple point) { return this->transformMatrix * point; };
|
||||||
Tuple normalToWorld(Tuple normalVector);
|
Tuple normalToWorld(Tuple normalVector);
|
||||||
|
|
||||||
|
void setParent(Shape *parent) { if (!this->locked) { this->parent = parent; };};
|
||||||
|
|
||||||
void setTransform(Matrix transform);
|
void setTransform(Matrix transform);
|
||||||
void setMaterial(Material material) { this->material = material; };
|
void setMaterial(Material material) { this->material = material; this->materialSet = true; };
|
||||||
Ray transform(Ray r) { return Ray(this->transformMatrix * r.origin, this->transformMatrix * r.direction); };
|
Material *getMaterial();
|
||||||
Ray invTransform(Ray r) { return Ray(this->inverseTransform * r.origin, this->inverseTransform * r.direction); };
|
Ray transform(Ray &r) { return Ray(this->transformMatrix * r.origin, this->transformMatrix * r.direction); };
|
||||||
|
Ray invTransform(Ray &r) { return Ray(this->inverseTransform * r.origin, this->inverseTransform * r.direction); };
|
||||||
|
|
||||||
bool operator==(const Shape &b) const { return this->material == b.material &&
|
bool operator==(const Shape &b) const { return this->material == b.material &&
|
||||||
this->type == b.type &&
|
this->type == b.type &&
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3);
|
SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3);
|
||||||
|
|
||||||
|
void dumpMe(FILE *fp);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DORAYME_SMOOTHTRIANGLE_H */
|
#endif /* DORAYME_SMOOTHTRIANGLE_H */
|
||||||
|
|||||||
@@ -18,11 +18,11 @@
|
|||||||
class Sphere : public Shape
|
class Sphere : public Shape
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Sphere() : Shape(SHAPE_SPHERE) { stats.addSphere(); };
|
Sphere() : Shape(Shape::SPHERE) { stats.addSphere(); };
|
||||||
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
|
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
class TestShape : public Shape
|
class TestShape : public Shape
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
class Triangle : public Shape
|
class Triangle : public Shape
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
Intersect localIntersect(Ray r);
|
void localIntersect(Ray r, Intersect &xs);
|
||||||
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -39,16 +39,23 @@ public:
|
|||||||
this->z * b, this->w * b); };
|
this->z * b, this->w * b); };
|
||||||
Tuple operator/(const double &b) const { return Tuple(this->x / b, this->y / b,
|
Tuple operator/(const double &b) const { return Tuple(this->x / b, this->y / b,
|
||||||
this->z / b, this->w / b); };
|
this->z / b, this->w / b); };
|
||||||
|
|
||||||
void fixPoint();
|
|
||||||
void fixVector();
|
|
||||||
bool isRepresentable();
|
bool isRepresentable();
|
||||||
|
|
||||||
void set(double nX, double nY, double nZ) { this->x = nX; this->y = nY; this->z = nZ; };
|
void set(double nX, double nY, double nZ) { this->x = nX; this->y = nY; this->z = nZ; };
|
||||||
double magnitude();
|
double magnitude();
|
||||||
Tuple normalise();
|
Tuple normalise();
|
||||||
double dot(const Tuple &b);
|
|
||||||
Tuple cross(const Tuple &b) const;
|
double dot(const Tuple &b) {
|
||||||
|
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple cross(const Tuple &b) const {
|
||||||
|
return Tuple(this->y * b.z - this->z * b.y,
|
||||||
|
this->z * b.x - this->x * b.z,
|
||||||
|
this->x * b.y - this->y * b.x,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
Tuple reflect(const Tuple &normal);
|
Tuple reflect(const Tuple &normal);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include <intersect.h>
|
#include <intersect.h>
|
||||||
#include <ray.h>
|
#include <ray.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <group.h>
|
||||||
|
#include <worldoptimiser.h>
|
||||||
|
|
||||||
#ifdef ENABLE_LUA_SUPPORT
|
#ifdef ENABLE_LUA_SUPPORT
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -25,15 +27,13 @@ extern "C" {
|
|||||||
class World
|
class World
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
uint32_t objectCount;
|
|
||||||
uint32_t lightCount;
|
uint32_t lightCount;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t allocatedObjectCount;
|
|
||||||
uint32_t allocatedLightCount;
|
uint32_t allocatedLightCount;
|
||||||
|
|
||||||
Light* *lightList;
|
Light* *lightList;
|
||||||
Shape* *objectList;
|
|
||||||
|
Group worldGroup;
|
||||||
|
|
||||||
#ifdef ENABLE_LUA_SUPPORT
|
#ifdef ENABLE_LUA_SUPPORT
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
@@ -50,9 +50,12 @@ public:
|
|||||||
bool lightIsIn(Light &l);
|
bool lightIsIn(Light &l);
|
||||||
bool objectIsIn(Shape &s);
|
bool objectIsIn(Shape &s);
|
||||||
|
|
||||||
Shape *getObject(int i) { return this->objectList[i]; };
|
Shape *getObject(int i) { return this->worldGroup[i]; };
|
||||||
Light *getLight(int i) { return this->lightList[i]; };
|
Light *getLight(int i) { return this->lightList[i]; };
|
||||||
|
|
||||||
|
uint32_t getObjectCount() { return this->worldGroup.getObjectCount(); };
|
||||||
|
uint32_t getLightCount() { return this->lightCount; };
|
||||||
|
|
||||||
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
|
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
|
||||||
Tuple colourAt(Ray r, uint32_t depthCount = 4);
|
Tuple colourAt(Ray r, uint32_t depthCount = 4);
|
||||||
bool isShadowed(Tuple point, Tuple lightPosition);
|
bool isShadowed(Tuple point, Tuple lightPosition);
|
||||||
@@ -60,7 +63,9 @@ public:
|
|||||||
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
|
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
|
||||||
Colour refractedColour(Computation comps, uint32_t depthCount = 4);
|
Colour refractedColour(Computation comps, uint32_t depthCount = 4);
|
||||||
|
|
||||||
Intersect intersect(Ray r);
|
void intersect(Ray &r, Intersect &xs);
|
||||||
|
|
||||||
|
void finalise(WorldOptimiser &opt);
|
||||||
|
|
||||||
void dumpMe(FILE *fp);
|
void dumpMe(FILE *fp);
|
||||||
};
|
};
|
||||||
|
|||||||
51
source/include/worldoptimiser.h
Normal file
51
source/include/worldoptimiser.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* World optimiser header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_WORLDOPTIMISER_H
|
||||||
|
#define DORAYME_WORLDOPTIMISER_H
|
||||||
|
|
||||||
|
#include <group.h>
|
||||||
|
|
||||||
|
/* World Optimiser subclasses will created move objects around to try to optimise the raytrace of the world, to
|
||||||
|
* have as least as possible object to intersect per ray.
|
||||||
|
* This class is abstract to we can implement different type and change at runtime or build time
|
||||||
|
*/
|
||||||
|
class WorldOptimiser
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
Group *root;
|
||||||
|
void moveInfiniteObjects(Shape *s = nullptr);
|
||||||
|
void moveAllObjects(Shape *s = nullptr);
|
||||||
|
public:
|
||||||
|
void setRoot(Group *root) { this->root = root; };
|
||||||
|
virtual void run() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NoWorldOptimisation : public WorldOptimiser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void run() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class BVHOptimisation : public WorldOptimiser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void makeTree(Group *leaf, int depth = 0);
|
||||||
|
public:
|
||||||
|
void run();
|
||||||
|
};
|
||||||
|
|
||||||
|
class OctreeOptimisation : public WorldOptimiser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void makeTree(Group *leaf, int depth = 0);
|
||||||
|
public:
|
||||||
|
void run();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_WORLDOPTIMISER_H */
|
||||||
@@ -24,9 +24,17 @@ Intersect::Intersect()
|
|||||||
{
|
{
|
||||||
this->allocated = MIN_ALLOC;
|
this->allocated = MIN_ALLOC;
|
||||||
this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
|
this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
|
||||||
stats.addMalloc();
|
if (this->list != nullptr)
|
||||||
stats.addIntersect();
|
{
|
||||||
this->num = 0;
|
stats.addMalloc();
|
||||||
|
stats.addIntersect();
|
||||||
|
this->num = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ABORT: Allocation error [%s]!\n", __FUNCTION__);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect::~Intersect()
|
Intersect::~Intersect()
|
||||||
@@ -34,12 +42,17 @@ Intersect::~Intersect()
|
|||||||
int i;
|
int i;
|
||||||
for(i = 0; i < this->num; i++)
|
for(i = 0; i < this->num; i++)
|
||||||
{
|
{
|
||||||
delete this->list[i];
|
if (this->list[i] != nullptr)
|
||||||
|
{
|
||||||
|
delete this->list[i];
|
||||||
|
this->list[i] = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Free stuff */
|
/* Free stuff */
|
||||||
if (this->list != nullptr)
|
if (this->list != nullptr)
|
||||||
{
|
{
|
||||||
free(this->list);
|
free(this->list);
|
||||||
|
this->list = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,32 @@
|
|||||||
#include <shape.h>
|
#include <shape.h>
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
|
|
||||||
|
double Computation::schlick()
|
||||||
|
{
|
||||||
|
/* Find the cos of the angle betzeen the eye and normal vector */
|
||||||
|
double cos = this->eyeVector.dot(this->normalVector);
|
||||||
|
double r0;
|
||||||
|
/* Total internal reflection can only occur when n1 > n2 */
|
||||||
|
if (this->n1 > this->n2)
|
||||||
|
{
|
||||||
|
double n, sin2_t;
|
||||||
|
n = this->n1 / this->n2;
|
||||||
|
sin2_t = (n * n) * (1.0 - (cos * cos));
|
||||||
|
if (sin2_t > 1.0)
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
/* Compute the cos of theta */
|
||||||
|
cos = sqrt(1.0 - sin2_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
r0 = ((this->n1 - this->n2) / (this->n1 + this->n2));
|
||||||
|
r0 = r0 * r0;
|
||||||
|
|
||||||
|
return r0 + (1 - r0) * ((1 - cos)*(1 - cos)*(1 - cos)*(1 - cos)*(1 - cos));
|
||||||
|
};
|
||||||
|
|
||||||
Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
||||||
{
|
{
|
||||||
double n1 = 1.0;
|
double n1 = 1.0;
|
||||||
@@ -42,10 +68,10 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
|||||||
Tuple reflectV = r.direction.reflect(normalV);
|
Tuple reflectV = r.direction.reflect(normalV);
|
||||||
|
|
||||||
/* If the hit object is not transparent, there is no need to do that. I think .*/
|
/* If the hit object is not transparent, there is no need to do that. I think .*/
|
||||||
if ((xs != nullptr) && (xs->hit().object->material.transparency > 0))
|
if ((xs != nullptr) && (xs->hit().object->getMaterial()->transparency > 0))
|
||||||
{
|
{
|
||||||
List containers;
|
List containers;
|
||||||
int j, k;
|
int j;
|
||||||
|
|
||||||
for (j = 0 ; j < xs->count() ; j++)
|
for (j = 0 ; j < xs->count() ; j++)
|
||||||
{
|
{
|
||||||
@@ -54,7 +80,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
|||||||
{
|
{
|
||||||
if (!containers.isEmpty())
|
if (!containers.isEmpty())
|
||||||
{
|
{
|
||||||
n1 = containers.last()->material.refractiveIndex;
|
n1 = containers.last()->getMaterial()->refractiveIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +97,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
|||||||
{
|
{
|
||||||
if (!containers.isEmpty())
|
if (!containers.isEmpty())
|
||||||
{
|
{
|
||||||
n2 = containers.last()->material.refractiveIndex;
|
n2 = containers.last()->getMaterial()->refractiveIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End the loop */
|
/* End the loop */
|
||||||
@@ -80,10 +106,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape *s = this->object;
|
Material *m = this->object->getMaterial();
|
||||||
|
|
||||||
/* For now don't get root group material */
|
|
||||||
while((!s->materialSet) && (s->parent != nullptr)) { s = s->parent; }
|
|
||||||
|
|
||||||
return Computation(this->object,
|
return Computation(this->object,
|
||||||
this->t,
|
this->t,
|
||||||
@@ -96,5 +119,5 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
|
|||||||
n1,
|
n1,
|
||||||
n2,
|
n2,
|
||||||
underHitP,
|
underHitP,
|
||||||
&s->material);
|
m);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,4 +68,9 @@ double max3(double a, double b, double c)
|
|||||||
double frand()
|
double frand()
|
||||||
{
|
{
|
||||||
return rand() / ((double) RAND_MAX);
|
return rand() / ((double) RAND_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
double frandclip(double min, double max)
|
||||||
|
{
|
||||||
|
return (frand() * (max - min)) + min;
|
||||||
}
|
}
|
||||||
@@ -101,15 +101,18 @@ Matrix Matrix::operator*(const Matrix &b) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#define FastGet4(_x, _y) (this->data[4 * (_x) + (_y)])
|
||||||
|
|
||||||
/* TODO: Check if we can optimise this function. It is called a lot */
|
/* TODO: Check if we can optimise this function. It is called a lot */
|
||||||
|
/*
|
||||||
Tuple Matrix::operator*(const Tuple &b) const
|
Tuple Matrix::operator*(const Tuple &b) const
|
||||||
{
|
{
|
||||||
return Tuple(b.x * this->get(0, 0) + b.y * this->get(0, 1) + b.z * this->get(0, 2) + b.w * this->get(0, 3),
|
return Tuple(b.x * FastGet4(0, 0) + b.y * FastGet4(0, 1) + b.z * FastGet4(0, 2) + b.w * FastGet4(0, 3),
|
||||||
b.x * this->get(1, 0) + b.y * this->get(1, 1) + b.z * this->get(1, 2) + b.w * this->get(1, 3),
|
b.x * FastGet4(1, 0) + b.y * FastGet4(1, 1) + b.z * FastGet4(1, 2) + b.w * FastGet4(1, 3),
|
||||||
b.x * this->get(2, 0) + b.y * this->get(2, 1) + b.z * this->get(2, 2) + b.w * this->get(2, 3),
|
b.x * FastGet4(2, 0) + b.y * FastGet4(2, 1) + b.z * FastGet4(2, 2) + b.w * FastGet4(2, 3),
|
||||||
b.x * this->get(3, 0) + b.y * this->get(3, 1) + b.z * this->get(3, 2) + b.w * this->get(3, 3));
|
b.x * FastGet4(3, 0) + b.y * FastGet4(3, 1) + b.z * FastGet4(3, 2) + b.w * FastGet4(3, 3));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
Matrix Matrix::identity()
|
Matrix Matrix::identity()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|||||||
@@ -153,18 +153,17 @@ public:
|
|||||||
double u,v;
|
double u,v;
|
||||||
if (this->type == CUBIC_MAP)
|
if (this->type == CUBIC_MAP)
|
||||||
{
|
{
|
||||||
CubeFaces face = this->faceFromPoint(point);
|
CubeFaces face = TextureMap::faceFromPoint(point);
|
||||||
UVPattern *facePat;
|
UVPattern *facePat;
|
||||||
double u, v;
|
|
||||||
switch(face)
|
switch(face)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case CUBE_LEFT: facePat = this->leftPat; this->cubeUBLeft(point, u, v); break;
|
case CUBE_LEFT: facePat = this->leftPat; TextureMap::cubeUBLeft(point, u, v); break;
|
||||||
case CUBE_RIGHT: facePat = this->rightPat; this->cubeUBRight(point, u, v); break;
|
case CUBE_RIGHT: facePat = this->rightPat; TextureMap::cubeUBRight(point, u, v); break;
|
||||||
case CUBE_FRONT: facePat = this->frontPat; this->cubeUBFront(point, u, v); break;
|
case CUBE_FRONT: facePat = this->frontPat; TextureMap::cubeUBFront(point, u, v); break;
|
||||||
case CUBE_BACK: facePat = this->backPat; this->cubeUBBack(point, u, v); break;
|
case CUBE_BACK: facePat = this->backPat; TextureMap::cubeUBBack(point, u, v); break;
|
||||||
case CUBE_UP: facePat = this->upPat; this->cubeUBUp(point, u, v); break;
|
case CUBE_UP: facePat = this->upPat; TextureMap::cubeUBUp(point, u, v); break;
|
||||||
case CUBE_DOWN: facePat = this->downPat; this->cubeUBDown(point, u, v); break;
|
case CUBE_DOWN: facePat = this->downPat; TextureMap::cubeUBDown(point, u, v); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return facePat->uvPatternAt(u, v);
|
return facePat->uvPatternAt(u, v);
|
||||||
|
|||||||
@@ -85,20 +85,32 @@ void RenderStats::addRay()
|
|||||||
this->rayCount++;
|
this->rayCount++;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void RenderStats::addCastedRay()
|
||||||
|
{
|
||||||
|
#pragma omp atomic
|
||||||
|
this->rayCasted++;
|
||||||
|
};
|
||||||
|
|
||||||
void RenderStats::addLightRay()
|
void RenderStats::addLightRay()
|
||||||
{
|
{
|
||||||
|
this->addCastedRay();
|
||||||
|
|
||||||
#pragma omp atomic
|
#pragma omp atomic
|
||||||
this->lightRayEmitedCount++;
|
this->lightRayEmitedCount++;
|
||||||
};
|
};
|
||||||
|
|
||||||
void RenderStats::addReflectRay()
|
void RenderStats::addReflectRay()
|
||||||
{
|
{
|
||||||
|
this->addCastedRay();
|
||||||
|
|
||||||
#pragma omp atomic
|
#pragma omp atomic
|
||||||
this->reflectionRayCount++;
|
this->reflectionRayCount++;
|
||||||
};
|
};
|
||||||
|
|
||||||
void RenderStats::addRefractRay()
|
void RenderStats::addRefractRay()
|
||||||
{
|
{
|
||||||
|
this->addCastedRay();
|
||||||
|
|
||||||
#pragma omp atomic
|
#pragma omp atomic
|
||||||
this->refractedRayCount++;
|
this->refractedRayCount++;
|
||||||
};
|
};
|
||||||
@@ -133,6 +145,13 @@ void RenderStats::addDiscardedIntersect()
|
|||||||
this->discardedIntersectCount++;
|
this->discardedIntersectCount++;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void RenderStats::addCsg()
|
||||||
|
{
|
||||||
|
#pragma omp atomic
|
||||||
|
this->csgCount++;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void RenderStats::setMaxDepth(uint32_t depth)
|
void RenderStats::setMaxDepth(uint32_t depth)
|
||||||
{
|
{
|
||||||
if (this->maxDepthAttained > depth)
|
if (this->maxDepthAttained > depth)
|
||||||
@@ -162,9 +181,11 @@ void RenderStats::printStats()
|
|||||||
printf("Triangles : %lld\n", this->triangleCount);
|
printf("Triangles : %lld\n", this->triangleCount);
|
||||||
printf("Smooth Triangles : %lld\n", this->smoothTriangleCount);
|
printf("Smooth Triangles : %lld\n", this->smoothTriangleCount);
|
||||||
printf("OBJ File : %lld\n", this->objfileCount);
|
printf("OBJ File : %lld\n", this->objfileCount);
|
||||||
|
printf("CSG : %lld\n", this->csgCount);
|
||||||
printf("==================================================\n");
|
printf("==================================================\n");
|
||||||
printf("Pixel rendered : %lld\n", this->pixelCount);
|
printf("Pixel rendered : %lld\n", this->pixelCount);
|
||||||
printf("Ray casted : %lld\n", this->rayCount);
|
printf("Ray created : %lld\n", this->rayCount);
|
||||||
|
printf("Ray casted : %lld\n", this->rayCasted);
|
||||||
printf("Light Ray casted : %lld\n", this->lightRayEmitedCount);
|
printf("Light Ray casted : %lld\n", this->lightRayEmitedCount);
|
||||||
printf("Reflection ray casted : %lld\n", this->reflectionRayCount);
|
printf("Reflection ray casted : %lld\n", this->reflectionRayCount);
|
||||||
printf("Refraction ray casted : %lld\n", this->refractedRayCount);
|
printf("Refraction ray casted : %lld\n", this->refractedRayCount);
|
||||||
@@ -173,7 +194,7 @@ void RenderStats::printStats()
|
|||||||
printf("Malloc called : %lld\n", this->mallocCallCount);
|
printf("Malloc called : %lld\n", this->mallocCallCount);
|
||||||
printf("Realloc called : %lld\n", this->reallocCallCount);
|
printf("Realloc called : %lld\n", this->reallocCallCount);
|
||||||
printf("Bounding box missed : %lld\n", this->discardedIntersectCount);
|
printf("Bounding box missed : %lld\n", this->discardedIntersectCount);
|
||||||
printf("Min depth atteined : %lld\n", this->maxDepthAttained);
|
printf("Min depth attained : %lld\n", this->maxDepthAttained);
|
||||||
printf("Max intersect count : %lld\n", this->maxIntersectOnARay);
|
printf("Max intersect count : %lld\n", this->maxIntersectOnARay);
|
||||||
printf("==================================================\n");
|
printf("==================================================\n");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,10 +51,8 @@ void Cone::intersectCaps(Ray r, Intersect &xs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Cone::localIntersect(Ray r)
|
void Cone::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
|
||||||
|
|
||||||
double A = (r.direction.x * r.direction.x) -
|
double A = (r.direction.x * r.direction.x) -
|
||||||
(r.direction.y * r.direction.y) +
|
(r.direction.y * r.direction.y) +
|
||||||
(r.direction.z * r.direction.z);
|
(r.direction.z * r.direction.z);
|
||||||
@@ -70,7 +68,7 @@ Intersect Cone::localIntersect(Ray r)
|
|||||||
if ((fabs(A) <= getEpsilon()) && (fabs(B) >= getEpsilon()))
|
if ((fabs(A) <= getEpsilon()) && (fabs(B) >= getEpsilon()))
|
||||||
{
|
{
|
||||||
double t = -C / (2*B);
|
double t = -C / (2*B);
|
||||||
ret.add(Intersection(t, this));
|
xs.add(Intersection(t, this));
|
||||||
}
|
}
|
||||||
else if (fabs(A) >= getEpsilon())
|
else if (fabs(A) >= getEpsilon())
|
||||||
{
|
{
|
||||||
@@ -83,20 +81,18 @@ Intersect Cone::localIntersect(Ray r)
|
|||||||
double y0 = r.origin.y + t0 * r.direction.y;
|
double y0 = r.origin.y + t0 * r.direction.y;
|
||||||
if ((this->minCap < y0) && (y0 < this->maxCap))
|
if ((this->minCap < y0) && (y0 < this->maxCap))
|
||||||
{
|
{
|
||||||
ret.add(Intersection(t0, this));
|
xs.add(Intersection(t0, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
double y1 = r.origin.y + t1 * r.direction.y;
|
double y1 = r.origin.y + t1 * r.direction.y;
|
||||||
if ((this->minCap < y1) && (y1 < this->maxCap))
|
if ((this->minCap < y1) && (y1 < this->maxCap))
|
||||||
{
|
{
|
||||||
ret.add(Intersection(t1, this));
|
xs.add(Intersection(t1, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->intersectCaps(r, ret);
|
this->intersectCaps(r, xs);
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Cone::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Cone::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
|||||||
146
source/shapes/csg.cpp
Normal file
146
source/shapes/csg.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Constructive Solid Geometry (CSG) implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <tuple.h>
|
||||||
|
#include <ray.h>
|
||||||
|
#include <shape.h>
|
||||||
|
#include <csg.h>
|
||||||
|
#include <math_helper.h>
|
||||||
|
|
||||||
|
|
||||||
|
CSG::CSG(OperationType operation, Shape *left, Shape *right) : Shape(Shape::CSG), operation(operation), left(left), right(right)
|
||||||
|
{
|
||||||
|
stats.addCsg();
|
||||||
|
|
||||||
|
this->left->setParent(this);
|
||||||
|
this->right->setParent(this);
|
||||||
|
|
||||||
|
this->bounds | this->left->getBounds();
|
||||||
|
this->bounds | this->right->getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSG::localIntersect(Ray r, Intersect &xs)
|
||||||
|
{
|
||||||
|
this->intersect(r, xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CSG::intersect(Ray &r, Intersect &xs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Intersect tmp = Intersect();
|
||||||
|
|
||||||
|
if (this->bounds.intesectMe(r))
|
||||||
|
{
|
||||||
|
this->left->intersect(r, tmp);
|
||||||
|
this->right->intersect(r, tmp);
|
||||||
|
|
||||||
|
this->filterIntersections(tmp, xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple CSG::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
{
|
||||||
|
return Vector(1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundingBox CSG::getLocalBounds()
|
||||||
|
{
|
||||||
|
return this->bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundingBox CSG::getBounds()
|
||||||
|
{
|
||||||
|
if (this->bounds.isEmpty()) { this->updateBoundingBox(); }
|
||||||
|
return this->bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSG::updateBoundingBox()
|
||||||
|
{
|
||||||
|
this->bounds.reset();
|
||||||
|
|
||||||
|
this->bounds | this->left->getBounds();
|
||||||
|
this->bounds | this->right->getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSG::updateTransform()
|
||||||
|
{
|
||||||
|
Shape::updateTransform();
|
||||||
|
|
||||||
|
this->left->updateTransform();
|
||||||
|
this->right->updateTransform();
|
||||||
|
|
||||||
|
/* Once the full stack being notified of the changes, let's update the
|
||||||
|
* bounding box
|
||||||
|
*/
|
||||||
|
this->updateBoundingBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSG::includes(Shape *b)
|
||||||
|
{
|
||||||
|
if (this->left->includes(b)) { return true; }
|
||||||
|
if (this->right->includes(b)) { return true; }
|
||||||
|
if (this == b) { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSG::intersectionAllowed(bool leftHit, bool inLeft, bool inRight)
|
||||||
|
{
|
||||||
|
switch(this->operation)
|
||||||
|
{
|
||||||
|
case CSG::UNION: return (leftHit && !inRight) || (!leftHit && !inLeft);
|
||||||
|
case CSG::INTERSECTION: return (!leftHit && inLeft) || (leftHit && inRight);
|
||||||
|
case CSG::DIFFERENCE: return (leftHit && !inRight) || (!leftHit && inLeft);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSG::filterIntersections(Intersect &xs, Intersect &ret)
|
||||||
|
{
|
||||||
|
bool inl = false;
|
||||||
|
bool inr = false;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < xs.count(); i++)
|
||||||
|
{
|
||||||
|
bool lhit = this->left->includes(xs[i].object);
|
||||||
|
|
||||||
|
if (this->intersectionAllowed(lhit, inl, inr))
|
||||||
|
{
|
||||||
|
ret.add(xs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhit)
|
||||||
|
{
|
||||||
|
inl = !inl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inr = !inr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSG::lock()
|
||||||
|
{
|
||||||
|
Shape::lock();
|
||||||
|
if(this->left)
|
||||||
|
{
|
||||||
|
this->left->lock();
|
||||||
|
}
|
||||||
|
if(this->right)
|
||||||
|
{
|
||||||
|
this->right->lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSG::dumpMe(FILE *fp)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -36,10 +36,8 @@ void Cube::checkAxis(double axeOrigin, double axeDirection, double *axeMin, doub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Cube::localIntersect(Ray r)
|
void Cube::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
|
||||||
|
|
||||||
double xtMin, xtMax, ytMin, ytMax, ztMin, ztMax;
|
double xtMin, xtMax, ytMin, ytMax, ztMin, ztMax;
|
||||||
double tMin, tMax;
|
double tMin, tMax;
|
||||||
|
|
||||||
@@ -52,11 +50,9 @@ Intersect Cube::localIntersect(Ray r)
|
|||||||
|
|
||||||
if (tMin <= tMax)
|
if (tMin <= tMax)
|
||||||
{
|
{
|
||||||
ret.add(Intersection(tMin, this));
|
xs.add(Intersection(tMin, this));
|
||||||
ret.add(Intersection(tMax, this));
|
xs.add(Intersection(tMax, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Cube::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Cube::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ bool Cylinder::checkCap(Ray r, double t)
|
|||||||
|
|
||||||
void Cylinder::intersectCaps(Ray r, Intersect &xs)
|
void Cylinder::intersectCaps(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
/* Caps only mattter is the cylinder is closed, and might possibly be
|
/* Caps only matter if the cylinder is closed, and might possibly be
|
||||||
* intersected by the ray
|
* intersected by the ray
|
||||||
*/
|
*/
|
||||||
if ((this->isClosed) && (fabs(r.direction.y) > getEpsilon()))
|
if ((this->isClosed) && (fabs(r.direction.y) > getEpsilon()))
|
||||||
@@ -51,10 +51,8 @@ void Cylinder::intersectCaps(Ray r, Intersect &xs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Cylinder::localIntersect(Ray r)
|
void Cylinder::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
|
||||||
|
|
||||||
double A = r.direction.x * r.direction.x + r.direction.z * r.direction.z;
|
double A = r.direction.x * r.direction.x + r.direction.z * r.direction.z;
|
||||||
|
|
||||||
/* Ray is parallel to the Y axis */
|
/* Ray is parallel to the Y axis */
|
||||||
@@ -74,20 +72,18 @@ Intersect Cylinder::localIntersect(Ray r)
|
|||||||
double y0 = r.origin.y + t0 * r.direction.y;
|
double y0 = r.origin.y + t0 * r.direction.y;
|
||||||
if ((this->minCap < y0) && (y0 < this->maxCap))
|
if ((this->minCap < y0) && (y0 < this->maxCap))
|
||||||
{
|
{
|
||||||
ret.add(Intersection(t0, this));
|
xs.add(Intersection(t0, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
double y1 = r.origin.y + t1 * r.direction.y;
|
double y1 = r.origin.y + t1 * r.direction.y;
|
||||||
if ((this->minCap < y1) && (y1 < this->maxCap))
|
if ((this->minCap < y1) && (y1 < this->maxCap))
|
||||||
{
|
{
|
||||||
ret.add(Intersection(t1, this));
|
xs.add(Intersection(t1, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->intersectCaps(r, ret);
|
this->intersectCaps(r, xs);
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Cylinder::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Cylinder::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
|||||||
@@ -11,25 +11,33 @@
|
|||||||
#include <group.h>
|
#include <group.h>
|
||||||
#include <math_helper.h>
|
#include <math_helper.h>
|
||||||
#include <renderstat.h>
|
#include <renderstat.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define MIN_ALLOC (2)
|
#define MIN_ALLOC (2)
|
||||||
|
|
||||||
Group::Group() : Shape(SHAPE_GROUP)
|
Group::Group(const char *name) : Shape(Shape::GROUP)
|
||||||
{
|
{
|
||||||
stats.addGroup();
|
stats.addGroup();
|
||||||
this->allocatedObjectCount = MIN_ALLOC;
|
this->allocatedObjectCount = MIN_ALLOC;
|
||||||
this->objectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC);
|
this->objectList = (Shape **)calloc(sizeof(Shape **), MIN_ALLOC);
|
||||||
this->objectCount = 0;
|
this->objectCount = 0;
|
||||||
|
|
||||||
this->allocatedUnboxableObjectCount = MIN_ALLOC;
|
this->allocatedUnboxableObjectCount = MIN_ALLOC;
|
||||||
this->unboxableObjectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC);
|
this->unboxableObjectList = (Shape **)calloc(sizeof(Shape **), MIN_ALLOC);
|
||||||
this->unboxableObjectCount = 0;
|
this->unboxableObjectCount = 0;
|
||||||
|
|
||||||
|
if (name != nullptr)
|
||||||
|
{
|
||||||
|
strncpy(this->name, name, 32);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(this->name, "untitled", 32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Group::intersect(Ray r)
|
void Group::intersect(Ray &r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
|
||||||
int i, j;
|
int i, j;
|
||||||
if (this->objectCount > 0)
|
if (this->objectCount > 0)
|
||||||
{
|
{
|
||||||
@@ -37,14 +45,7 @@ Intersect Group::intersect(Ray r)
|
|||||||
{
|
{
|
||||||
for (i = 0 ; i < this->objectCount ; i++)
|
for (i = 0 ; i < this->objectCount ; i++)
|
||||||
{
|
{
|
||||||
Intersect xs = this->objectList[i]->intersect(r);
|
this->objectList[i]->intersect(r, xs);
|
||||||
if (xs.count() > 0)
|
|
||||||
{
|
|
||||||
for (j = 0 ; j < xs.count() ; j++)
|
|
||||||
{
|
|
||||||
ret.add(xs[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,22 +55,42 @@ Intersect Group::intersect(Ray r)
|
|||||||
{
|
{
|
||||||
for(i = 0; i < this->unboxableObjectCount; i++)
|
for(i = 0; i < this->unboxableObjectCount; i++)
|
||||||
{
|
{
|
||||||
Intersect xs = this->unboxableObjectList[i]->intersect(r);
|
this->unboxableObjectList[i]->intersect(r, xs);
|
||||||
if (xs.count() > 0)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Group::includes(Shape *b)
|
||||||
|
{
|
||||||
|
if (this->objectCount > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0 ; i < this->objectCount ; i++)
|
||||||
|
{
|
||||||
|
if (this->objectList[i] == b)
|
||||||
{
|
{
|
||||||
for(j = 0; j < xs.count(); j++)
|
return true;
|
||||||
{
|
|
||||||
ret.add(xs[j]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
if (this->unboxableObjectCount > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < this->unboxableObjectCount; i++)
|
||||||
|
{
|
||||||
|
if (this->unboxableObjectList[i] == b)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Group::localIntersect(Ray r)
|
void Group::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
return Intersect();
|
this->intersect(r, xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Group::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Group::localNormalAt(Tuple point, Intersection *hit)
|
||||||
@@ -88,7 +109,7 @@ void Group::addObject(Shape *s)
|
|||||||
this->objectList = (Shape **)realloc(this->objectList, sizeof(Shape **) * this->allocatedObjectCount);
|
this->objectList = (Shape **)realloc(this->objectList, sizeof(Shape **) * this->allocatedObjectCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->parent = this;
|
s->setParent(this);
|
||||||
s->updateTransform();
|
s->updateTransform();
|
||||||
|
|
||||||
this->objectList[this->objectCount++] = s;
|
this->objectList[this->objectCount++] = s;
|
||||||
@@ -104,13 +125,44 @@ void Group::addObject(Shape *s)
|
|||||||
this->unboxableObjectList = (Shape **)realloc(this->unboxableObjectList, sizeof(Shape **) * this->allocatedUnboxableObjectCount);
|
this->unboxableObjectList = (Shape **)realloc(this->unboxableObjectList, sizeof(Shape **) * this->allocatedUnboxableObjectCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->parent = this;
|
s->setParent(this);
|
||||||
s->updateTransform();
|
s->updateTransform();
|
||||||
|
|
||||||
this->unboxableObjectList[this->unboxableObjectCount++] = s;
|
this->unboxableObjectList[this->unboxableObjectCount++] = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Group::removeObject(Shape *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (s->haveFiniteBounds())
|
||||||
|
{
|
||||||
|
for (i = 0; i < this->objectCount; i++)
|
||||||
|
{
|
||||||
|
if (this->objectList[i] == s)
|
||||||
|
{
|
||||||
|
this->objectCount --;
|
||||||
|
this->objectList[i] = this->objectList[this->objectCount];
|
||||||
|
this->objectList[this->objectCount] = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < this->unboxableObjectCount; i++)
|
||||||
|
{
|
||||||
|
if (this->unboxableObjectList[i] == s)
|
||||||
|
{
|
||||||
|
this->unboxableObjectCount --;
|
||||||
|
this->unboxableObjectList[i] = this->unboxableObjectList[this->unboxableObjectCount];
|
||||||
|
this->unboxableObjectList[this->unboxableObjectCount] = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Group::isEmpty()
|
bool Group::isEmpty()
|
||||||
{
|
{
|
||||||
return (this->objectCount == 0) && (this->unboxableObjectCount == 0);
|
return (this->objectCount == 0) && (this->unboxableObjectCount == 0);
|
||||||
@@ -175,6 +227,7 @@ void Group::dumpMe(FILE *fp)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
fprintf(fp, "\"Type\": \"Group\",\n");
|
fprintf(fp, "\"Type\": \"Group\",\n");
|
||||||
|
fprintf(fp, "\"Name\": \"%s\",\n", this->name);
|
||||||
if (this->objectCount > 0)
|
if (this->objectCount > 0)
|
||||||
{
|
{
|
||||||
fprintf(fp, "\"Objects\": {\n");
|
fprintf(fp, "\"Objects\": {\n");
|
||||||
@@ -199,4 +252,28 @@ void Group::dumpMe(FILE *fp)
|
|||||||
fprintf(fp, "},\n");
|
fprintf(fp, "},\n");
|
||||||
}
|
}
|
||||||
Shape::dumpMe(fp);
|
Shape::dumpMe(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Group::lock()
|
||||||
|
{
|
||||||
|
Shape::lock();
|
||||||
|
|
||||||
|
/* Now notify included object they have to lock */
|
||||||
|
int i;
|
||||||
|
if (this->objectCount > 0)
|
||||||
|
{
|
||||||
|
for (i = 0 ; i < this->objectCount ; i++)
|
||||||
|
{
|
||||||
|
this->objectList[i]->lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->unboxableObjectCount > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < this->unboxableObjectCount; i++)
|
||||||
|
{
|
||||||
|
this->unboxableObjectList[i]->lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,6 @@ double Light::intensityAt(World &w, Tuple point)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total / this->samples;
|
return total / this->samples;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,32 +17,64 @@
|
|||||||
#include <math_helper.h>
|
#include <math_helper.h>
|
||||||
#include <group.h>
|
#include <group.h>
|
||||||
#include <triangle.h>
|
#include <triangle.h>
|
||||||
|
#include <sphere.h>
|
||||||
#include <smoothtriangle.h>
|
#include <smoothtriangle.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <cone.h>
|
||||||
|
#include <cylinder.h>
|
||||||
|
|
||||||
#define MIN_ALLOC (2)
|
#define MIN_ALLOC (2)
|
||||||
#define DEFAULT_GROUP (0)
|
|
||||||
|
|
||||||
OBJFile::OBJFile() : Shape(SHAPE_OBJFILE), ignoredLines(0)
|
//#define DEBUG_NORMAL
|
||||||
|
|
||||||
|
OBJFile::OBJFile() : Shape(Shape::OBJFILE), ignoredLines(0)
|
||||||
{
|
{
|
||||||
stats.addOBJFile();
|
stats.addOBJFile();
|
||||||
|
|
||||||
this->allocatedFaceGroupCount = MIN_ALLOC;
|
|
||||||
this->faceGroupList = (Group **)calloc(sizeof(Group **), MIN_ALLOC);
|
|
||||||
this->faceGroupCount = 0;
|
|
||||||
|
|
||||||
this->allocatedVertexCount = MIN_ALLOC;
|
this->allocatedVertexCount = MIN_ALLOC;
|
||||||
this->vertexList = (Point **)calloc(sizeof(Point **), MIN_ALLOC);
|
this->vertexList = (Point **)calloc(sizeof(Point **), MIN_ALLOC);
|
||||||
this->vertexCount = 0;
|
this->vertexCount = 0;
|
||||||
|
|
||||||
|
|
||||||
this->allocatedVertexNormalCount = MIN_ALLOC;
|
this->allocatedVertexNormalCount = MIN_ALLOC;
|
||||||
this->vertexNormalList = (Vector **)calloc(sizeof(Vector **), MIN_ALLOC);
|
this->vertexNormalList = (Vector **)calloc(sizeof(Vector **), MIN_ALLOC);
|
||||||
this->vertexNormalCount = 0;
|
this->vertexNormalCount = 0;
|
||||||
|
|
||||||
/* There is always a default group */
|
/* The base group */
|
||||||
this->addGroup(new Group());
|
this->baseGroup = new Group(OBJ_DEFAULT_GROUP);
|
||||||
|
this->currentGroup = this->baseGroup;
|
||||||
|
this->baseGroup->parent = this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OBJFile::~OBJFile()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (vertexCount > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
delete this->vertexList[i];
|
||||||
|
this->vertexList[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(this->vertexList);
|
||||||
|
this->vertexList = nullptr;
|
||||||
|
|
||||||
|
if (vertexNormalCount > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < vertexNormalCount; i++)
|
||||||
|
{
|
||||||
|
delete this->vertexNormalList[i];
|
||||||
|
this->vertexNormalList[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(this->vertexNormalList);
|
||||||
|
this->vertexNormalList = nullptr;
|
||||||
|
|
||||||
|
/* It is not our responsibility to clear the group object as this object may be destroyed before the
|
||||||
|
* render is done
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
OBJFile::OBJFile(const char *filepath) : OBJFile()
|
OBJFile::OBJFile(const char *filepath) : OBJFile()
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@@ -53,7 +85,8 @@ OBJFile::OBJFile(const char *filepath) : OBJFile()
|
|||||||
{
|
{
|
||||||
fseek(fp, 0, SEEK_END);
|
fseek(fp, 0, SEEK_END);
|
||||||
fileSize = ftell(fp);
|
fileSize = ftell(fp);
|
||||||
fileBuff = (char *)calloc(fileSize, 1);
|
/* Add one byte to the size to make sure it is null terminated */
|
||||||
|
fileBuff = (char *)calloc(fileSize + 1, 1);
|
||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
fileSize = fread(fileBuff, 1, fileSize, fp);
|
fileSize = fread(fileBuff, 1, fileSize, fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -70,18 +103,13 @@ OBJFile::OBJFile(const char *filepath) : OBJFile()
|
|||||||
|
|
||||||
void OBJFile::addGroup(Group *group)
|
void OBJFile::addGroup(Group *group)
|
||||||
{
|
{
|
||||||
if ((this->faceGroupCount + 1) > this->allocatedFaceGroupCount)
|
this->baseGroup->addObject(group);
|
||||||
{
|
|
||||||
this->allocatedFaceGroupCount *= 2;
|
|
||||||
this->faceGroupList = (Group **)realloc(this->faceGroupList, sizeof(Group **) * this->allocatedFaceGroupCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
group->parent = this;
|
group->setParent(this);
|
||||||
group->updateTransform();
|
group->updateTransform();
|
||||||
|
|
||||||
this->faceGroupList[this->faceGroupCount++] = group;
|
|
||||||
|
|
||||||
this->bounds | group->getBounds();
|
this->bounds | group->getBounds();
|
||||||
|
|
||||||
|
this->currentGroup = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBJFile::addVertex(Point *vertex)
|
void OBJFile::addVertex(Point *vertex)
|
||||||
@@ -106,33 +134,45 @@ void OBJFile::addVertexNormal(Vector *vertexNormal)
|
|||||||
this->vertexNormalList[this->vertexNormalCount++] = vertexNormal;
|
this->vertexNormalList[this->vertexNormalCount++] = vertexNormal;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect OBJFile::intersect(Ray r)
|
Group *OBJFile::groups(const char *groupName)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
if (strncmp(groupName, this->baseGroup->getName(), strlen(groupName)) == 0)
|
||||||
int i, j;
|
|
||||||
if (this->faceGroupCount > 0)
|
|
||||||
{
|
{
|
||||||
if (this->bounds.intesectMe(r))
|
return this->baseGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < this->baseGroup->getObjectCount(); i++)
|
||||||
|
{
|
||||||
|
Shape *cur = (*this->baseGroup)[i];
|
||||||
|
|
||||||
|
if (cur->getType() == Shape::GROUP)
|
||||||
{
|
{
|
||||||
for (i = 0 ; i < this->faceGroupCount ; i++)
|
Group *curGrp = (Group *)cur;
|
||||||
|
if (strncmp(groupName, curGrp->getName(), strlen(groupName)) == 0)
|
||||||
{
|
{
|
||||||
Intersect xs = this->faceGroupList[i]->intersect(r);
|
return curGrp;
|
||||||
if (xs.count() > 0)
|
|
||||||
{
|
|
||||||
for (j = 0 ; j < xs.count() ; j++)
|
|
||||||
{
|
|
||||||
ret.add(xs[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
/* Not found */
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect OBJFile::localIntersect(Ray r)
|
void OBJFile::intersect(Ray &r, Intersect &xs)
|
||||||
{
|
{
|
||||||
return Intersect();
|
this->baseGroup->intersect(r, xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OBJFile::includes(Shape *b)
|
||||||
|
{
|
||||||
|
return this->baseGroup->includes(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBJFile::localIntersect(Ray r, Intersect &xs)
|
||||||
|
{
|
||||||
|
this->intersect(r, xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple OBJFile::localNormalAt(Tuple point, Intersection *hit)
|
Tuple OBJFile::localNormalAt(Tuple point, Intersection *hit)
|
||||||
@@ -156,14 +196,7 @@ void OBJFile::updateBoundingBox()
|
|||||||
int i;
|
int i;
|
||||||
this->bounds.reset();
|
this->bounds.reset();
|
||||||
|
|
||||||
for(i = 0; i < this->faceGroupCount; i++)
|
this->bounds | this->baseGroup->getBounds();
|
||||||
{
|
|
||||||
if (this->faceGroupList[i]->haveFiniteBounds())
|
|
||||||
{
|
|
||||||
BoundingBox objB = this->faceGroupList[i]->getBounds();
|
|
||||||
this->bounds | objB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBJFile::updateTransform()
|
void OBJFile::updateTransform()
|
||||||
@@ -171,10 +204,8 @@ void OBJFile::updateTransform()
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
Shape::updateTransform();
|
Shape::updateTransform();
|
||||||
for (i = 0 ; i < this->faceGroupCount ; i++)
|
|
||||||
{
|
this->baseGroup->updateTransform();
|
||||||
this->faceGroupList[i]->updateTransform();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Once the full stack being notified of the changes, let's update the
|
/* Once the full stack being notified of the changes, let's update the
|
||||||
* bounding box
|
* bounding box
|
||||||
@@ -187,14 +218,22 @@ void OBJFile::dumpMe(FILE * fp)
|
|||||||
int i;
|
int i;
|
||||||
fprintf(fp, "\"Type\": \"OBJFile\",\n");
|
fprintf(fp, "\"Type\": \"OBJFile\",\n");
|
||||||
fprintf(fp, "\"Objects\": {\n");
|
fprintf(fp, "\"Objects\": {\n");
|
||||||
for(i = 0; i < this->faceGroupCount; i++)
|
this->baseGroup->dumpMe(fp);
|
||||||
|
fprintf(fp, "},\n");
|
||||||
|
fprintf(fp, "\"Vertices\": {\n");
|
||||||
|
for(i = 1; i < this->vertexCount + 1; i++)
|
||||||
{
|
{
|
||||||
fprintf(fp, "\"%d\": {\n", i);
|
fprintf(fp, "\"v[%d]\": { \"x\": %f, \"y\": %f, \"z\": %f },\n", i,
|
||||||
this->faceGroupList[i]->dumpMe(fp);
|
this->vertices(i).x, this->vertices(i).y, this->vertices(i).z);
|
||||||
fprintf(fp, "},\n");
|
}
|
||||||
|
fprintf(fp, "},\n");
|
||||||
|
fprintf(fp, "\"NormalVertices\": {\n");
|
||||||
|
for(i = 1; i < this->vertexNormalCount + 1; i++)
|
||||||
|
{
|
||||||
|
fprintf(fp, "\"vn[%d]\": { \"x\": %f, \"y\": %f, \"z\": %f },\n", i,
|
||||||
|
this->verticesNormal(i).x, this->verticesNormal(i).y, this->verticesNormal(i).z);
|
||||||
}
|
}
|
||||||
fprintf(fp, "},\n");
|
fprintf(fp, "},\n");
|
||||||
|
|
||||||
Shape::dumpMe(fp);
|
Shape::dumpMe(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +244,7 @@ int OBJFile::parseOBJFile(const char *content)
|
|||||||
/* I don't think we will handle lines of more than 512 characters... */
|
/* I don't think we will handle lines of more than 512 characters... */
|
||||||
char lineBuff[MAX_LINE_LENGTH];
|
char lineBuff[MAX_LINE_LENGTH];
|
||||||
uint32_t currentLineNum = 1;
|
uint32_t currentLineNum = 1;
|
||||||
|
uint32_t totalLength = strlen(content);
|
||||||
/* Need to process line by line */
|
/* Need to process line by line */
|
||||||
const char *bufferPos = content;
|
const char *bufferPos = content;
|
||||||
const char *lineNewline;
|
const char *lineNewline;
|
||||||
@@ -233,6 +272,12 @@ int OBJFile::parseOBJFile(const char *content)
|
|||||||
this->parseLine(lineBuff, currentLineNum);
|
this->parseLine(lineBuff, currentLineNum);
|
||||||
|
|
||||||
bufferPos += lineLength + 1;
|
bufferPos += lineLength + 1;
|
||||||
|
|
||||||
|
if ((bufferPos - content) >= totalLength)
|
||||||
|
{
|
||||||
|
/* We are past the length of the buffer, don't need to continue */
|
||||||
|
break;
|
||||||
|
}
|
||||||
currentLineNum++;
|
currentLineNum++;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -283,7 +328,6 @@ static int parseFaceVertex(char *buf, uint32_t &v, uint32_t &vt, uint32_t &vn)
|
|||||||
{
|
{
|
||||||
uint32_t bufPos = 0;
|
uint32_t bufPos = 0;
|
||||||
uint32_t lineLength = strlen(buf);
|
uint32_t lineLength = strlen(buf);
|
||||||
char *tmp = buf;
|
|
||||||
vt = INT32_MAX;
|
vt = INT32_MAX;
|
||||||
vn = INT32_MAX;
|
vn = INT32_MAX;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@@ -319,6 +363,58 @@ static int parseFaceVertex(char *buf, uint32_t &v, uint32_t &vt, uint32_t &vn)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_NORMAL
|
||||||
|
Shape *makeVector(Point pos, Vector verNorm, Colour c, double scale = 1)
|
||||||
|
{
|
||||||
|
Group *ret = new Group("Vector");
|
||||||
|
Sphere *sp = new Sphere();
|
||||||
|
Colour c2 = c;
|
||||||
|
sp->material.colour = c2;
|
||||||
|
sp->material.ambient = 1;
|
||||||
|
sp->material.refractiveIndex = 0;
|
||||||
|
sp->material.reflective = 0;
|
||||||
|
sp->material.specular = 0;
|
||||||
|
sp->materialSet = true;
|
||||||
|
sp->setTransform(translation(pos.x, pos.y, pos.z) * scaling(0.1, 0.1, 0.1));
|
||||||
|
ret->addObject(sp);
|
||||||
|
|
||||||
|
double theta = atan2(verNorm.x, verNorm.z);
|
||||||
|
double radius = verNorm.magnitude();
|
||||||
|
double phi = acos(verNorm.y / radius);
|
||||||
|
|
||||||
|
sp = new Sphere();
|
||||||
|
|
||||||
|
c2 = c;
|
||||||
|
c2.x /=3; c2.y /=3; c2.y /=3;
|
||||||
|
|
||||||
|
sp->material.colour = c2;
|
||||||
|
sp->material.ambient = 1;
|
||||||
|
sp->material.refractiveIndex = 0;
|
||||||
|
sp->material.transparency = 0;
|
||||||
|
sp->material.specular = 0;
|
||||||
|
sp->materialSet = true;
|
||||||
|
sp->setTransform(translation(pos.x, pos.y, pos.z) * translation(verNorm.x, verNorm.y, verNorm.z) * scaling(0.1, 0.1, 0.1));
|
||||||
|
ret->addObject(sp);
|
||||||
|
|
||||||
|
c2 = c;
|
||||||
|
c2.x /=2; c2.y /=3; c2.y /=2;
|
||||||
|
|
||||||
|
Cone *cn = new Cone();
|
||||||
|
cn->minCap = 0;
|
||||||
|
cn->maxCap = 1;
|
||||||
|
cn->material.colour = c2;
|
||||||
|
cn->material.ambient = 1;
|
||||||
|
cn->material.refractiveIndex = 0;
|
||||||
|
cn->material.reflective = 0;
|
||||||
|
cn->material.specular = 0;
|
||||||
|
cn->materialSet = true;
|
||||||
|
cn->setTransform(translation(pos.x, pos.y, pos.z) * rotationY(theta) * rotationX(phi) * scaling(0.1, radius, 0.1));
|
||||||
|
ret->addObject(cn);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Actually execute the line */
|
/* Actually execute the line */
|
||||||
int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
||||||
{
|
{
|
||||||
@@ -370,6 +466,17 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_NORMAL
|
||||||
|
this->currentGroup->addObject(makeVector(this->vertices(v[1]),
|
||||||
|
this->verticesNormal(vn[1]),
|
||||||
|
Colour(1, 0, 1)));
|
||||||
|
this->currentGroup->addObject(makeVector(this->vertices(v[2]),
|
||||||
|
this->verticesNormal(vn[2]),
|
||||||
|
Colour(0.5, 0, 0.5)));
|
||||||
|
this->currentGroup->addObject(makeVector(this->vertices(v[3]),
|
||||||
|
this->verticesNormal(vn[3]),
|
||||||
|
Colour(0.5, 0, 1)));
|
||||||
|
#endif
|
||||||
t = new SmoothTriangle(this->vertices(v[1]),
|
t = new SmoothTriangle(this->vertices(v[1]),
|
||||||
this->vertices(v[2]),
|
this->vertices(v[2]),
|
||||||
this->vertices(v[3]),
|
this->vertices(v[3]),
|
||||||
@@ -377,11 +484,29 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
|||||||
this->verticesNormal(vn[2]),
|
this->verticesNormal(vn[2]),
|
||||||
this->verticesNormal(vn[3]));
|
this->verticesNormal(vn[3]));
|
||||||
}
|
}
|
||||||
this->faceGroupList[this->faceGroupCount - 1]->addObject(t);
|
/* Set the object id to the OBJ one */
|
||||||
|
t->setObjectId(this->getObjectId());
|
||||||
|
|
||||||
|
this->currentGroup->addObject(t);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
else if (argc > 4)
|
else if (argc > 4)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_NORMAL
|
||||||
|
if (vn[1] != INT32_MAX)
|
||||||
|
{
|
||||||
|
for(i = 2; i < (argc); i++)
|
||||||
|
{
|
||||||
|
this->currentGroup->addObject(makeVector(this->vertices(v[i]),
|
||||||
|
this->verticesNormal(vn[i]),
|
||||||
|
Colour(1, 1, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->currentGroup->addObject(makeVector(this->vertices(v[1]),
|
||||||
|
this->verticesNormal(vn[1]),
|
||||||
|
Colour(0, 1, 0)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
for(i = 2; i < (argc - 1); i++)
|
for(i = 2; i < (argc - 1); i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -401,7 +526,8 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
|||||||
this->verticesNormal(vn[i]),
|
this->verticesNormal(vn[i]),
|
||||||
this->verticesNormal(vn[i + 1]));
|
this->verticesNormal(vn[i + 1]));
|
||||||
}
|
}
|
||||||
this->faceGroupList[this->faceGroupCount - 1]->addObject(t);
|
t->setObjectId(this->getObjectId());
|
||||||
|
this->currentGroup->addObject(t);
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
@@ -414,7 +540,7 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
|||||||
{
|
{
|
||||||
if (argc == 2)
|
if (argc == 2)
|
||||||
{
|
{
|
||||||
this->addGroup(new Group());
|
this->addGroup(new Group(argv[1]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -422,4 +548,11 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBJFile::lock()
|
||||||
|
{
|
||||||
|
Shape::lock();
|
||||||
|
|
||||||
|
this->baseGroup->lock();
|
||||||
}
|
}
|
||||||
@@ -12,22 +12,20 @@
|
|||||||
#include <plane.h>
|
#include <plane.h>
|
||||||
#include <math_helper.h>
|
#include <math_helper.h>
|
||||||
|
|
||||||
Intersect Plane::localIntersect(Ray r)
|
void Plane::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
double t;
|
double t;
|
||||||
Intersect ret = Intersect();
|
|
||||||
|
|
||||||
if (fabs(r.direction.y) < getEpsilon())
|
if (fabs(r.direction.y) < getEpsilon())
|
||||||
{
|
{
|
||||||
/* With a direction == 0, the ray can't intersect the plane */
|
/* With a direction == 0, the ray can't intersect the plane */
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t = -r.origin.y / r.direction.y;
|
||||||
|
|
||||||
t = -r.origin.y / r.direction.y;
|
xs.add(Intersection(t, this));
|
||||||
|
}
|
||||||
ret.add(Intersection(t, this));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Plane::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Plane::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
|||||||
@@ -15,18 +15,26 @@
|
|||||||
|
|
||||||
Shape::Shape(ShapeType type)
|
Shape::Shape(ShapeType type)
|
||||||
{
|
{
|
||||||
|
this->objectId = Shape::newObjectId();
|
||||||
|
this->locked = false;
|
||||||
this->parent = nullptr;
|
this->parent = nullptr;
|
||||||
this->dropShadow = true;
|
this->dropShadow = true;
|
||||||
this->type = type;
|
this->type = type;
|
||||||
this->localTransformMatrix = Matrix4().identity();
|
this->localTransformMatrix = Matrix4().identity();
|
||||||
this->updateTransform();
|
|
||||||
this->materialSet = false;
|
this->materialSet = false;
|
||||||
|
|
||||||
|
this->updateTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Shape::intersect(Ray r)
|
uint64_t Shape::newObjectId()
|
||||||
{
|
{
|
||||||
return this->localIntersect(this->invTransform(r));
|
static uint64_t id = 0;
|
||||||
};
|
uint64_t ret;
|
||||||
|
|
||||||
|
ret = id++;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
Tuple Shape::normalToWorld(Tuple normalVector)
|
Tuple Shape::normalToWorld(Tuple normalVector)
|
||||||
{
|
{
|
||||||
@@ -51,6 +59,8 @@ Tuple Shape::normalAt(Tuple point, Intersection *hit)
|
|||||||
|
|
||||||
void Shape::updateTransform()
|
void Shape::updateTransform()
|
||||||
{
|
{
|
||||||
|
if (this->locked) return;
|
||||||
|
|
||||||
this->transformMatrix = this->localTransformMatrix;
|
this->transformMatrix = this->localTransformMatrix;
|
||||||
if (this->parent != nullptr)
|
if (this->parent != nullptr)
|
||||||
{
|
{
|
||||||
@@ -63,6 +73,8 @@ void Shape::updateTransform()
|
|||||||
|
|
||||||
void Shape::setTransform(Matrix transform)
|
void Shape::setTransform(Matrix transform)
|
||||||
{
|
{
|
||||||
|
if (this->locked) return;
|
||||||
|
|
||||||
this->localTransformMatrix = transform;
|
this->localTransformMatrix = transform;
|
||||||
this->updateTransform();
|
this->updateTransform();
|
||||||
}
|
}
|
||||||
@@ -89,13 +101,36 @@ BoundingBox Shape::getBounds()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Material *Shape::getMaterial()
|
||||||
|
{
|
||||||
|
Shape *s = this;
|
||||||
|
while((!s->materialSet) && (s->parent != nullptr))
|
||||||
|
{
|
||||||
|
s = s->parent;
|
||||||
|
}
|
||||||
|
return &s->material;
|
||||||
|
}
|
||||||
|
|
||||||
void Shape::dumpMe(FILE *fp)
|
void Shape::dumpMe(FILE *fp)
|
||||||
{
|
{
|
||||||
fprintf(fp, "\"Material\": {\n");
|
if (this->materialSet)
|
||||||
this->material.dumpMe(fp);
|
{
|
||||||
fprintf(fp, "},\n");
|
fprintf(fp, "\"Material\": {\n");
|
||||||
|
this->material.dumpMe(fp);
|
||||||
|
fprintf(fp, "},\n");
|
||||||
|
}
|
||||||
fprintf(fp, "\"DropShadow\": %d,\n", this->dropShadow);
|
fprintf(fp, "\"DropShadow\": %d,\n", this->dropShadow);
|
||||||
fprintf(fp, "\"BoundingBox\": {\n");
|
fprintf(fp, "\"Locked\": %d,\n", this->locked);
|
||||||
this->getBounds().dumpMe(fp);
|
fprintf(fp, "\"MaterialSet\": %d,\n", this->materialSet);
|
||||||
fprintf(fp, "},\n");
|
if (this->haveFiniteBounds())
|
||||||
|
{
|
||||||
|
fprintf(fp, "\"BoundingBox\": {\n");
|
||||||
|
this->getBounds().dumpMe(fp);
|
||||||
|
fprintf(fp, "},\n");
|
||||||
|
}
|
||||||
|
fprintf(fp, "\"id\": %ld,\n", this->getObjectId());
|
||||||
|
if (this->parent)
|
||||||
|
{
|
||||||
|
fprintf(fp, "\"parentId\": %ld,\n", this->parent->getObjectId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
SmoothTriangle::SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : Triangle(p1, p2, p3),
|
SmoothTriangle::SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : Triangle(p1, p2, p3),
|
||||||
n1(n1), n2(n2), n3(n3)
|
n1(n1), n2(n2), n3(n3)
|
||||||
{
|
{
|
||||||
this->type = SHAPE_SMOOTHTRIANGLE;
|
this->type = Shape::SMOOTHTRIANGLE;
|
||||||
stats.addSmoothTriangle();
|
stats.addSmoothTriangle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,4 +25,18 @@ Tuple SmoothTriangle::localNormalAt(Tuple point, Intersection *hit)
|
|||||||
return (this->n2 * hit->u +
|
return (this->n2 * hit->u +
|
||||||
this->n3 * hit->v +
|
this->n3 * hit->v +
|
||||||
this->n1 * (1 - hit->u - hit->v)).normalise();
|
this->n1 * (1 - hit->u - hit->v)).normalise();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmoothTriangle::dumpMe(FILE *fp)
|
||||||
|
{
|
||||||
|
Tuple t = this->n1;
|
||||||
|
fprintf(fp, "\"n1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
|
t.x, t.y, t.z);
|
||||||
|
t = this->n2;
|
||||||
|
fprintf(fp, "\"n2\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
|
t.x, t.y, t.z);
|
||||||
|
t = this->n3;
|
||||||
|
fprintf(fp, "\"n3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
|
t.x, t.y, t.z);
|
||||||
|
Triangle::dumpMe(fp);
|
||||||
}
|
}
|
||||||
@@ -13,9 +13,8 @@
|
|||||||
#include <tuple.h>
|
#include <tuple.h>
|
||||||
#include <intersect.h>
|
#include <intersect.h>
|
||||||
|
|
||||||
Intersect Sphere::localIntersect(Ray r)
|
void Sphere::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
|
||||||
double a, b, c, discriminant;
|
double a, b, c, discriminant;
|
||||||
|
|
||||||
Tuple sphere_to_ray = r.origin - Point(0, 0, 0);
|
Tuple sphere_to_ray = r.origin - Point(0, 0, 0);
|
||||||
@@ -28,11 +27,9 @@ Intersect Sphere::localIntersect(Ray r)
|
|||||||
|
|
||||||
if (discriminant >= 0)
|
if (discriminant >= 0)
|
||||||
{
|
{
|
||||||
ret.add(Intersection((-b - sqrt(discriminant)) / (2 * a), this));
|
xs.add(Intersection((-b - sqrt(discriminant)) / (2 * a), this));
|
||||||
ret.add(Intersection((-b + sqrt(discriminant)) / (2 * a), this));
|
xs.add(Intersection((-b + sqrt(discriminant)) / (2 * a), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Sphere::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Sphere::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ TestShape::TestShape() : localRay(Point(0, 0, 0), Vector(0, 0, 0))
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect TestShape::localIntersect(Ray r)
|
void TestShape::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
this->localRay = r;
|
this->localRay = r;
|
||||||
return Intersect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple TestShape::localNormalAt(Tuple point, Intersection *hit)
|
Tuple TestShape::localNormalAt(Tuple point, Intersection *hit)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <math_helper.h>
|
#include <math_helper.h>
|
||||||
#include <renderstat.h>
|
#include <renderstat.h>
|
||||||
|
|
||||||
Triangle::Triangle(Point p1, Point p2, Point p3) : Shape(SHAPE_TRIANGLE), p1(p1), p2(p2), p3(p3)
|
Triangle::Triangle(Point p1, Point p2, Point p3) : Shape(Shape::TRIANGLE), p1(p1), p2(p2), p3(p3)
|
||||||
{
|
{
|
||||||
stats.addTriangle();
|
stats.addTriangle();
|
||||||
|
|
||||||
@@ -21,15 +21,13 @@ Triangle::Triangle(Point p1, Point p2, Point p3) : Shape(SHAPE_TRIANGLE), p1(p1)
|
|||||||
this->normal = e2.cross(e1).normalise();
|
this->normal = e2.cross(e1).normalise();
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect Triangle::localIntersect(Ray r)
|
void Triangle::localIntersect(Ray r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
|
||||||
|
|
||||||
Tuple dirCrossE2 = r.direction.cross(this->e2);
|
Tuple dirCrossE2 = r.direction.cross(this->e2);
|
||||||
double determinant = this->e1.dot(dirCrossE2);
|
double determinant = this->e1.dot(dirCrossE2);
|
||||||
if (fabs(determinant) < getEpsilon())
|
if (fabs(determinant) < getEpsilon())
|
||||||
{
|
{
|
||||||
return ret;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double f = 1.0 / determinant;
|
double f = 1.0 / determinant;
|
||||||
@@ -40,18 +38,16 @@ Intersect Triangle::localIntersect(Ray r)
|
|||||||
|
|
||||||
if ((u < 0) || (u > 1))
|
if ((u < 0) || (u > 1))
|
||||||
{
|
{
|
||||||
return ret;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((v < 0) || ((u + v) > 1))
|
if ((v < 0) || ((u + v) > 1))
|
||||||
{
|
{
|
||||||
return ret;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double t = f * this->e2.dot(originCrossE1);
|
double t = f * this->e2.dot(originCrossE1);
|
||||||
ret.add(Intersection(t, this, u, v));
|
xs.add(Intersection(t, this, u, v));
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple Triangle::localNormalAt(Tuple point, Intersection *hit)
|
Tuple Triangle::localNormalAt(Tuple point, Intersection *hit)
|
||||||
@@ -73,6 +69,8 @@ BoundingBox Triangle::getLocalBounds()
|
|||||||
void Triangle::dumpMe(FILE *fp)
|
void Triangle::dumpMe(FILE *fp)
|
||||||
{
|
{
|
||||||
fprintf(fp, "\"Type\": \"Triangle\",\n");
|
fprintf(fp, "\"Type\": \"Triangle\",\n");
|
||||||
|
|
||||||
|
/* World points*/
|
||||||
Tuple t = this->transformMatrix * this->p1;
|
Tuple t = this->transformMatrix * this->p1;
|
||||||
fprintf(fp, "\"p1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
fprintf(fp, "\"p1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
t.x, t.y, t.z);
|
t.x, t.y, t.z);
|
||||||
@@ -82,5 +80,16 @@ void Triangle::dumpMe(FILE *fp)
|
|||||||
t = this->transformMatrix * this->p3;
|
t = this->transformMatrix * this->p3;
|
||||||
fprintf(fp, "\"p3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
fprintf(fp, "\"p3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
t.x, t.y, t.z);
|
t.x, t.y, t.z);
|
||||||
|
|
||||||
|
/* Local points */
|
||||||
|
t = this->p1;
|
||||||
|
fprintf(fp, "\"lp1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
|
t.x, t.y, t.z);
|
||||||
|
t = this->p2;
|
||||||
|
fprintf(fp, "\"lp2\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
|
t.x, t.y, t.z);
|
||||||
|
t = this->p3;
|
||||||
|
fprintf(fp, "\"lp3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
|
||||||
|
t.x, t.y, t.z);
|
||||||
Shape::dumpMe(fp);
|
Shape::dumpMe(fp);
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ Tuple Tuple::normalise()
|
|||||||
|
|
||||||
return Tuple(this->x / mag, this->y / mag, this->z / mag, this->w / mag);
|
return Tuple(this->x / mag, this->y / mag, this->z / mag, this->w / mag);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
double Tuple::dot(const Tuple &b)
|
double Tuple::dot(const Tuple &b)
|
||||||
{
|
{
|
||||||
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
|
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
|
||||||
@@ -39,30 +39,12 @@ Tuple Tuple::cross(const Tuple &b) const
|
|||||||
this->x * b.y - this->y * b.x,
|
this->x * b.y - this->y * b.x,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
Tuple Tuple::reflect(const Tuple &normal)
|
Tuple Tuple::reflect(const Tuple &normal)
|
||||||
{
|
{
|
||||||
return *this - normal * 2 * this->dot(normal);
|
return *this - normal * 2 * this->dot(normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tuple::fixPoint()
|
|
||||||
{
|
|
||||||
if (isnan(this->x) || isnan(this->y) || isnan(this->z))
|
|
||||||
{
|
|
||||||
/* w is probably broken, so fix it */
|
|
||||||
this->w = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tuple::fixVector()
|
|
||||||
{
|
|
||||||
if (isnan(this->x) || isnan(this->y) || isnan(this->z))
|
|
||||||
{
|
|
||||||
/* w is probably broken, so fix it */
|
|
||||||
this->w = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Tuple::isRepresentable()
|
bool Tuple::isRepresentable()
|
||||||
{
|
{
|
||||||
return !(isnan(this->x) || isnan(this->y) || isnan(this->z) ||
|
return !(isnan(this->x) || isnan(this->y) || isnan(this->z) ||
|
||||||
|
|||||||
@@ -22,16 +22,12 @@ extern "C" {
|
|||||||
|
|
||||||
#define MIN_ALLOC (2)
|
#define MIN_ALLOC (2)
|
||||||
|
|
||||||
World::World() : objectCount(0), lightCount(0)
|
World::World() : lightCount(0), worldGroup("World")
|
||||||
{
|
{
|
||||||
this->allocatedLightCount = MIN_ALLOC;
|
this->allocatedLightCount = MIN_ALLOC;
|
||||||
this->lightList = (Light **)calloc(sizeof(Light *), MIN_ALLOC);
|
this->lightList = (Light **)calloc(sizeof(Light *), MIN_ALLOC);
|
||||||
this->lightCount = 0;
|
this->lightCount = 0;
|
||||||
|
|
||||||
this->allocatedObjectCount = MIN_ALLOC;
|
|
||||||
this->objectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC);
|
|
||||||
this->objectCount = 0;
|
|
||||||
|
|
||||||
#ifdef ENABLE_LUA_SUPPORT
|
#ifdef ENABLE_LUA_SUPPORT
|
||||||
this->L = luaL_newstate(); /* opens Lua */
|
this->L = luaL_newstate(); /* opens Lua */
|
||||||
luaL_openlibs(L); /* opens the basic library */
|
luaL_openlibs(L); /* opens the basic library */
|
||||||
@@ -45,12 +41,10 @@ World::~World()
|
|||||||
|
|
||||||
void World::addObject(Shape *s)
|
void World::addObject(Shape *s)
|
||||||
{
|
{
|
||||||
if ((this->objectCount + 1) > this->allocatedObjectCount)
|
/* Cheaty but need to be done for now */
|
||||||
{
|
s->materialSet = true;
|
||||||
this->allocatedObjectCount *= 2;
|
|
||||||
this->objectList = (Shape **)realloc(this->objectList, sizeof(Shape **) * this->allocatedObjectCount);
|
this->worldGroup.addObject(s);
|
||||||
}
|
|
||||||
this->objectList[this->objectCount++] = s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::addLight(Light *l)
|
void World::addLight(Light *l)
|
||||||
@@ -78,33 +72,12 @@ bool World::lightIsIn(Light &l)
|
|||||||
|
|
||||||
bool World::objectIsIn(Shape &s)
|
bool World::objectIsIn(Shape &s)
|
||||||
{
|
{
|
||||||
int i;
|
return this->worldGroup.includes(&s);
|
||||||
for(i = 0; i < this->objectCount; i++)
|
|
||||||
{
|
|
||||||
if (*this->objectList[i] == s)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersect World::intersect(Ray r)
|
void World::intersect(Ray &r, Intersect &xs)
|
||||||
{
|
{
|
||||||
Intersect ret;
|
this->worldGroup.intersect(r, xs);
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for(i = 0; i < this->objectCount; i++)
|
|
||||||
{
|
|
||||||
Intersect xs = this->objectList[i]->intersect(r);
|
|
||||||
|
|
||||||
for(j = 0; j < xs.count(); j++)
|
|
||||||
{
|
|
||||||
ret.add(xs[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple World::shadeHit(Computation comps, uint32_t depthCount)
|
Tuple World::shadeHit(Computation comps, uint32_t depthCount)
|
||||||
@@ -135,7 +108,8 @@ Tuple World::shadeHit(Computation comps, uint32_t depthCount)
|
|||||||
|
|
||||||
Tuple World::colourAt(Ray r, uint32_t depthCount)
|
Tuple World::colourAt(Ray r, uint32_t depthCount)
|
||||||
{
|
{
|
||||||
Intersect allHits = this->intersect(r);
|
Intersect allHits;
|
||||||
|
this->intersect(r, allHits);
|
||||||
Intersection hit = allHits.hit();
|
Intersection hit = allHits.hit();
|
||||||
|
|
||||||
stats.setMaxDepth(depthCount);
|
stats.setMaxDepth(depthCount);
|
||||||
@@ -158,7 +132,8 @@ bool World::isShadowed(Tuple point, Tuple lightPosition)
|
|||||||
|
|
||||||
Ray r = Ray(point, direction);
|
Ray r = Ray(point, direction);
|
||||||
stats.addLightRay();
|
stats.addLightRay();
|
||||||
Intersect xs = this->intersect(r);
|
Intersect xs;
|
||||||
|
this->intersect(r, xs);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < xs.count(); i++)
|
for(i = 0; i < xs.count(); i++)
|
||||||
@@ -214,6 +189,16 @@ Colour World::refractedColour(Computation comps, uint32_t depthCount)
|
|||||||
return Colour(hitColour.x, hitColour.y, hitColour.z);
|
return Colour(hitColour.x, hitColour.y, hitColour.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::finalise(WorldOptimiser &opt)
|
||||||
|
{
|
||||||
|
/* First lock everything */
|
||||||
|
this->worldGroup.lock();
|
||||||
|
|
||||||
|
/* Now run the optimiser */
|
||||||
|
opt.setRoot(&this->worldGroup);
|
||||||
|
opt.run();
|
||||||
|
}
|
||||||
|
|
||||||
void World::dumpMe(FILE *fp)
|
void World::dumpMe(FILE *fp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -230,12 +215,7 @@ void World::dumpMe(FILE *fp)
|
|||||||
fprintf(fp, "},\n");
|
fprintf(fp, "},\n");
|
||||||
|
|
||||||
fprintf(fp, "\"Objects\": {\n");
|
fprintf(fp, "\"Objects\": {\n");
|
||||||
for(i = 0; i < this->objectCount; i++)
|
this->worldGroup.dumpMe(fp);
|
||||||
{
|
|
||||||
fprintf(fp, "\"%d\": {\n", i);
|
|
||||||
this->objectList[i]->dumpMe(fp);
|
|
||||||
fprintf(fp, "},\n");
|
|
||||||
}
|
|
||||||
fprintf(fp, "},\n");
|
fprintf(fp, "},\n");
|
||||||
|
|
||||||
/* JSON Closing */
|
/* JSON Closing */
|
||||||
|
|||||||
124
source/worldoptimiser.cpp
Normal file
124
source/worldoptimiser.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* WorldOptimiser implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <shape.h>
|
||||||
|
#include <group.h>
|
||||||
|
#include <objfile.h>
|
||||||
|
#include <world.h>
|
||||||
|
#include <worldoptimiser.h>
|
||||||
|
|
||||||
|
/* This function is meant to move all infinite object to the root group */
|
||||||
|
void WorldOptimiser::moveInfiniteObjects(Shape *s)
|
||||||
|
{
|
||||||
|
if (s == nullptr)
|
||||||
|
{
|
||||||
|
s = this->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->getType() == Shape::OBJFILE)
|
||||||
|
{
|
||||||
|
/* Special case */
|
||||||
|
OBJFile *obj = (OBJFile *)s;
|
||||||
|
s = obj->getBaseGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (s->getType() == Shape::GROUP)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Group *grp = (Group *)s;
|
||||||
|
|
||||||
|
if (grp->getUnboxableCount() > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < grp->getUnboxableCount(); i++)
|
||||||
|
{
|
||||||
|
Shape *shp = grp->getUnboxable(i);
|
||||||
|
|
||||||
|
if (this->root != s)
|
||||||
|
{
|
||||||
|
if (shp->getType() == Shape::GROUP)
|
||||||
|
{
|
||||||
|
/* Issue a warning if it is a group */
|
||||||
|
printf("WARNING: The group '%s' in '%s' have infinite bounds, all items part of it will not be optimised."
|
||||||
|
"That may affect performances!",
|
||||||
|
((Group *)shp)->getName(),
|
||||||
|
grp->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
this->root->addObject(shp);
|
||||||
|
grp->removeObject(shp);
|
||||||
|
|
||||||
|
/* We remove an object from that list, so need to do some stuffs. */
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now let's traverse the rest of that group */
|
||||||
|
if (grp->getObjectCount() > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < grp->getObjectCount(); i++)
|
||||||
|
{
|
||||||
|
Shape *shp = grp->getObject(i);
|
||||||
|
this->moveInfiniteObjects(shp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If it is not a group, there is nothing to be done there. */
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldOptimiser::moveAllObjects(Shape *s)
|
||||||
|
{
|
||||||
|
if (s == nullptr)
|
||||||
|
{
|
||||||
|
s = this->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->getType() == Shape::OBJFILE)
|
||||||
|
{
|
||||||
|
/* Special case */
|
||||||
|
OBJFile *obj = (OBJFile *)s;
|
||||||
|
s = obj->getBaseGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We should be here only when it is a group, but better being safe. */
|
||||||
|
if (s->getType() == Shape::GROUP)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Group *grp = (Group *)s;
|
||||||
|
|
||||||
|
/* Now let's traverse the rest of that group */
|
||||||
|
if (grp->getObjectCount() > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < grp->getObjectCount(); i++)
|
||||||
|
{
|
||||||
|
Shape *shp = grp->getObject(i);
|
||||||
|
switch(shp->getType())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
/* Don't move if we are on the same leaf */
|
||||||
|
if (this->root != s)
|
||||||
|
{
|
||||||
|
/* It is not a group type object so, move it! */
|
||||||
|
this->root->addObject(shp);
|
||||||
|
grp->removeObject(shp);
|
||||||
|
|
||||||
|
/* We remove an object from that list, so need to do some stuffs. */
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Shape::GROUP:
|
||||||
|
case Shape::OBJFILE:
|
||||||
|
this->moveAllObjects(shp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If it is not a group, there is nothing to be done there. */
|
||||||
|
}
|
||||||
144
source/worldoptimiser/bvhoptimisation.cpp
Normal file
144
source/worldoptimiser/bvhoptimisation.cpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* BVH world optimiser implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <worldoptimiser.h>
|
||||||
|
#include <cube.h>
|
||||||
|
#include <objfile.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
|
||||||
|
void BVHOptimisation::makeTree(Group *leaf, int depth)
|
||||||
|
{
|
||||||
|
/* Let's take the bounding box of the root */
|
||||||
|
BoundingBox rootBB = leaf->getBounds();
|
||||||
|
|
||||||
|
double dx = (rootBB.max.x - rootBB.min.x);
|
||||||
|
double dy = (rootBB.max.y - rootBB.min.y);
|
||||||
|
double dz = (rootBB.max.z - rootBB.min.z);
|
||||||
|
/* Take the mid value for each axes */
|
||||||
|
Tuple midMin = rootBB.min;
|
||||||
|
Tuple midMax = rootBB.max;
|
||||||
|
|
||||||
|
BoundingBox SlicesBB[2];
|
||||||
|
int sliceIdx;
|
||||||
|
Group *Slices[2];
|
||||||
|
|
||||||
|
double largestSide = max3(dx, dy, dz);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (largestSide == dx)
|
||||||
|
{
|
||||||
|
midMin.x = rootBB.min.x + dx / 2.0;
|
||||||
|
midMax.x = rootBB.min.x + dx / 2.0;
|
||||||
|
}
|
||||||
|
else if (largestSide == dy)
|
||||||
|
{
|
||||||
|
midMin.y = rootBB.min.y + dy / 2.0;
|
||||||
|
midMax.y = rootBB.min.y + dy / 2.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
midMin.z = rootBB.min.z + dx / 2.0;
|
||||||
|
midMax.z = rootBB.min.z + dx / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Split the main bounding box into 8 boxes */
|
||||||
|
SlicesBB[0] | rootBB.min;
|
||||||
|
SlicesBB[0] | midMax;
|
||||||
|
|
||||||
|
SlicesBB[1] | rootBB.max;
|
||||||
|
SlicesBB[1] | midMin;
|
||||||
|
|
||||||
|
|
||||||
|
for (sliceIdx = 0 ; sliceIdx < 2 ; sliceIdx++)
|
||||||
|
{
|
||||||
|
Slices[sliceIdx] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < leaf->getObjectCount(); i++)
|
||||||
|
{
|
||||||
|
Shape *shp = leaf->getObject(i);
|
||||||
|
BoundingBox objBB = shp->getBounds();
|
||||||
|
|
||||||
|
for (sliceIdx = 0 ; sliceIdx < 2 ; sliceIdx++)
|
||||||
|
{
|
||||||
|
if (SlicesBB[sliceIdx].fitsIn(objBB))
|
||||||
|
{
|
||||||
|
if (Slices[sliceIdx] == nullptr)
|
||||||
|
{
|
||||||
|
char name[32];
|
||||||
|
snprintf(name, 32, "%d_Slice %d", depth, sliceIdx);
|
||||||
|
Slices[sliceIdx] = new Group(name);
|
||||||
|
|
||||||
|
Slices[sliceIdx]->setBounds(SlicesBB[sliceIdx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slices[sliceIdx]->addObject(shp);
|
||||||
|
leaf->removeObject(shp);
|
||||||
|
|
||||||
|
i -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shp->getType() == Shape::GROUP)
|
||||||
|
{
|
||||||
|
this->makeTree((Group *)shp, depth + 1);
|
||||||
|
}
|
||||||
|
else if (shp->getType() == Shape::OBJFILE)
|
||||||
|
{
|
||||||
|
this->makeTree((Group *)((OBJFile *)shp)->getBaseGroup(), depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now add the quadrant to the root and recurse in it */
|
||||||
|
for (sliceIdx = 0 ; sliceIdx < 2 ; sliceIdx++)
|
||||||
|
{
|
||||||
|
if (Slices[sliceIdx] != nullptr)
|
||||||
|
{
|
||||||
|
this->makeTree(Slices[sliceIdx], depth + 1);
|
||||||
|
|
||||||
|
Slices[sliceIdx]->updateBoundingBox();
|
||||||
|
|
||||||
|
leaf->addObject(Slices[sliceIdx]);
|
||||||
|
#if 0
|
||||||
|
Cube *cb = new Cube();
|
||||||
|
double sx = SlicesBB[sliceIdx].max.x - SlicesBB[sliceIdx].min.x;
|
||||||
|
double sy = SlicesBB[sliceIdx].max.y - SlicesBB[sliceIdx].min.y;
|
||||||
|
double sz = SlicesBB[sliceIdx].max.z - SlicesBB[sliceIdx].min.z;
|
||||||
|
|
||||||
|
cb->setTransform(translation(SlicesBB[sliceIdx].min.x, SlicesBB[sliceIdx].min.y,
|
||||||
|
SlicesBB[sliceIdx].min.z) * scaling(sx, sy, sz));
|
||||||
|
cb->material.colour = Colour(0.01, 0.01, 0);
|
||||||
|
cb->materialSet = true;
|
||||||
|
cb->dropShadow = false;
|
||||||
|
cb->material.ambient = 0.1;
|
||||||
|
cb->material.reflective = 0;
|
||||||
|
cb->material.transparency = 0.95;
|
||||||
|
cb->material.refractiveIndex = 1;
|
||||||
|
cb->material.specular = 0;
|
||||||
|
leaf->addObject(cb);
|
||||||
|
|
||||||
|
printf("%s: %d objs\n", Slices[sliceIdx]->getName(),
|
||||||
|
Slices[sliceIdx]->getObjectCount());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BVHOptimisation::run()
|
||||||
|
{
|
||||||
|
/* First let's clear our hands */
|
||||||
|
this->moveInfiniteObjects();
|
||||||
|
|
||||||
|
/* Then let's have some fun! */
|
||||||
|
//this->moveAllObjects();
|
||||||
|
|
||||||
|
/* Now.. The fun start ! */
|
||||||
|
makeTree(this->root, 0);
|
||||||
|
}
|
||||||
141
source/worldoptimiser/octreeoptimisation.cpp
Normal file
141
source/worldoptimiser/octreeoptimisation.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Octree world optimiser implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <worldoptimiser.h>
|
||||||
|
#include <cube.h>
|
||||||
|
#include <objfile.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
|
||||||
|
void OctreeOptimisation::makeTree(Group *leaf, int depth)
|
||||||
|
{
|
||||||
|
/* Let's take the bounding box of the root */
|
||||||
|
BoundingBox rootBB = leaf->getBounds();
|
||||||
|
|
||||||
|
/* Take the mid value for each axes */
|
||||||
|
double midX = (rootBB.max.x - rootBB.min.x) / 2.0 + rootBB.min.x;
|
||||||
|
double midY = (rootBB.max.y - rootBB.min.y) / 2.0 + rootBB.min.y;
|
||||||
|
double midZ = (rootBB.max.z - rootBB.min.z) / 2.0 + rootBB.min.z;
|
||||||
|
BoundingBox octantBB[8];
|
||||||
|
int octantIdx;
|
||||||
|
Group *octants[8];
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
|
||||||
|
/* Split the main bounding box into 8 boxes */
|
||||||
|
octantBB[0] | Point(rootBB.min.x, rootBB.min.y, rootBB.min.z);
|
||||||
|
octantBB[0] | Point(midX, midY, midZ);
|
||||||
|
|
||||||
|
octantBB[1] | Point(midX, rootBB.min.y, rootBB.min.z);
|
||||||
|
octantBB[1] | Point(rootBB.max.x, midY, midZ);
|
||||||
|
|
||||||
|
octantBB[2] | Point(rootBB.min.x, midY, rootBB.min.z);
|
||||||
|
octantBB[2] | Point(midX, rootBB.max.y, midZ);
|
||||||
|
|
||||||
|
octantBB[3] | Point(midX, midY, rootBB.min.z);
|
||||||
|
octantBB[3] | Point(rootBB.max.x, rootBB.max.y, midZ);
|
||||||
|
|
||||||
|
octantBB[4] | Point(rootBB.min.x, midY, midZ);
|
||||||
|
octantBB[4] | Point(midX, rootBB.max.y, rootBB.max.z);
|
||||||
|
|
||||||
|
octantBB[5] | Point(midX, midY, midZ);
|
||||||
|
octantBB[5] | Point(rootBB.max.x, rootBB.max.y, rootBB.max.z);
|
||||||
|
|
||||||
|
octantBB[6] | Point(rootBB.min.x, rootBB.min.y, midZ);
|
||||||
|
octantBB[6] | Point(midX, midY, rootBB.max.z);
|
||||||
|
|
||||||
|
octantBB[7] | Point(midX, rootBB.min.y, midZ);
|
||||||
|
octantBB[7] | Point(rootBB.max.x, midY, rootBB.max.z);
|
||||||
|
|
||||||
|
for (octantIdx = 0 ; octantIdx < 8 ; octantIdx++)
|
||||||
|
{
|
||||||
|
octants[octantIdx] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < leaf->getObjectCount(); i++)
|
||||||
|
{
|
||||||
|
Shape *shp = leaf->getObject(i);
|
||||||
|
|
||||||
|
BoundingBox objBB = shp->getBounds();
|
||||||
|
//if ((shp->getType() != Shape::GROUP) && (shp->getType() != Shape::OBJFILE))
|
||||||
|
for (octantIdx = 0 ; octantIdx < 8 ; octantIdx++)
|
||||||
|
{
|
||||||
|
if (octantBB[octantIdx].fitsIn(objBB))
|
||||||
|
{
|
||||||
|
if (octants[octantIdx] == nullptr)
|
||||||
|
{
|
||||||
|
char name[32];
|
||||||
|
snprintf(name, 32, "%d_Quadrant %d", depth, octantIdx);
|
||||||
|
octants[octantIdx] = new Group(name);
|
||||||
|
|
||||||
|
octants[octantIdx]->setBounds(octantBB[octantIdx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
octants[octantIdx]->addObject(shp);
|
||||||
|
leaf->removeObject(shp);
|
||||||
|
|
||||||
|
i -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shp->getType() == Shape::GROUP)
|
||||||
|
{
|
||||||
|
this->makeTree((Group *)shp, depth + 1);
|
||||||
|
}
|
||||||
|
if (shp->getType() == Shape::OBJFILE)
|
||||||
|
{
|
||||||
|
this->makeTree((Group *)((OBJFile *)shp)->getBaseGroup(), depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now add the quadrant to the root and recurse in it */
|
||||||
|
for (octantIdx = 0 ; octantIdx < 8 ; octantIdx++)
|
||||||
|
{
|
||||||
|
if (octants[octantIdx] != nullptr)
|
||||||
|
{
|
||||||
|
this->makeTree(octants[octantIdx], depth + 1);
|
||||||
|
|
||||||
|
octants[octantIdx]->updateBoundingBox();
|
||||||
|
|
||||||
|
leaf->addObject(octants[octantIdx]);
|
||||||
|
#if 0
|
||||||
|
Cube *cb = new Cube();
|
||||||
|
double sx = octantBB[octantIdx].max.x - octantBB[octantIdx].min.x;
|
||||||
|
double sy = octantBB[octantIdx].max.y - octantBB[octantIdx].min.y;
|
||||||
|
double sz = octantBB[octantIdx].max.z - octantBB[octantIdx].min.z;
|
||||||
|
|
||||||
|
cb->setTransform(translation(octantBB[octantIdx].min.x, octantBB[octantIdx].min.y,
|
||||||
|
octantBB[octantIdx].min.z) * scaling(sx, sy, sz));
|
||||||
|
cb->material.colour = Colour(0.01, 0.01, 0);
|
||||||
|
cb->materialSet = true;
|
||||||
|
cb->dropShadow = false;
|
||||||
|
cb->material.ambient = 0.1;
|
||||||
|
cb->material.reflective = 0;
|
||||||
|
cb->material.transparency = 0.95;
|
||||||
|
cb->material.refractiveIndex = 1;
|
||||||
|
cb->material.specular = 0;
|
||||||
|
leaf->addObject(cb);
|
||||||
|
|
||||||
|
printf("%s: %d objs\n", octants[octantIdx]->getName(),
|
||||||
|
octants[octantIdx]->getObjectCount());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeOptimisation::run()
|
||||||
|
{
|
||||||
|
/* First let's clear our hands */
|
||||||
|
this->moveInfiniteObjects();
|
||||||
|
|
||||||
|
/* Then let's have some fun! */
|
||||||
|
//this->moveAllObjects();
|
||||||
|
|
||||||
|
/* Now.. The fun start ! */
|
||||||
|
makeTree(this->root, 0);
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ link_libraries(rayonnement)
|
|||||||
set(TESTS_SRC math_test.cpp tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp
|
set(TESTS_SRC math_test.cpp tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp
|
||||||
ray_test.cpp intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
|
ray_test.cpp intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
|
||||||
shape_test.cpp plane_test.cpp pattern_test.cpp cube_test.cpp cylinder_test.cpp cone_test.cpp group_test.cpp
|
shape_test.cpp plane_test.cpp pattern_test.cpp cube_test.cpp cylinder_test.cpp cone_test.cpp group_test.cpp
|
||||||
boundingbox_test.cpp triangle_test.cpp sequence_test.cpp objfile_test.cpp smoothtriangle_test.cpp)
|
boundingbox_test.cpp triangle_test.cpp sequence_test.cpp objfile_test.cpp smoothtriangle_test.cpp csg_test.cpp)
|
||||||
|
|
||||||
add_executable(testMyRays)
|
add_executable(testMyRays)
|
||||||
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||||
@@ -81,6 +81,9 @@ add_custom_command(
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/
|
${CMAKE_CURRENT_BINARY_DIR}/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(ch16_test)
|
||||||
|
target_sources(ch16_test PRIVATE ch16_test.cpp)
|
||||||
|
|
||||||
add_executable(arealight_test)
|
add_executable(arealight_test)
|
||||||
target_sources(arealight_test PRIVATE arealight_test.cpp)
|
target_sources(arealight_test PRIVATE arealight_test.cpp)
|
||||||
|
|
||||||
@@ -125,7 +128,7 @@ file(DOWNLOAD
|
|||||||
)
|
)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET uvmap_skybox POST_BUILD
|
TARGET uvmap_skybox POST_BUILD
|
||||||
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/external/LancellottiChapel.zip -d LancellottiChapel
|
COMMAND unzip -qq -o ${CMAKE_SOURCE_DIR}/external/LancellottiChapel.zip -d LancellottiChapel
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external/
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external/
|
||||||
)
|
)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
@@ -135,6 +138,26 @@ add_custom_command(
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/
|
${CMAKE_CURRENT_BINARY_DIR}/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Dragon scene
|
||||||
|
add_executable(dragon_scene)
|
||||||
|
target_sources(dragon_scene PRIVATE dragon_scene.cpp)
|
||||||
|
file(DOWNLOAD
|
||||||
|
http://raytracerchallenge.com/bonus/assets/dragon.zip
|
||||||
|
${CMAKE_SOURCE_DIR}/external/dragon.zip
|
||||||
|
EXPECTED_HASH MD5=308b0f2aca1d48d24e6fc4584dfdf345
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET dragon_scene POST_BUILD
|
||||||
|
COMMAND unzip -qq -o ${CMAKE_SOURCE_DIR}/external/dragon.zip
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external/
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET dragon_scene POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy
|
||||||
|
${CMAKE_SOURCE_DIR}/external/dragon.obj
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/
|
||||||
|
)
|
||||||
|
|
||||||
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
|
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
|
||||||
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
|
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
|
||||||
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
|
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
|
||||||
@@ -148,6 +171,7 @@ add_test(NAME Chapter13_Test COMMAND $<TARGET_FILE:ch13_test>)
|
|||||||
add_test(NAME Chapter13_ConeBonus COMMAND $<TARGET_FILE:ch13_cone>)
|
add_test(NAME Chapter13_ConeBonus COMMAND $<TARGET_FILE:ch13_cone>)
|
||||||
add_test(NAME Chapter14_Test COMMAND $<TARGET_FILE:ch14_test>)
|
add_test(NAME Chapter14_Test COMMAND $<TARGET_FILE:ch14_test>)
|
||||||
add_test(NAME Chapter15_Teapots COMMAND $<TARGET_FILE:ch15_teapot_objfile>)
|
add_test(NAME Chapter15_Teapots COMMAND $<TARGET_FILE:ch15_teapot_objfile>)
|
||||||
|
add_test(NAME Chapter16_Test COMMAND $<TARGET_FILE:ch16_test>)
|
||||||
add_test(NAME AreaLight_Test COMMAND $<TARGET_FILE:arealight_test>)
|
add_test(NAME AreaLight_Test COMMAND $<TARGET_FILE:arealight_test>)
|
||||||
add_test(NAME UVMap_CheckeredSphere COMMAND $<TARGET_FILE:uvmap_checkeredsphere>)
|
add_test(NAME UVMap_CheckeredSphere COMMAND $<TARGET_FILE:uvmap_checkeredsphere>)
|
||||||
add_test(NAME UVMap_CheckeredPlane COMMAND $<TARGET_FILE:uvmap_checkeredplane>)
|
add_test(NAME UVMap_CheckeredPlane COMMAND $<TARGET_FILE:uvmap_checkeredplane>)
|
||||||
@@ -156,6 +180,7 @@ add_test(NAME UVMap_AlignCheckPlane COMMAND $<TARGET_FILE:uvmap_aligncheckplane>
|
|||||||
add_test(NAME UVMap_CheckeredCube COMMAND $<TARGET_FILE:uvmap_checkeredcube>)
|
add_test(NAME UVMap_CheckeredCube COMMAND $<TARGET_FILE:uvmap_checkeredcube>)
|
||||||
add_test(NAME UVMap_Earth COMMAND $<TARGET_FILE:uvmap_earth>)
|
add_test(NAME UVMap_Earth COMMAND $<TARGET_FILE:uvmap_earth>)
|
||||||
add_test(NAME UVMap_Skybox COMMAND $<TARGET_FILE:uvmap_skybox>)
|
add_test(NAME UVMap_Skybox COMMAND $<TARGET_FILE:uvmap_skybox>)
|
||||||
|
add_test(NAME Dragon_Sceme COMMAND $<TARGET_FILE:dragon_scene>)
|
||||||
add_test(NAME Test_Rendering COMMAND $<TARGET_FILE:test_render>)
|
add_test(NAME Test_Rendering COMMAND $<TARGET_FILE:test_render>)
|
||||||
add_test(NAME Triangle_RenderTest COMMAND $<TARGET_FILE:triangle_rendertest>)
|
add_test(NAME Triangle_RenderTest COMMAND $<TARGET_FILE:triangle_rendertest>)
|
||||||
add_test(NAME ChristmasBall_Rendering COMMAND $<TARGET_FILE:christmasball_render>)
|
add_test(NAME ChristmasBall_Rendering COMMAND $<TARGET_FILE:christmasball_render>)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <transformation.h>
|
#include <transformation.h>
|
||||||
|
|
||||||
|
#include <worldoptimiser.h>
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
World w = World();
|
World w = World();
|
||||||
@@ -198,6 +200,9 @@ int main()
|
|||||||
|
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
BVHOptimisation opt;
|
||||||
|
w.finalise(opt);
|
||||||
|
|
||||||
/* Set the camera */
|
/* Set the camera */
|
||||||
Camera camera = Camera(400, 200, 0.785);
|
Camera camera = Camera(400, 200, 0.785);
|
||||||
camera.setTransform(viewTransform(Point(8, 6, -8),
|
camera.setTransform(viewTransform(Point(8, 6, -8),
|
||||||
|
|||||||
@@ -36,47 +36,62 @@ int main()
|
|||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
|
|
||||||
/* Floor */
|
/* Floor */
|
||||||
Plane p = Plane();
|
Material planesMaterial = Material();
|
||||||
CheckersPattern checkered = CheckersPattern(Colour(0.35, 0.35, 0.35), Colour(0.4, 0.4, 0.4));
|
CheckersPattern checkered = CheckersPattern(Colour(0.35, 0.35, 0.35), Colour(0.4, 0.4, 0.4));
|
||||||
p.material.pattern = &checkered;
|
planesMaterial.pattern = &checkered;
|
||||||
p.material.ambient = 1;
|
planesMaterial.ambient = 1;
|
||||||
p.material.diffuse = 0;
|
planesMaterial.diffuse = 0;
|
||||||
p.material.specular = 0;
|
planesMaterial.specular = 0;
|
||||||
|
|
||||||
|
|
||||||
|
Plane p = Plane();
|
||||||
|
p.setMaterial(planesMaterial);
|
||||||
p.material.reflective = 0.1;
|
p.material.reflective = 0.1;
|
||||||
w.addObject(&p);
|
w.addObject(&p);
|
||||||
|
|
||||||
Plane p2 = Plane();
|
Plane p2 = Plane();
|
||||||
p2.setTransform(translation(0, 0, -10) * rotationX(M_PI/2));
|
p2.setTransform(translation(0, 0, -10) * rotationX(M_PI/2));
|
||||||
p2.material.pattern = &checkered;
|
p2.setMaterial(planesMaterial);
|
||||||
p2.material.ambient = 1;
|
|
||||||
p2.material.diffuse = 0;
|
|
||||||
p2.material.specular = 0;
|
|
||||||
w.addObject(&p2);
|
w.addObject(&p2);
|
||||||
|
|
||||||
|
Material lowPoly = Material();
|
||||||
|
lowPoly.colour = Colour(1, 0.3, 0.2);
|
||||||
|
lowPoly.shininess = 5;
|
||||||
|
lowPoly.specular = 0.4;
|
||||||
|
|
||||||
OBJFile teapot = OBJFile("teapot-low.obj");
|
OBJFile teapot = OBJFile("teapot-low.obj");
|
||||||
teapot.setTransform(translation(7, 0, 3) * rotationY(M_PI*23/22) * rotationX(-M_PI/2) * scaling(0.3, 0.3, 0.3));
|
teapot.setTransform(translation(7, 0, 3) * rotationY(M_PI*23/22) * rotationX(-M_PI/2) * scaling(0.3, 0.3, 0.3));
|
||||||
teapot.material.colour = Colour(1, 0.3, 0.2);
|
teapot.setMaterial(lowPoly);
|
||||||
teapot.material.shininess = 5;
|
w.addObject(teapot.getBaseGroup());
|
||||||
teapot.material.specular = 0.4;
|
|
||||||
w.addObject(&teapot);
|
FILE *fpOut = fopen("lowpoly_teapot.json", "wt");
|
||||||
|
if (fpOut)
|
||||||
|
{
|
||||||
|
teapot.dumpMe(fpOut);
|
||||||
|
fclose(fpOut);
|
||||||
|
}
|
||||||
|
|
||||||
OBJFile teapot2 = OBJFile("teapot-lowtri.obj");
|
OBJFile teapot2 = OBJFile("teapot-lowtri.obj");
|
||||||
teapot2.setTransform(translation(-7, 0, 3) * rotationY(-M_PI*46/22) * rotationX(-M_PI/2) * scaling(0.3, 0.3, 0.3));
|
teapot2.setTransform(translation(-7, 0, 3) * rotationY(-M_PI*46/22) * rotationX(-M_PI/2) * scaling(0.3, 0.3, 0.3));
|
||||||
teapot2.material.colour = Colour(1, 0.3, 0.2);
|
teapot2.setMaterial(lowPoly);;
|
||||||
teapot2.material.shininess = 5;
|
w.addObject(teapot2.getBaseGroup());
|
||||||
teapot2.material.specular = 0.4;
|
|
||||||
w.addObject(&teapot2);
|
Material highPoly = Material();
|
||||||
|
highPoly.colour = Colour(0.3, 1, 0.2);
|
||||||
|
highPoly.shininess = 5;
|
||||||
|
highPoly.specular = 0.4;
|
||||||
|
highPoly.reflective = 0.5;
|
||||||
|
|
||||||
OBJFile teapot3= OBJFile("teapot.obj");
|
OBJFile teapot3= OBJFile("teapot.obj");
|
||||||
teapot3.setTransform(translation(0, 0, -5) * rotationY(-M_PI) * rotationX(-M_PI/2) * scaling(0.4, 0.4, 0.4));
|
teapot3.setTransform(translation(0, 0, -5) * rotationY(-M_PI) * rotationX(-M_PI/2) * scaling(0.4, 0.4, 0.4));
|
||||||
teapot3.material.colour = Colour(0.3, 1, 0.2);
|
teapot3.setMaterial(highPoly);
|
||||||
teapot3.material.shininess = 5;
|
w.addObject(teapot3.getBaseGroup());
|
||||||
teapot3.material.specular = 0.4;
|
|
||||||
teapot3.material.reflective = 0.5;
|
|
||||||
w.addObject(&teapot3);
|
|
||||||
|
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
BVHOptimisation opt;
|
||||||
|
w.finalise(opt);
|
||||||
|
|
||||||
/* Set the camera */
|
/* Set the camera */
|
||||||
Camera camera = Camera(80, 40, M_PI/2);
|
Camera camera = Camera(80, 40, M_PI/2);
|
||||||
camera.setTransform(viewTransform(Point(0, 7, 13),
|
camera.setTransform(viewTransform(Point(0, 7, 13),
|
||||||
|
|||||||
194
tests/ch16_test.cpp
Normal file
194
tests/ch16_test.cpp
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Render test for CSG in chapter 16.
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <world.h>
|
||||||
|
#include <light.h>
|
||||||
|
#include <sphere.h>
|
||||||
|
#include <plane.h>
|
||||||
|
#include <cube.h>
|
||||||
|
#include <cylinder.h>
|
||||||
|
#include <material.h>
|
||||||
|
#include <colour.h>
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <camera.h>
|
||||||
|
#include <group.h>
|
||||||
|
#include <cone.h>
|
||||||
|
#include <csg.h>
|
||||||
|
|
||||||
|
#include <pattern.h>
|
||||||
|
#include <strippattern.h>
|
||||||
|
#include <gradientpattern.h>
|
||||||
|
#include <checkerspattern.h>
|
||||||
|
#include <ringpattern.h>
|
||||||
|
|
||||||
|
#include <transformation.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
World w = World();
|
||||||
|
|
||||||
|
/* Add lights */
|
||||||
|
Light light1 = Light(POINT_LIGHT, Point(6, 10, 10), Colour(0.5, 0.4, 0.5));
|
||||||
|
w.addLight(&light1);
|
||||||
|
|
||||||
|
/* Add lights */
|
||||||
|
Light light2 = Light(POINT_LIGHT, Point(6, 10, -2.5), Colour(0.5, 0.6, 0.5));
|
||||||
|
w.addLight(&light2);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
/* Floor */
|
||||||
|
Plane p = Plane();
|
||||||
|
CheckersPattern checkered = CheckersPattern(Colour(0.35, 0.35, 0.35), Colour(0.4, 0.4, 0.4));
|
||||||
|
p.material.pattern = &checkered;
|
||||||
|
p.material.ambient = 0.2;
|
||||||
|
p.material.diffuse = 1;
|
||||||
|
p.material.specular = 0;
|
||||||
|
p.material.reflective = 0.1;
|
||||||
|
p.setTransform(translation(0, 0, 0));
|
||||||
|
w.addObject(&p);
|
||||||
|
|
||||||
|
Plane p2 = Plane();
|
||||||
|
p2.setTransform(translation(0, 0, -3) * rotationX(M_PI/2));
|
||||||
|
p2.material.pattern = &checkered;
|
||||||
|
p2.material.ambient = 0.2;
|
||||||
|
p2.material.diffuse = 1;
|
||||||
|
p2.material.specular = 0;
|
||||||
|
w.addObject(&p2);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
/* Funky cube */
|
||||||
|
Cylinder c1 = Cylinder();
|
||||||
|
c1.minCap = -2;
|
||||||
|
c1.maxCap = 2;
|
||||||
|
c1.isClosed = true;
|
||||||
|
c1.material.colour = Colour(1, 0, 0);
|
||||||
|
c1.setTransform(scaling(0.4, 1, 0.4));
|
||||||
|
c1.materialSet = true;
|
||||||
|
|
||||||
|
Cylinder c2 = Cylinder();
|
||||||
|
c2.minCap = -2;
|
||||||
|
c2.maxCap = 2;
|
||||||
|
c2.isClosed = true;
|
||||||
|
c2.material.colour = Colour(0, 1, 0);
|
||||||
|
c2.setTransform(rotationX(M_PI/2) * scaling(0.4, 1, 0.4));
|
||||||
|
c2.materialSet = true;
|
||||||
|
|
||||||
|
CSG leaf1 = CSG(CSG::UNION, &c1, &c2);
|
||||||
|
|
||||||
|
Cylinder c3 = Cylinder();
|
||||||
|
c3.minCap = -2;
|
||||||
|
c3.maxCap = 2;
|
||||||
|
c3.isClosed = true;
|
||||||
|
c3.material.colour = Colour(0, 0, 1);
|
||||||
|
c3.setTransform(rotationZ(M_PI/2) * scaling(0.4, 1, 0.4));
|
||||||
|
c3.materialSet = true;
|
||||||
|
|
||||||
|
CSG leaf2 = CSG(CSG::UNION, &leaf1, &c3);
|
||||||
|
|
||||||
|
Cube cb = Cube();
|
||||||
|
cb.materialSet = true;
|
||||||
|
cb.material.reflective = 0.5;
|
||||||
|
cb.material.colour = Colour(0.3, 0.3, 0.3);
|
||||||
|
cb.material.ambient = 0;
|
||||||
|
cb.material.diffuse = 0.3;
|
||||||
|
cb.material.specular = 0.3;
|
||||||
|
cb.material.shininess = 20;
|
||||||
|
|
||||||
|
Sphere sp = Sphere();
|
||||||
|
sp.setTransform(scaling(1.35, 1.35, 1.35));
|
||||||
|
sp.materialSet = true;
|
||||||
|
sp.material.colour = Colour(0, 0, 0);
|
||||||
|
sp.material.ambient = 0;
|
||||||
|
sp.material.specular = 0.3;
|
||||||
|
sp.material.shininess = 20;
|
||||||
|
sp.material.reflective = 0.05;
|
||||||
|
sp.material.diffuse = 0.3;
|
||||||
|
CSG leaf3 = CSG(CSG::INTERSECTION, &sp, &cb);
|
||||||
|
|
||||||
|
CSG leaf4 = CSG(CSG::DIFFERENCE, &leaf3, &leaf2);
|
||||||
|
leaf4.setTransform(translation(0, 1, 0.8) * rotationY(-0.45));
|
||||||
|
w.addObject(&leaf4);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
/* Tricylinder weirdy */
|
||||||
|
Cylinder sp1 = Cylinder();
|
||||||
|
sp1.minCap = -2;
|
||||||
|
sp1.maxCap = 2;
|
||||||
|
sp1.isClosed = true;
|
||||||
|
sp1.materialSet = true;
|
||||||
|
sp1.material.colour = Colour(1, 0, 0);
|
||||||
|
Cylinder sp2 = Cylinder();
|
||||||
|
sp2.minCap = -2;
|
||||||
|
sp2.maxCap = 2;
|
||||||
|
sp2.isClosed = true;
|
||||||
|
sp2.materialSet = true;
|
||||||
|
sp2.setTransform(rotationX(M_PI/2));
|
||||||
|
sp2.material.colour = Colour(0, 1, 0);
|
||||||
|
Cylinder sp3 = Cylinder();
|
||||||
|
sp3.minCap = -2;
|
||||||
|
sp3.maxCap = 2;
|
||||||
|
sp3.isClosed = true;
|
||||||
|
sp3.materialSet = true;
|
||||||
|
sp3.setTransform(rotationZ(M_PI/2));
|
||||||
|
sp3.material.colour = Colour(0, 0, 1);
|
||||||
|
|
||||||
|
CSG spleaf1 = CSG(CSG::INTERSECTION, &sp1, &sp2);
|
||||||
|
CSG spleaf2 = CSG(CSG::INTERSECTION, &spleaf1, &sp3);
|
||||||
|
|
||||||
|
spleaf2.setTransform(translation(4, 1, -0.1) * rotationY(0.35));
|
||||||
|
w.addObject(&spleaf2);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
Group grp = Group();
|
||||||
|
int i;
|
||||||
|
#define SLICE_NUM (12)
|
||||||
|
for(i = 0; i < SLICE_NUM; i++)
|
||||||
|
{
|
||||||
|
Cube *c = new Cube();
|
||||||
|
|
||||||
|
c->setTransform(rotationY((2*M_PI / SLICE_NUM) * i) * scaling(0.1, 1.1, 0.7) * translation(0, 0, 0.9));
|
||||||
|
c->dropShadow = false;
|
||||||
|
grp.addObject(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
grp.materialSet = true;
|
||||||
|
grp.dropShadow = false;
|
||||||
|
grp.material.ambient = 0;
|
||||||
|
grp.material.diffuse = 0.1;
|
||||||
|
grp.material.specular = 0;
|
||||||
|
grp.material.transparency = 1;
|
||||||
|
grp.material.reflective = 1;
|
||||||
|
grp.material.refractiveIndex = 1;
|
||||||
|
|
||||||
|
Sphere ballSp = Sphere();
|
||||||
|
ballSp.materialSet = true;
|
||||||
|
ballSp.material.colour = Colour(0.7, 0.2, 0.1);
|
||||||
|
CSG ballLeaf = CSG(CSG::INTERSECTION, &grp, &ballSp);
|
||||||
|
|
||||||
|
ballLeaf.setTransform(translation(-4, 1, -0.1) * rotationY(-0.35) * rotationZ(0.1));
|
||||||
|
w.addObject(&ballLeaf);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
/* Set the camera */
|
||||||
|
Camera camera = Camera(80, 40, M_PI / 2);
|
||||||
|
camera.setTransform(viewTransform(Point(0, 3, 5),
|
||||||
|
Point(0, 1, 0),
|
||||||
|
Vector(0, 1, 0)));
|
||||||
|
|
||||||
|
/* Now render it */
|
||||||
|
Canvas image = camera.render(w, 5);
|
||||||
|
|
||||||
|
image.SaveAsPNG("ch16_test.png");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -32,7 +32,8 @@ int main()
|
|||||||
double worldX = -(wallSize / 2) + pixelSize * x;
|
double worldX = -(wallSize / 2) + pixelSize * x;
|
||||||
Point position = Point(worldX, worldY, wallDistance);
|
Point position = Point(worldX, worldY, wallDistance);
|
||||||
Ray r = Ray(cameraOrigin, (position - cameraOrigin).normalise());
|
Ray r = Ray(cameraOrigin, (position - cameraOrigin).normalise());
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs;
|
||||||
|
s.intersect(r, xs);
|
||||||
|
|
||||||
if (!xs.hit().nothing())
|
if (!xs.hit().nothing())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ int main()
|
|||||||
double worldX = -(wallSize / 2) + pixelSize * x;
|
double worldX = -(wallSize / 2) + pixelSize * x;
|
||||||
Point position = Point(worldX, worldY, wallDistance);
|
Point position = Point(worldX, worldY, wallDistance);
|
||||||
Ray r = Ray(cameraOrigin, (position - cameraOrigin).normalise());
|
Ray r = Ray(cameraOrigin, (position - cameraOrigin).normalise());
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs;
|
||||||
|
s.intersect(r, xs);
|
||||||
|
|
||||||
Intersection hit = xs.hit();
|
Intersection hit = xs.hit();
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,9 @@ Shape *fir_branch()
|
|||||||
Group *ret = new Group();
|
Group *ret = new Group();
|
||||||
double length = 2;
|
double length = 2;
|
||||||
double radius = 0.025;
|
double radius = 0.025;
|
||||||
#if 0
|
|
||||||
int segments = 20;
|
int segments = 20;
|
||||||
int perSegment = 24;
|
int perSegment = 24;
|
||||||
#else
|
|
||||||
int segments = 5;
|
|
||||||
int perSegment = 24;
|
|
||||||
#endif
|
|
||||||
Cylinder *branch = new Cylinder();
|
Cylinder *branch = new Cylinder();
|
||||||
branch->minCap = 0;
|
branch->minCap = 0;
|
||||||
branch->maxCap = length;
|
branch->maxCap = length;
|
||||||
@@ -50,11 +46,12 @@ Shape *fir_branch()
|
|||||||
branch->material.ambient = 0.2;
|
branch->material.ambient = 0.2;
|
||||||
branch->material.specular = 0;
|
branch->material.specular = 0;
|
||||||
branch->material.diffuse = 0.6;
|
branch->material.diffuse = 0.6;
|
||||||
|
branch->materialSet = true;
|
||||||
ret->addObject(branch);
|
ret->addObject(branch);
|
||||||
|
|
||||||
double seq_size = length / (segments - 1);
|
double seq_size = length / (segments - 1);
|
||||||
double theta = 2.1 * M_PI / perSegment;
|
double theta = 2.1 * M_PI / perSegment;
|
||||||
double maxLenght = 20 * radius;
|
double maxLength = 20 * radius;
|
||||||
|
|
||||||
int y, i;
|
int y, i;
|
||||||
Triangle *needle;
|
Triangle *needle;
|
||||||
@@ -73,7 +70,7 @@ Shape *fir_branch()
|
|||||||
* around the branch */
|
* around the branch */
|
||||||
double yAngle = i * theta + frand() * theta;
|
double yAngle = i * theta + frand() * theta;
|
||||||
/* How long is the needle? */
|
/* How long is the needle? */
|
||||||
double needleLenght = maxLenght / 2 * (1 + frand());
|
double needleLenght = maxLength / 2 * (1 + frand());
|
||||||
/* How much is the needle offset fomr the center of the branch? */
|
/* How much is the needle offset fomr the center of the branch? */
|
||||||
double ofs = radius / 2;
|
double ofs = radius / 2;
|
||||||
Point p1 = Point(ofs, yBase, ofs);
|
Point p1 = Point(ofs, yBase, ofs);
|
||||||
@@ -81,14 +78,14 @@ Shape *fir_branch()
|
|||||||
Point p3 = Point(0, yTip, needleLenght);
|
Point p3 = Point(0, yTip, needleLenght);
|
||||||
needle = new Triangle(p1, p2, p3);
|
needle = new Triangle(p1, p2, p3);
|
||||||
needle->setTransform(rotationY(yAngle));
|
needle->setTransform(rotationY(yAngle));
|
||||||
needle->material.colour = Colour((1.0 / (double)perSegment)*i /*0.26*/, 0.36, /*0.16*/ (1. / (double)segments) * y);
|
needle->material.colour = Colour(0.26, 0.36, 0.16);
|
||||||
needle->material.specular = 0.1;
|
needle->material.specular = 0.1;
|
||||||
|
needle->materialSet = true;
|
||||||
subGroup->addObject(needle);
|
subGroup->addObject(needle);
|
||||||
}
|
}
|
||||||
ret->addObject(subGroup);
|
ret->addObject(subGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +105,7 @@ int main()
|
|||||||
light1Sphere.material.ambient = 0.6;
|
light1Sphere.material.ambient = 0.6;
|
||||||
light1Sphere.material.diffuse = 0;
|
light1Sphere.material.diffuse = 0;
|
||||||
light1Sphere.material.specular = 0;
|
light1Sphere.material.specular = 0;
|
||||||
|
light1Sphere.materialSet = true;
|
||||||
w.addObject(&light1Sphere);
|
w.addObject(&light1Sphere);
|
||||||
|
|
||||||
Light light2 = Light(POINT_LIGHT, Point(10, 10, -10), Colour(0.6, 0.6, 0.6));
|
Light light2 = Light(POINT_LIGHT, Point(10, 10, -10), Colour(0.6, 0.6, 0.6));
|
||||||
@@ -119,6 +117,7 @@ int main()
|
|||||||
light2Sphere.material.ambient = 0.6;
|
light2Sphere.material.ambient = 0.6;
|
||||||
light2Sphere.material.diffuse = 0;
|
light2Sphere.material.diffuse = 0;
|
||||||
light2Sphere.material.specular = 0;
|
light2Sphere.material.specular = 0;
|
||||||
|
light2Sphere.materialSet = true;
|
||||||
w.addObject(&light2Sphere);
|
w.addObject(&light2Sphere);
|
||||||
|
|
||||||
Light light3 = Light(POINT_LIGHT, Point(-2, 1, -6), Colour(0.2, 0.1, 0.1));
|
Light light3 = Light(POINT_LIGHT, Point(-2, 1, -6), Colour(0.2, 0.1, 0.1));
|
||||||
@@ -130,6 +129,7 @@ int main()
|
|||||||
light3Sphere.material.ambient = 0.6;
|
light3Sphere.material.ambient = 0.6;
|
||||||
light3Sphere.material.diffuse = 0;
|
light3Sphere.material.diffuse = 0;
|
||||||
light3Sphere.material.specular = 0;
|
light3Sphere.material.specular = 0;
|
||||||
|
light3Sphere.materialSet = true;
|
||||||
w.addObject(&light3Sphere);
|
w.addObject(&light3Sphere);
|
||||||
|
|
||||||
Light light4 = Light(POINT_LIGHT, Point(-1, -2, -6), Colour(0.1, 0.2, 0.1));
|
Light light4 = Light(POINT_LIGHT, Point(-1, -2, -6), Colour(0.1, 0.2, 0.1));
|
||||||
@@ -141,6 +141,7 @@ int main()
|
|||||||
light4Sphere.material.ambient = 0.6;
|
light4Sphere.material.ambient = 0.6;
|
||||||
light4Sphere.material.diffuse = 0;
|
light4Sphere.material.diffuse = 0;
|
||||||
light4Sphere.material.specular = 0;
|
light4Sphere.material.specular = 0;
|
||||||
|
light4Sphere.materialSet = true;
|
||||||
w.addObject(&light4Sphere);
|
w.addObject(&light4Sphere);
|
||||||
|
|
||||||
Light light5 = Light(POINT_LIGHT, Point(3, -1, -6), Colour(0.2, 0.2, 0.2));
|
Light light5 = Light(POINT_LIGHT, Point(3, -1, -6), Colour(0.2, 0.2, 0.2));
|
||||||
@@ -152,6 +153,7 @@ int main()
|
|||||||
light5Sphere.material.ambient = 0.6;
|
light5Sphere.material.ambient = 0.6;
|
||||||
light5Sphere.material.diffuse = 0;
|
light5Sphere.material.diffuse = 0;
|
||||||
light5Sphere.material.specular = 0;
|
light5Sphere.material.specular = 0;
|
||||||
|
light5Sphere.materialSet = true;
|
||||||
w.addObject(&light5Sphere);
|
w.addObject(&light5Sphere);
|
||||||
|
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
@@ -162,6 +164,7 @@ int main()
|
|||||||
theBall.material.specular = 0;
|
theBall.material.specular = 0;
|
||||||
theBall.material.diffuse = 0.5;
|
theBall.material.diffuse = 0.5;
|
||||||
theBall.material.reflective = 0.5;
|
theBall.material.reflective = 0.5;
|
||||||
|
theBall.materialSet = true;
|
||||||
w.addObject(&theBall);
|
w.addObject(&theBall);
|
||||||
|
|
||||||
Cylinder crown = Cylinder();
|
Cylinder crown = Cylinder();
|
||||||
@@ -175,6 +178,7 @@ int main()
|
|||||||
crown.material.specular = 0.8;
|
crown.material.specular = 0.8;
|
||||||
crown.material.shininess = 20;
|
crown.material.shininess = 20;
|
||||||
crown.material.reflective = 0.05;
|
crown.material.reflective = 0.05;
|
||||||
|
crown.materialSet = true;
|
||||||
w.addObject(&crown);
|
w.addObject(&crown);
|
||||||
|
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
@@ -183,7 +187,7 @@ int main()
|
|||||||
s = fir_branch();
|
s = fir_branch();
|
||||||
s->setTransform(translation(-1, -1, 0) * rotationY(0.349) * rotationX(-1.5708) * translation(0, -0.5, 0));
|
s->setTransform(translation(-1, -1, 0) * rotationY(0.349) * rotationX(-1.5708) * translation(0, -0.5, 0));
|
||||||
w.addObject(s);
|
w.addObject(s);
|
||||||
/*
|
|
||||||
s = fir_branch();
|
s = fir_branch();
|
||||||
s->setTransform(translation(-1, 1, 0) * rotationY(0.349) * rotationX(-1.5708) * translation(0, -0.5, 0));
|
s->setTransform(translation(-1, 1, 0) * rotationY(0.349) * rotationX(-1.5708) * translation(0, -0.5, 0));
|
||||||
w.addObject(s);
|
w.addObject(s);
|
||||||
@@ -223,16 +227,16 @@ int main()
|
|||||||
s = fir_branch();
|
s = fir_branch();
|
||||||
s->setTransform(translation(1.5, -0.5, 0) * rotationY(-0.1754) * rotationX(0.087) * rotationX(-1.5708) * translation(0, -0.5, 0));
|
s->setTransform(translation(1.5, -0.5, 0) * rotationY(-0.1754) * rotationX(0.087) * rotationX(-1.5708) * translation(0, -0.5, 0));
|
||||||
w.addObject(s);
|
w.addObject(s);
|
||||||
*/
|
|
||||||
|
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
|
/*
|
||||||
FILE *fpOut = fopen("christmas_worlddump.json", "wt");
|
FILE *fpOut = fopen("christmas_worlddump.json", "wt");
|
||||||
if (fpOut)
|
if (fpOut)
|
||||||
{
|
{
|
||||||
w.dumpMe(fpOut);
|
w.dumpMe(fpOut);
|
||||||
fclose(fpOut);
|
fclose(fpOut);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ TEST(ConeTest, Intersecting_a_cone_with_a_ray)
|
|||||||
Tuple direction = Directions[i].normalise();
|
Tuple direction = Directions[i].normalise();
|
||||||
Ray r = Ray(Origins[i], direction);
|
Ray r = Ray(Origins[i], direction);
|
||||||
|
|
||||||
Intersect xs = cone.intersect(r);
|
Intersect xs; cone.intersect(r, xs);
|
||||||
|
|
||||||
/* Temporary lower the precision */
|
/* Temporary lower the precision */
|
||||||
set_equal_precision(0.00001);
|
set_equal_precision(0.00001);
|
||||||
@@ -64,7 +64,7 @@ TEST(ConeTest, Intersecting_a_cone_with_a_ray_parall_to_one_of_its_halves)
|
|||||||
Cone cone = Cone();
|
Cone cone = Cone();
|
||||||
Tuple direction = Vector(0, 1, 1).normalise();
|
Tuple direction = Vector(0, 1, 1).normalise();
|
||||||
Ray r = Ray(Point(0, 0, -1), direction);
|
Ray r = Ray(Point(0, 0, -1), direction);
|
||||||
Intersect xs = cone.intersect(r);
|
Intersect xs; cone.intersect(r, xs);
|
||||||
ASSERT_EQ(xs.count(), 1);
|
ASSERT_EQ(xs.count(), 1);
|
||||||
|
|
||||||
/* Temporary lower the precision */
|
/* Temporary lower the precision */
|
||||||
@@ -102,7 +102,8 @@ TEST(ConeTest, Intersecting_a_cone_end_cap)
|
|||||||
Tuple direction = Directions[i].normalise();
|
Tuple direction = Directions[i].normalise();
|
||||||
Ray r = Ray(Origins[i], direction);
|
Ray r = Ray(Origins[i], direction);
|
||||||
|
|
||||||
Intersect xs = cone.intersect(r);
|
Intersect xs;
|
||||||
|
cone.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), Counts[i]);
|
ASSERT_EQ(xs.count(), Counts[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
227
tests/csg_test.cpp
Normal file
227
tests/csg_test.cpp
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* CSG unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <intersect.h>
|
||||||
|
#include <intersection.h>
|
||||||
|
#include <sphere.h>
|
||||||
|
#include <cube.h>
|
||||||
|
#include <csg.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
/* Proxy class to get access to protected functions / members */
|
||||||
|
class CSGTest : public CSG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void doLocalIntersect(Ray r, Intersect &xs) {
|
||||||
|
return this->localIntersect(r, xs);
|
||||||
|
};
|
||||||
|
Tuple doLocalNormalAt(Tuple point, Intersection *hit = nullptr) {
|
||||||
|
return this->localNormalAt(point, hit);
|
||||||
|
};
|
||||||
|
BoundingBox doGetLocalBounds() {
|
||||||
|
return this->getLocalBounds();
|
||||||
|
};
|
||||||
|
bool doIntersectionAllowed(bool leftHit, bool inLeft, bool inRight) {
|
||||||
|
return this->intersectionAllowed(leftHit, inLeft, inRight);
|
||||||
|
};
|
||||||
|
|
||||||
|
void doFilterIntersections(Intersect &xs, Intersect &ret) {
|
||||||
|
this->filterIntersections(xs, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSGTest(OperationType operation, Shape *left, Shape *right) : CSG(operation, left, right) {};
|
||||||
|
|
||||||
|
|
||||||
|
Shape *getLeft() { return this->left; };
|
||||||
|
Shape *getRight() { return this->right; };
|
||||||
|
OperationType getOperation() { return this->operation; };
|
||||||
|
void setOperation(OperationType operation) { this->operation = operation; };
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(CSGTest, Csg_is_created_with_an_operation_and_two_shape)
|
||||||
|
{
|
||||||
|
Sphere s1 = Sphere();
|
||||||
|
Cube s2 = Cube();
|
||||||
|
|
||||||
|
CSGTest c = CSGTest(CSG::UNION, &s1, &s2);
|
||||||
|
|
||||||
|
ASSERT_EQ(c.getOperation(), CSG::UNION);
|
||||||
|
ASSERT_EQ(*c.getLeft(), s1);
|
||||||
|
ASSERT_EQ(*c.getRight(), s2);
|
||||||
|
|
||||||
|
ASSERT_EQ(*s1.parent, c);
|
||||||
|
ASSERT_EQ(*s2.parent, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CSGTest, Evaluating_the_rules_for_a_csg_operation)
|
||||||
|
{
|
||||||
|
Sphere s1 = Sphere();
|
||||||
|
Cube s2 = Cube();
|
||||||
|
CSGTest c = CSGTest(CSG::UNION, &s1, &s2);
|
||||||
|
|
||||||
|
CSG::OperationType testList2[] = {
|
||||||
|
CSG::UNION, CSG::UNION,
|
||||||
|
CSG::UNION, CSG::UNION,
|
||||||
|
CSG::UNION, CSG::UNION,
|
||||||
|
CSG::UNION, CSG::UNION,
|
||||||
|
|
||||||
|
CSG::INTERSECTION, CSG::INTERSECTION,
|
||||||
|
CSG::INTERSECTION, CSG::INTERSECTION,
|
||||||
|
CSG::INTERSECTION, CSG::INTERSECTION,
|
||||||
|
CSG::INTERSECTION, CSG::INTERSECTION,
|
||||||
|
|
||||||
|
CSG::DIFFERENCE, CSG::DIFFERENCE,
|
||||||
|
CSG::DIFFERENCE, CSG::DIFFERENCE,
|
||||||
|
CSG::DIFFERENCE, CSG::DIFFERENCE,
|
||||||
|
CSG::DIFFERENCE, CSG::DIFFERENCE,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool testList[][3] = {
|
||||||
|
/* lhit, inl, inr */
|
||||||
|
/* UNION */ { true, true, true },
|
||||||
|
{ true, true, false },
|
||||||
|
{ true, false, true },
|
||||||
|
{ true, false, false },
|
||||||
|
{ false, true, true },
|
||||||
|
{ false, true, false },
|
||||||
|
{ false, false, true },
|
||||||
|
{ false, false, false },
|
||||||
|
|
||||||
|
/* INTER */ { true, true, true },
|
||||||
|
{ true, true, false },
|
||||||
|
{ true, false, true },
|
||||||
|
{ true, false, false },
|
||||||
|
{ false, true, true },
|
||||||
|
{ false, true, false },
|
||||||
|
{ false, false, true },
|
||||||
|
{ false, false, false },
|
||||||
|
|
||||||
|
/* DIFFE */ { true, true, true },
|
||||||
|
{ true, true, false },
|
||||||
|
{ true, false, true },
|
||||||
|
{ true, false, false },
|
||||||
|
{ false, true, true },
|
||||||
|
{ false, true, false },
|
||||||
|
{ false, false, true },
|
||||||
|
{ false, false, false },
|
||||||
|
};
|
||||||
|
|
||||||
|
bool testResults[] {
|
||||||
|
/* Unions */
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
/* Intersection */
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
/* difference */
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
int testCount = sizeof(testList)/sizeof((testList)[0]);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < testCount; i++)
|
||||||
|
{
|
||||||
|
c.setOperation(testList2[i]);
|
||||||
|
ASSERT_EQ(c.doIntersectionAllowed(testList[i][0], testList[i][1], testList[i][2]), testResults[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CSGTest, Filtering_a_list_of_intersections)
|
||||||
|
{
|
||||||
|
Sphere s1 = Sphere();
|
||||||
|
Cube s2 = Cube();
|
||||||
|
CSGTest c = CSGTest(CSG::UNION, &s1, &s2);
|
||||||
|
|
||||||
|
CSG::OperationType testList[] = {
|
||||||
|
CSG::UNION,
|
||||||
|
CSG::INTERSECTION,
|
||||||
|
CSG::DIFFERENCE,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t testResults[][2] = {
|
||||||
|
{0, 3},
|
||||||
|
{1, 2},
|
||||||
|
{0, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
int testCount = sizeof(testList)/sizeof((testList)[0]);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
Intersect xs = Intersect();
|
||||||
|
Intersection i1 = Intersection(1, &s1);
|
||||||
|
Intersection i2 = Intersection(2, &s2);
|
||||||
|
Intersection i3 = Intersection(3, &s1);
|
||||||
|
Intersection i4 = Intersection(4, &s2);
|
||||||
|
xs.add(i1);
|
||||||
|
xs.add(i2);
|
||||||
|
xs.add(i3);
|
||||||
|
xs.add(i4);
|
||||||
|
|
||||||
|
for(i = 0; i < testCount; i++)
|
||||||
|
{
|
||||||
|
c.setOperation(testList[i]);
|
||||||
|
Intersect result = Intersect();
|
||||||
|
c.doFilterIntersections(xs, result);
|
||||||
|
|
||||||
|
ASSERT_EQ(result.count(), 2);
|
||||||
|
ASSERT_EQ(result[0], xs[testResults[i][0]]);
|
||||||
|
ASSERT_EQ(result[1], xs[testResults[i][1]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CSGTest, A_ray_misses_a_csg_object)
|
||||||
|
{
|
||||||
|
Sphere s1 = Sphere();
|
||||||
|
Cube s2 = Cube();
|
||||||
|
CSGTest c = CSGTest(CSG::UNION, &s1, &s2);
|
||||||
|
|
||||||
|
Ray r = Ray(Point(0, 2, -5), Vector(0, 0, 1));
|
||||||
|
Intersect xs; c.doLocalIntersect(r, xs);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CSGTest, A_ray_hits_a_csg_object)
|
||||||
|
{
|
||||||
|
Sphere s1 = Sphere();
|
||||||
|
Sphere s2 = Sphere();
|
||||||
|
|
||||||
|
s2.setTransform(translation(0, 0, 0.5));
|
||||||
|
|
||||||
|
CSGTest c = CSGTest(CSG::UNION, &s1, &s2);
|
||||||
|
|
||||||
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
|
Intersect xs; c.doLocalIntersect(r, xs);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_TRUE(double_equal(xs[0].t, 4));
|
||||||
|
ASSERT_EQ(xs[0].object, &s1);
|
||||||
|
ASSERT_TRUE(double_equal(xs[1].t, 6.5));
|
||||||
|
ASSERT_EQ(xs[1].object, &s2);
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ TEST(CubeTest, A_ray_intersects_a_cube)
|
|||||||
for(i = 0; i < 7; i++)
|
for(i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
Ray r = Ray(Origins[i], Directions[i]);
|
Ray r = Ray(Origins[i], Directions[i]);
|
||||||
Intersect xs = c.intersect(r);
|
Intersect xs; c.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
EXPECT_EQ(xs[0].t, t1[i]);
|
EXPECT_EQ(xs[0].t, t1[i]);
|
||||||
@@ -77,7 +77,7 @@ TEST(CubeTest, A_ray_miss_a_cube)
|
|||||||
for(i = 0; i < 6; i++)
|
for(i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
Ray r = Ray(Origins[i], Directions[i]);
|
Ray r = Ray(Origins[i], Directions[i]);
|
||||||
Intersect xs = c.intersect(r);
|
Intersect xs; c.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ TEST(CylinderTest, A_ray_miss_a_cylinder)
|
|||||||
Tuple direction = Directions[i].normalise();
|
Tuple direction = Directions[i].normalise();
|
||||||
Ray r = Ray(Origins[i], direction);
|
Ray r = Ray(Origins[i], direction);
|
||||||
|
|
||||||
Intersect xs = cyl.intersect(r);
|
Intersect xs; cyl.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -59,13 +59,13 @@ TEST(CylinderTest, A_ray_hit_a_cylinder)
|
|||||||
double t0s[] = { 5, 4, 6.80798 };
|
double t0s[] = { 5, 4, 6.80798 };
|
||||||
double t1s[] = { 5, 6, 7.08872 };
|
double t1s[] = { 5, 6, 7.08872 };
|
||||||
|
|
||||||
int i, j;
|
int i;
|
||||||
for(i = 0; i < 3; i++)
|
for(i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
Tuple direction = Directions[i].normalise();
|
Tuple direction = Directions[i].normalise();
|
||||||
Ray r = Ray(Origins[i], direction);
|
Ray r = Ray(Origins[i], direction);
|
||||||
|
|
||||||
Intersect xs = cyl.intersect(r);
|
Intersect xs; cyl.intersect(r, xs);
|
||||||
|
|
||||||
/* Temporary lower the precision */
|
/* Temporary lower the precision */
|
||||||
set_equal_precision(0.00001);
|
set_equal_precision(0.00001);
|
||||||
@@ -142,7 +142,7 @@ TEST(CylinderTest, Intersecting_a_constrained_cylinder)
|
|||||||
Tuple direction = Directions[i].normalise();
|
Tuple direction = Directions[i].normalise();
|
||||||
Ray r = Ray(Origins[i], direction);
|
Ray r = Ray(Origins[i], direction);
|
||||||
|
|
||||||
Intersect xs = cyl.intersect(r);
|
Intersect xs; cyl.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), Counts[i]);
|
ASSERT_EQ(xs.count(), Counts[i]);
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ TEST(CylinderTest, Intersecting_the_caps_of_a_close_cylinder)
|
|||||||
Tuple direction = Directions[i].normalise();
|
Tuple direction = Directions[i].normalise();
|
||||||
Ray r = Ray(Origins[i], direction);
|
Ray r = Ray(Origins[i], direction);
|
||||||
|
|
||||||
Intersect xs = cyl.intersect(r);
|
Intersect xs; cyl.intersect(r, xs);
|
||||||
ASSERT_EQ(xs.count(), Counts[i]);
|
ASSERT_EQ(xs.count(), Counts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
136
tests/dragon_scene.cpp
Normal file
136
tests/dragon_scene.cpp
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Render test for OBJ File using teapots in chapter 15.
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <world.h>
|
||||||
|
#include <light.h>
|
||||||
|
#include <plane.h>
|
||||||
|
#include <cube.h>
|
||||||
|
#include <sphere.h>
|
||||||
|
#include <material.h>
|
||||||
|
#include <colour.h>
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <camera.h>
|
||||||
|
#include <objfile.h>
|
||||||
|
|
||||||
|
#include <pattern.h>
|
||||||
|
#include <strippattern.h>
|
||||||
|
#include <gradientpattern.h>
|
||||||
|
#include <checkerspattern.h>
|
||||||
|
#include <ringpattern.h>
|
||||||
|
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <cylinder.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
World w = World();
|
||||||
|
|
||||||
|
/* Add lights */
|
||||||
|
/* Light light1 = Light(POINT_LIGHT, Point(10, 100, 10), Colour(.1, .1, .1));
|
||||||
|
w.addLight(&light1);
|
||||||
|
Light light2 = Light(POINT_LIGHT, Point(10, 100, -10), Colour(.1, .1, .2));
|
||||||
|
w.addLight(&light2);
|
||||||
|
Light light3 = Light(POINT_LIGHT, Point(-10, 100, 10), Colour(.1, .1, .1));
|
||||||
|
w.addLight(&light3);
|
||||||
|
Light light4 = Light(POINT_LIGHT, Point(-10, 100, -10), Colour(.1, .1, .1));
|
||||||
|
w.addLight(&light4);*/
|
||||||
|
|
||||||
|
Light light1 = Light(POINT_LIGHT, Point(0, 100, 0), Colour(.3, .3, .3));
|
||||||
|
w.addLight(&light1);
|
||||||
|
|
||||||
|
|
||||||
|
Point lightPos = Point(3.8, 6.8, 4.5);
|
||||||
|
Light mouthLight = Light(POINT_LIGHT, lightPos, Colour(0.5, 0.5, 0.5));
|
||||||
|
w.addLight(&mouthLight);
|
||||||
|
|
||||||
|
Light mainLight = Light(POINT_LIGHT, Point(8, 10, 16), Colour(1, 1, 1));
|
||||||
|
w.addLight(&mainLight);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
#if 0
|
||||||
|
/* Spot light */
|
||||||
|
Sphere spot = Sphere();
|
||||||
|
spot.dropShadow = false;
|
||||||
|
spot.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * scaling(0.2, 0.2, 0.2));
|
||||||
|
w.addObject(&spot);
|
||||||
|
|
||||||
|
Cylinder X = Cylinder();
|
||||||
|
X.minCap = 0;
|
||||||
|
X.maxCap = 4;
|
||||||
|
Cylinder Y = Cylinder();
|
||||||
|
Y.minCap = 0;
|
||||||
|
Y.maxCap = 4;
|
||||||
|
Cylinder Z = Cylinder();
|
||||||
|
Z.minCap = 0;
|
||||||
|
Z.maxCap = 4;
|
||||||
|
Z.materialSet = Y.materialSet = X.materialSet = true;
|
||||||
|
X.material.ambient = 1;
|
||||||
|
X.material.specular = 0;
|
||||||
|
X.material.diffuse = 0;
|
||||||
|
Z.material = Y.material = X.material;
|
||||||
|
Z.dropShadow = Y.dropShadow = X.dropShadow = false;
|
||||||
|
X.material.colour = Colour(1, 0, 0);
|
||||||
|
Y.material.colour = Colour(0, 1, 0);
|
||||||
|
Z.material.colour = Colour(0, 0, 1);
|
||||||
|
|
||||||
|
Y.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * scaling(0.1, 1, 0.1));
|
||||||
|
X.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * rotationZ(M_PI/2) * scaling(0.1, 1, 0.1));
|
||||||
|
Z.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * rotationX(M_PI/2) * scaling(0.1, 1, 0.1));
|
||||||
|
w.addObject(&X);
|
||||||
|
w.addObject(&Y);
|
||||||
|
w.addObject(&Z);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Floor */
|
||||||
|
Material floorMaterial;
|
||||||
|
floorMaterial.colour = Colour(0.2, 0.3, 0.3);
|
||||||
|
floorMaterial.reflective = 0.3;
|
||||||
|
floorMaterial.specular = 0.4;
|
||||||
|
floorMaterial.shininess = 5;
|
||||||
|
floorMaterial.ambient = 0;
|
||||||
|
floorMaterial.diffuse = 0.8;
|
||||||
|
|
||||||
|
/* Let's use a cube for the floor */
|
||||||
|
Cube floor = Cube();
|
||||||
|
floor.setMaterial(floorMaterial);
|
||||||
|
floor.setTransform(translation(0, -0.1, 0) * scaling(10, 0.1, 10));
|
||||||
|
w.addObject(&floor);
|
||||||
|
|
||||||
|
Material dragonMat;
|
||||||
|
dragonMat.colour = Colour(0.23, 0.75, 0.39);
|
||||||
|
dragonMat.reflective = 0.9;
|
||||||
|
dragonMat.transparency = 0.99;
|
||||||
|
dragonMat.specular = 0.9;
|
||||||
|
dragonMat.shininess = 50;
|
||||||
|
dragonMat.refractiveIndex = 1.6;
|
||||||
|
dragonMat.ambient = 0;
|
||||||
|
dragonMat.diffuse = 0.7;
|
||||||
|
|
||||||
|
Shape *dragon = OBJFile("dragon.obj").getBaseGroup();
|
||||||
|
dragon->setTransform(rotationY(M_PI * 0.75) * scaling(2, 2, 2));
|
||||||
|
dragon->setMaterial(dragonMat);
|
||||||
|
w.addObject(dragon);
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
OctreeOptimisation opt;
|
||||||
|
w.finalise(opt);
|
||||||
|
|
||||||
|
/* Set the camera */
|
||||||
|
Camera camera = Camera(40, 30, M_PI/2);
|
||||||
|
camera.setTransform(viewTransform(Point(-5, 10, 13),
|
||||||
|
Point(0, 1, 0),
|
||||||
|
Vector(0, 1, 0)));
|
||||||
|
|
||||||
|
/* Now render it */
|
||||||
|
Canvas image = camera.render(w, 5);
|
||||||
|
|
||||||
|
image.SaveAsPNG("ch15_teapot_objfile.png");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ TEST(GroupTest, Intersecting_a_ray_with_an_empty_group)
|
|||||||
{
|
{
|
||||||
Group g = Group();
|
Group g = Group();
|
||||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||||
Intersect xs = g.intersect(r);
|
Intersect xs; g.intersect(r, xs);
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ TEST(GroupTest, Intersecting_a_ray_with_an_nonempty_group)
|
|||||||
g.addObject(&s3);
|
g.addObject(&s3);
|
||||||
|
|
||||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
Intersect xs = g.intersect(r);
|
Intersect xs; g.intersect(r, xs);
|
||||||
ASSERT_EQ(xs.count(), 4);
|
ASSERT_EQ(xs.count(), 4);
|
||||||
EXPECT_EQ(xs[0].object, &s2);
|
EXPECT_EQ(xs[0].object, &s2);
|
||||||
EXPECT_EQ(xs[1].object, &s2);
|
EXPECT_EQ(xs[1].object, &s2);
|
||||||
@@ -77,7 +77,7 @@ TEST(GroupTest, Intersecting_a_transformed_group)
|
|||||||
g.addObject(&s);
|
g.addObject(&s);
|
||||||
|
|
||||||
Ray r = Ray(Point(10, 0, -50), Vector(0, 0, 1));
|
Ray r = Ray(Point(10, 0, -50), Vector(0, 0, 1));
|
||||||
Intersect xs = g.intersect(r);
|
Intersect xs; g.intersect(r, xs);
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,4 +97,36 @@ TEST(GroupTest, Group_bounding_box)
|
|||||||
|
|
||||||
ASSERT_EQ(res.min, b.min);
|
ASSERT_EQ(res.min, b.min);
|
||||||
ASSERT_EQ(res.max, b.max);
|
ASSERT_EQ(res.max, b.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(GroupTest, Removing_an_object)
|
||||||
|
{
|
||||||
|
Group g = Group();
|
||||||
|
Sphere s1 = Sphere();
|
||||||
|
Sphere s2 = Sphere();
|
||||||
|
Sphere s3 = Sphere();
|
||||||
|
|
||||||
|
s1.setTransform(translation(-1, 0, 0));
|
||||||
|
s2.setTransform(scaling(0.5, 0.5, 0.5));
|
||||||
|
|
||||||
|
g.addObject(&s1);
|
||||||
|
g.addObject(&s2);
|
||||||
|
|
||||||
|
ASSERT_EQ(g.getObjectCount(), 2);
|
||||||
|
ASSERT_EQ(*(g[0]), s1);
|
||||||
|
ASSERT_EQ(*(g[1]), s2);
|
||||||
|
|
||||||
|
g.removeObject(&s1);
|
||||||
|
|
||||||
|
ASSERT_EQ(g.getObjectCount(), 1);
|
||||||
|
ASSERT_EQ(*(g[0]), s2);
|
||||||
|
ASSERT_EQ(g[1], nullptr);
|
||||||
|
|
||||||
|
g.removeObject(&s3);
|
||||||
|
ASSERT_EQ(g.getObjectCount(), 1);
|
||||||
|
|
||||||
|
g.removeObject(&s2);
|
||||||
|
ASSERT_EQ(g.getObjectCount(), 0);
|
||||||
|
ASSERT_EQ(g[0], nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ TEST(IntersectTest, Intersect_sets_the_object_on_the_intersection)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
Sphere s = Sphere();
|
Sphere s = Sphere();
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
ASSERT_EQ(xs[0].object, (Shape *)&s);
|
ASSERT_EQ(xs[0].object, (Shape *)&s);
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ TEST(OBJFileTest, Parsing_triangle_faces)
|
|||||||
OBJFile parser = OBJFile();
|
OBJFile parser = OBJFile();
|
||||||
parser.parseOBJFile(file);
|
parser.parseOBJFile(file);
|
||||||
|
|
||||||
Group *g0 = parser.groups(0);
|
Group *g0 = parser.groups(OBJ_DEFAULT_GROUP);
|
||||||
|
|
||||||
Triangle *t1 = (Triangle *)(*g0)[0];
|
Triangle *t1 = (Triangle *)(*g0)[0];
|
||||||
Triangle *t2 = (Triangle *)(*g0)[1];
|
Triangle *t2 = (Triangle *)(*g0)[1];
|
||||||
@@ -82,7 +82,7 @@ TEST(OBJFileTest, Triangulating_polygons)
|
|||||||
OBJFile parser = OBJFile();
|
OBJFile parser = OBJFile();
|
||||||
parser.parseOBJFile(file);
|
parser.parseOBJFile(file);
|
||||||
|
|
||||||
Group *g0 = parser.groups(0);
|
Group *g0 = parser.groups(OBJ_DEFAULT_GROUP);
|
||||||
|
|
||||||
Triangle *t1 = (Triangle *)(*g0)[0];
|
Triangle *t1 = (Triangle *)(*g0)[0];
|
||||||
Triangle *t2 = (Triangle *)(*g0)[1];
|
Triangle *t2 = (Triangle *)(*g0)[1];
|
||||||
@@ -105,9 +105,12 @@ TEST(OBJFileTest, Triangle_in_groups)
|
|||||||
{
|
{
|
||||||
OBJFile parser = OBJFile("triangles.obj");
|
OBJFile parser = OBJFile("triangles.obj");
|
||||||
|
|
||||||
/* TODO: Add group name search/test */
|
Group *g1 = parser.groups("FirstGroup");
|
||||||
Group *g1 = parser.groups(1);
|
Group *g2 = parser.groups("SecondGroup");
|
||||||
Group *g2 = parser.groups(2);
|
|
||||||
|
/* The groups must exists */
|
||||||
|
ASSERT_NE(g1, nullptr);
|
||||||
|
ASSERT_NE(g2, nullptr);
|
||||||
|
|
||||||
Triangle *t1 = (Triangle *)(*g1)[0];
|
Triangle *t1 = (Triangle *)(*g1)[0];
|
||||||
Triangle *t2 = (Triangle *)(*g2)[0];
|
Triangle *t2 = (Triangle *)(*g2)[0];
|
||||||
@@ -151,10 +154,10 @@ TEST(OBJFileTest, Faces_with_normal)
|
|||||||
OBJFile parser = OBJFile();
|
OBJFile parser = OBJFile();
|
||||||
parser.parseOBJFile(file);
|
parser.parseOBJFile(file);
|
||||||
|
|
||||||
Group *g0 = parser.groups(0);
|
Group *g0 = parser.groups(OBJ_DEFAULT_GROUP);
|
||||||
|
|
||||||
SmoothTriangle *t1 = (SmoothTriangle *)(*g0)[0];
|
SmoothTriangle *t1 = (SmoothTriangle *)g0->getObject(0);
|
||||||
SmoothTriangle *t2 = (SmoothTriangle *)(*g0)[1];
|
SmoothTriangle *t2 = (SmoothTriangle *)g0->getObject(1);
|
||||||
|
|
||||||
ASSERT_EQ(t1->p1, parser.vertices(1));
|
ASSERT_EQ(t1->p1, parser.vertices(1));
|
||||||
ASSERT_EQ(t1->p2, parser.vertices(2));
|
ASSERT_EQ(t1->p2, parser.vertices(2));
|
||||||
@@ -171,6 +174,4 @@ TEST(OBJFileTest, Faces_with_normal)
|
|||||||
ASSERT_EQ(t2->n1, parser.verticesNormal(3));
|
ASSERT_EQ(t2->n1, parser.verticesNormal(3));
|
||||||
ASSERT_EQ(t2->n2, parser.verticesNormal(1));
|
ASSERT_EQ(t2->n2, parser.verticesNormal(1));
|
||||||
ASSERT_EQ(t2->n3, parser.verticesNormal(2));
|
ASSERT_EQ(t2->n3, parser.verticesNormal(2));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ TEST(PlaneTest, Intersect_with_a_ray_parallel_to_the_plane)
|
|||||||
Plane p = Plane();
|
Plane p = Plane();
|
||||||
Ray r = Ray(Point(0, 10, 0), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 10, 0), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = p.intersect(r);
|
Intersect xs; p.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ TEST(PlaneTest, Intersect_with_a_coplanar_ray)
|
|||||||
Plane p = Plane();
|
Plane p = Plane();
|
||||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = p.intersect(r);
|
Intersect xs; p.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ TEST(PlaneTest, A_ray_intersecting_a_plane_from_above)
|
|||||||
Plane p = Plane();
|
Plane p = Plane();
|
||||||
Ray r = Ray(Point(0, 1, 0), Vector(0, -1, 0));
|
Ray r = Ray(Point(0, 1, 0), Vector(0, -1, 0));
|
||||||
|
|
||||||
Intersect xs = p.intersect(r);
|
Intersect xs; p.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 1);
|
ASSERT_EQ(xs.count(), 1);
|
||||||
ASSERT_EQ(xs[0].t, 1);
|
ASSERT_EQ(xs[0].t, 1);
|
||||||
@@ -63,7 +63,7 @@ TEST(PlaneTest, A_ray_intersecting_a_plane_from_below)
|
|||||||
Plane p = Plane();
|
Plane p = Plane();
|
||||||
Ray r = Ray(Point(0, -1, 0), Vector(0, 1, 0));
|
Ray r = Ray(Point(0, -1, 0), Vector(0, 1, 0));
|
||||||
|
|
||||||
Intersect xs = p.intersect(r);
|
Intersect xs; p.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 1);
|
ASSERT_EQ(xs.count(), 1);
|
||||||
ASSERT_EQ(xs[0].t, 1);
|
ASSERT_EQ(xs[0].t, 1);
|
||||||
@@ -73,7 +73,6 @@ TEST(PlaneTest, A_ray_intersecting_a_plane_from_below)
|
|||||||
TEST(PlaneTest, The_bounding_box_of_a_plane)
|
TEST(PlaneTest, The_bounding_box_of_a_plane)
|
||||||
{
|
{
|
||||||
Plane t = Plane();
|
Plane t = Plane();
|
||||||
BoundingBox b = BoundingBox(Point(-8, -5, -8), Point(8, 8, 8));
|
|
||||||
BoundingBox res = t.getBounds();
|
BoundingBox res = t.getBounds();
|
||||||
|
|
||||||
ASSERT_FALSE(res.min.isRepresentable());
|
ASSERT_FALSE(res.min.isRepresentable());
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ TEST(ShapeTest, Intersecting_a_scaled_shape_with_a_ray)
|
|||||||
TestShape s = TestShape();
|
TestShape s = TestShape();
|
||||||
|
|
||||||
s.setTransform(scaling(2, 2, 2));
|
s.setTransform(scaling(2, 2, 2));
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(s.localRay.origin, Point(0, 0, -2.5));
|
ASSERT_EQ(s.localRay.origin, Point(0, 0, -2.5));
|
||||||
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 0.5));
|
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 0.5));
|
||||||
@@ -65,7 +65,7 @@ TEST(ShapeTest, Intersecting_a_translated_shape_with_a_ray)
|
|||||||
TestShape s = TestShape();
|
TestShape s = TestShape();
|
||||||
|
|
||||||
s.setTransform(translation(5, 0, 0));
|
s.setTransform(translation(5, 0, 0));
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(s.localRay.origin, Point(-5, 0, -5));
|
ASSERT_EQ(s.localRay.origin, Point(-5, 0, -5));
|
||||||
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 1));
|
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 1));
|
||||||
@@ -198,6 +198,28 @@ TEST(ShapeTest, Test_the_bouding_box_of_the_translated_shape)
|
|||||||
|
|
||||||
BoundingBox res = t.getBounds();
|
BoundingBox res = t.getBounds();
|
||||||
|
|
||||||
|
ASSERT_EQ(res.min, b.min);
|
||||||
|
ASSERT_EQ(res.max, b.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ShapeTest, A_lock_shape_can_t_have_transformation_changed)
|
||||||
|
{
|
||||||
|
TestShape t = TestShape();
|
||||||
|
t.setTransform(translation(10, 0, 0));
|
||||||
|
|
||||||
|
BoundingBox b = BoundingBox(Point(9, -1, -1), Point(11, 1, 1));
|
||||||
|
|
||||||
|
BoundingBox res = t.getBounds();
|
||||||
|
|
||||||
|
ASSERT_EQ(res.min, b.min);
|
||||||
|
ASSERT_EQ(res.max, b.max);
|
||||||
|
|
||||||
|
t.lock();
|
||||||
|
|
||||||
|
t.setTransform(translation(-10, -10,-10));
|
||||||
|
|
||||||
|
res = t.getBounds();
|
||||||
|
|
||||||
ASSERT_EQ(res.min, b.min);
|
ASSERT_EQ(res.min, b.min);
|
||||||
ASSERT_EQ(res.max, b.max);
|
ASSERT_EQ(res.max, b.max);
|
||||||
}
|
}
|
||||||
@@ -14,9 +14,9 @@ class SmoothTriTest : public SmoothTriangle
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SmoothTriTest(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : SmoothTriangle(p1, p2, p3, n1, n2, n3) {};
|
SmoothTriTest(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : SmoothTriangle(p1, p2, p3, n1, n2, n3) {};
|
||||||
Intersect doLocalIntersect(Ray ray)
|
void doLocalIntersect(Ray ray, Intersect &xs)
|
||||||
{
|
{
|
||||||
return this->localIntersect(ray);
|
this->localIntersect(ray, xs);
|
||||||
};
|
};
|
||||||
|
|
||||||
Tuple doLocalNormalAt(Tuple point, Intersection *hit)
|
Tuple doLocalNormalAt(Tuple point, Intersection *hit)
|
||||||
@@ -48,7 +48,7 @@ TEST(SmoothTriangleTest, An_intersection_with_a_smooth_triangle_stores_u_v)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(-0.2, 0.3, -2), Vector(0, 0, 1));
|
Ray r = Ray(Point(-0.2, 0.3, -2), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = tri.doLocalIntersect(r);
|
Intersect xs; tri.doLocalIntersect(r, xs);
|
||||||
|
|
||||||
ASSERT_TRUE(double_equal(xs[0].u, 0.45));
|
ASSERT_TRUE(double_equal(xs[0].u, 0.45));
|
||||||
ASSERT_TRUE(double_equal(xs[0].v, 0.25));
|
ASSERT_TRUE(double_equal(xs[0].v, 0.25));
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ TEST(SphereTest, A_ray_intersect_a_sphere_at_two_points)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
Sphere s = Sphere();
|
Sphere s = Sphere();
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
ASSERT_EQ(xs[0].t, 4.0);
|
ASSERT_EQ(xs[0].t, 4.0);
|
||||||
@@ -28,7 +28,7 @@ TEST(SphereTest, A_ray_intersect_a_sphere_at_a_tangent)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(0, 1, -5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 1, -5), Vector(0, 0, 1));
|
||||||
Sphere s = Sphere();
|
Sphere s = Sphere();
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
ASSERT_EQ(xs[0].t, 5.0);
|
ASSERT_EQ(xs[0].t, 5.0);
|
||||||
@@ -39,7 +39,7 @@ TEST(SphereTest, A_ray_miss_a_sphere)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(0, 2, -5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 2, -5), Vector(0, 0, 1));
|
||||||
Sphere s = Sphere();
|
Sphere s = Sphere();
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ TEST(SphereTest, A_ray_originate_inside_a_sphere)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||||
Sphere s = Sphere();
|
Sphere s = Sphere();
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
ASSERT_EQ(xs[0].t, -1.0);
|
ASSERT_EQ(xs[0].t, -1.0);
|
||||||
@@ -59,7 +59,7 @@ TEST(SphereTest, A_sphere_is_behind_a_ray)
|
|||||||
{
|
{
|
||||||
Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1));
|
||||||
Sphere s = Sphere();
|
Sphere s = Sphere();
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
ASSERT_EQ(xs[0].t, -6.0);
|
ASSERT_EQ(xs[0].t, -6.0);
|
||||||
@@ -89,7 +89,7 @@ TEST(SphereTest, Intersecting_a_scaled_sphere_with_a_ray)
|
|||||||
|
|
||||||
s.setTransform(scaling(2, 2, 2));
|
s.setTransform(scaling(2, 2, 2));
|
||||||
|
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 2);
|
ASSERT_EQ(xs.count(), 2);
|
||||||
ASSERT_EQ(xs[0].t, 3.0);
|
ASSERT_EQ(xs[0].t, 3.0);
|
||||||
@@ -103,7 +103,7 @@ TEST(SphereTest, Intersecting_a_translated_sphere_with_a_ray)
|
|||||||
|
|
||||||
s.setTransform(translation(5, 0, 0));
|
s.setTransform(translation(5, 0, 0));
|
||||||
|
|
||||||
Intersect xs = s.intersect(r);
|
Intersect xs; s.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ int main()
|
|||||||
Plane p4 = Plane();
|
Plane p4 = Plane();
|
||||||
p3.setTransform(translation(6, 0, 0) * rotationZ(M_PI/2) );
|
p3.setTransform(translation(6, 0, 0) * rotationZ(M_PI/2) );
|
||||||
p3.material.pattern = new CheckersPattern(Colour(1, 1, 1), Colour(0.1, 0.1, 0.1));
|
p3.material.pattern = new CheckersPattern(Colour(1, 1, 1), Colour(0.1, 0.1, 0.1));
|
||||||
w.addObject(&p3);
|
w.addObject(&p4);
|
||||||
|
|
||||||
Plane p5 = Plane();
|
Plane p5 = Plane();
|
||||||
p5.material.pattern = new CheckersPattern(Colour(1, 1, 1), Colour(0.1, 0.1, 0.1));
|
p5.material.pattern = new CheckersPattern(Colour(1, 1, 1), Colour(0.1, 0.1, 0.1));
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ TEST(TriangleTest, Intersecting_a_ray_parallel_to_the_triangle)
|
|||||||
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
||||||
Ray r = Ray(Point(0, -1, -2), Vector(0, 1, 0));
|
Ray r = Ray(Point(0, -1, -2), Vector(0, 1, 0));
|
||||||
|
|
||||||
Intersect xs = t.intersect(r);
|
Intersect xs; t.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ TEST(TriangleTest, A_ray_miss_the_p1_p3_edge)
|
|||||||
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
||||||
Ray r = Ray(Point(1, 1, -2), Vector(0, 0, 1));
|
Ray r = Ray(Point(1, 1, -2), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = t.intersect(r);
|
Intersect xs; t.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ TEST(TriangleTest, A_ray_miss_the_p1_p2_edge)
|
|||||||
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
||||||
Ray r = Ray(Point(-1, 1, -2), Vector(0, 0, 1));
|
Ray r = Ray(Point(-1, 1, -2), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = t.intersect(r);
|
Intersect xs; t.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ TEST(TriangleTest, A_ray_miss_the_p2_p3_edge)
|
|||||||
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
||||||
Ray r = Ray(Point(0, -1, -2), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, -1, -2), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = t.intersect(r);
|
Intersect xs; t.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 0);
|
ASSERT_EQ(xs.count(), 0);
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ TEST(TriangleTest, A_ray_strikes_a_triangle)
|
|||||||
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
Triangle t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
|
||||||
Ray r = Ray(Point(0, .5, -2), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, .5, -2), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = t.intersect(r);
|
Intersect xs; t.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 1);
|
ASSERT_EQ(xs.count(), 1);
|
||||||
EXPECT_EQ(xs[0].t, 2);
|
EXPECT_EQ(xs[0].t, 2);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ int main()
|
|||||||
Light light = Light(POINT_LIGHT, Point(0, 100, 0), Colour(1, 1, 1));
|
Light light = Light(POINT_LIGHT, Point(0, 100, 0), Colour(1, 1, 1));
|
||||||
w.addLight(&light);
|
w.addLight(&light);
|
||||||
|
|
||||||
Sphere sp = Sphere();;
|
Sphere sp = Sphere();
|
||||||
sp.setTransform(translation(0, 0, 5) * scaling(0.75, 0.75, 0.75));
|
sp.setTransform(translation(0, 0, 5) * scaling(0.75, 0.75, 0.75));
|
||||||
sp.material.diffuse = 0.4;
|
sp.material.diffuse = 0.4;
|
||||||
sp.material.specular = 0.6;
|
sp.material.specular = 0.6;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ TEST(WorldTest, Creating_a_world)
|
|||||||
{
|
{
|
||||||
World w;
|
World w;
|
||||||
|
|
||||||
ASSERT_EQ(w.lightCount, 0);
|
ASSERT_EQ(w.getLightCount(), 0);
|
||||||
ASSERT_EQ(w.objectCount, 0);
|
ASSERT_EQ(w.getObjectCount(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WorldTest, The_default_world)
|
TEST(WorldTest, The_default_world)
|
||||||
@@ -41,17 +41,21 @@ TEST(WorldTest, The_default_world)
|
|||||||
|
|
||||||
s2.setTransform(scaling(0.5, 0.5,0.5));
|
s2.setTransform(scaling(0.5, 0.5,0.5));
|
||||||
|
|
||||||
|
Shape *obj0 = w.getObject(0);
|
||||||
|
Shape *obj1 = w.getObject(1);
|
||||||
|
|
||||||
ASSERT_TRUE(w.lightIsIn(l));
|
ASSERT_TRUE(w.lightIsIn(l));
|
||||||
ASSERT_TRUE(w.objectIsIn(s1));
|
ASSERT_EQ(*obj0, s1);
|
||||||
ASSERT_TRUE(w.objectIsIn(s2));
|
ASSERT_EQ(*obj1, s2);
|
||||||
};
|
}
|
||||||
|
|
||||||
TEST(WorldTest, Intersect_a_world_with_a_ray)
|
TEST(WorldTest, Intersect_a_world_with_a_ray)
|
||||||
{
|
{
|
||||||
World w = DefaultWorld();
|
World w = DefaultWorld();
|
||||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
|
|
||||||
Intersect xs = w.intersect(r);
|
Intersect xs;
|
||||||
|
w.intersect(r, xs);
|
||||||
|
|
||||||
ASSERT_EQ(xs.count(), 4);
|
ASSERT_EQ(xs.count(), 4);
|
||||||
ASSERT_EQ(xs[0].t, 4);
|
ASSERT_EQ(xs[0].t, 4);
|
||||||
@@ -169,7 +173,7 @@ TEST(WorldTest, Shade_hit_is_given_an_intersection_in_shadow)
|
|||||||
Tuple c = w.shadeHit(comps);
|
Tuple c = w.shadeHit(comps);
|
||||||
|
|
||||||
ASSERT_EQ(c, Colour(0.1, 0.1, 0.1));
|
ASSERT_EQ(c, Colour(0.1, 0.1, 0.1));
|
||||||
};
|
}
|
||||||
|
|
||||||
TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material)
|
TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user