Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Courses
Scotty3D
Commits
c2535f0f
Commit
c2535f0f
authored
Sep 26, 2020
by
TheNumbat
Browse files
upstream changes
parent
8b8ee1f1
Changes
93
Hide whitespace changes
Inline
Side-by-side
src/scene/object.h
View file @
c2535f0f
#pragma once
#include "../platform/gl.h"
#include "../geometry/halfedge.h"
#include "../platform/gl.h"
#include "../rays/shapes.h"
#include "pose.h"
#include "material.h"
#include "pose.h"
#include "skeleton.h"
using
Scene_ID
=
unsigned
int
;
class
Scene_Object
{
public:
Scene_Object
()
=
default
;
Scene_Object
(
Scene_ID
id
,
Pose
pose
,
GL
::
Mesh
&&
mesh
,
std
::
string
n
=
{});
Scene_Object
(
Scene_ID
id
,
Pose
pose
,
Halfedge_Mesh
&&
mesh
,
std
::
string
n
=
{});
Scene_Object
(
const
Scene_Object
&
src
)
=
delete
;
Scene_Object
(
Scene_Object
&&
src
)
=
default
;
~
Scene_Object
()
=
default
;
void
operator
=
(
const
Scene_Object
&
src
)
=
delete
;
Scene_Object
&
operator
=
(
Scene_Object
&&
src
)
=
default
;
Scene_ID
id
()
const
{
return
_id
;}
void
sync_mesh
();
void
sync_anim_mesh
();
void
set_time
(
float
time
);
const
GL
::
Mesh
&
mesh
()
{
sync_mesh
();
return
_mesh
;}
const
GL
::
Mesh
&
posed_mesh
();
void
render
(
const
Mat4
&
view
,
bool
solid
=
false
,
bool
depth_only
=
false
,
bool
posed
=
true
,
bool
anim
=
true
);
Halfedge_Mesh
&
get_mesh
();
const
Halfedge_Mesh
&
get_mesh
()
const
;
void
copy_mesh
(
Halfedge_Mesh
&
out
);
void
take_mesh
(
Halfedge_Mesh
&&
in
);
void
set_mesh
(
Halfedge_Mesh
&
in
);
Halfedge_Mesh
::
ElementRef
set_mesh
(
Halfedge_Mesh
&
in
,
unsigned
int
eid
);
BBox
bbox
();
bool
is_editable
()
const
;
bool
is_shape
()
const
;
void
try_make_editable
(
PT
::
Shape_Type
prev
=
PT
::
Shape_Type
::
none
);
void
set_mesh_dirty
();
void
set_skel_dirty
();
void
set_pose_dirty
();
static
const
inline
int
max_name_len
=
256
;
struct
Options
{
char
name
[
max_name_len
]
=
{};
bool
wireframe
=
false
;
bool
smooth_normals
=
false
;
PT
::
Shape_Type
shape_type
=
PT
::
Shape_Type
::
none
;
PT
::
Shape
shape
;
};
Options
opt
;
Pose
pose
;
Anim_Pose
anim
;
Skeleton
armature
;
Material
material
;
mutable
bool
rig_dirty
=
false
;
Scene_Object
()
=
default
;
Scene_Object
(
Scene_ID
id
,
Pose
pose
,
GL
::
Mesh
&&
mesh
,
std
::
string
n
=
{});
Scene_Object
(
Scene_ID
id
,
Pose
pose
,
Halfedge_Mesh
&&
mesh
,
std
::
string
n
=
{});
Scene_Object
(
const
Scene_Object
&
src
)
=
delete
;
Scene_Object
(
Scene_Object
&&
src
)
=
default
;
~
Scene_Object
()
=
default
;
void
operator
=
(
const
Scene_Object
&
src
)
=
delete
;
Scene_Object
&
operator
=
(
Scene_Object
&&
src
)
=
default
;
Scene_ID
id
()
const
{
return
_id
;
}
void
sync_mesh
();
void
sync_anim_mesh
();
void
set_time
(
float
time
);
const
GL
::
Mesh
&
mesh
()
{
sync_mesh
();
return
_mesh
;
}
const
GL
::
Mesh
&
posed_mesh
();
void
render
(
const
Mat4
&
view
,
bool
solid
=
false
,
bool
depth_only
=
false
,
bool
posed
=
true
,
bool
anim
=
true
);
Halfedge_Mesh
&
get_mesh
();
const
Halfedge_Mesh
&
get_mesh
()
const
;
void
copy_mesh
(
Halfedge_Mesh
&
out
);
void
take_mesh
(
Halfedge_Mesh
&&
in
);
void
set_mesh
(
Halfedge_Mesh
&
in
);
Halfedge_Mesh
::
ElementRef
set_mesh
(
Halfedge_Mesh
&
in
,
unsigned
int
eid
);
BBox
bbox
();
bool
is_editable
()
const
;
bool
is_shape
()
const
;
void
try_make_editable
(
PT
::
Shape_Type
prev
=
PT
::
Shape_Type
::
none
);
void
set_mesh_dirty
();
void
set_skel_dirty
();
void
set_pose_dirty
();
static
const
inline
int
max_name_len
=
256
;
struct
Options
{
char
name
[
max_name_len
]
=
{};
bool
wireframe
=
false
;
bool
smooth_normals
=
false
;
PT
::
Shape_Type
shape_type
=
PT
::
Shape_Type
::
none
;
PT
::
Shape
shape
;
};
Options
opt
;
Pose
pose
;
Anim_Pose
anim
;
Skeleton
armature
;
Material
material
;
mutable
bool
rig_dirty
=
false
;
private:
Scene_ID
_id
=
0
;
Halfedge_Mesh
halfedge
;
mutable
GL
::
Mesh
_mesh
,
_anim_mesh
;
mutable
std
::
unordered_map
<
unsigned
int
,
std
::
vector
<
Joint
*>>
vertex_joints
;
mutable
bool
editable
=
true
;
mutable
bool
mesh_dirty
=
false
;
mutable
bool
skel_dirty
=
false
,
pose_dirty
=
false
;
Scene_ID
_id
=
0
;
Halfedge_Mesh
halfedge
;
mutable
GL
::
Mesh
_mesh
,
_anim_mesh
;
mutable
std
::
unordered_map
<
unsigned
int
,
std
::
vector
<
Joint
*>>
vertex_joints
;
mutable
bool
editable
=
true
;
mutable
bool
mesh_dirty
=
false
;
mutable
bool
skel_dirty
=
false
,
pose_dirty
=
false
;
};
bool
operator
!=
(
const
Scene_Object
::
Options
&
l
,
const
Scene_Object
::
Options
&
r
);
bool
operator
!=
(
const
Scene_Object
::
Options
&
l
,
const
Scene_Object
::
Options
&
r
);
src/scene/pose.cpp
View file @
c2535f0f
#include "pose.h"
Mat4
Pose
::
transform
()
const
{
return
Mat4
::
translate
(
pos
)
*
rotation_mat
()
*
Mat4
::
scale
(
scale
);
}
Mat4
Pose
::
transform
()
const
{
return
Mat4
::
translate
(
pos
)
*
rotation_mat
()
*
Mat4
::
scale
(
scale
);
}
Mat4
Pose
::
rotation_mat
()
const
{
return
Mat4
::
euler
(
euler
);
}
Mat4
Pose
::
rotation_mat
()
const
{
return
Mat4
::
euler
(
euler
);
}
Quat
Pose
::
rotation_quat
()
const
{
return
Quat
::
euler
(
euler
);
}
Quat
Pose
::
rotation_quat
()
const
{
return
Quat
::
euler
(
euler
);
}
bool
Pose
::
valid
()
const
{
return
pos
.
valid
()
&&
euler
.
valid
()
&&
scale
.
valid
();
}
bool
Pose
::
valid
()
const
{
return
pos
.
valid
()
&&
euler
.
valid
()
&&
scale
.
valid
();
}
void
Pose
::
clamp_euler
()
{
if
(
!
valid
())
return
;
while
(
euler
.
x
<
0
)
euler
.
x
+=
360.0
f
;
while
(
euler
.
x
>=
360.0
f
)
euler
.
x
-=
360.0
f
;
while
(
euler
.
y
<
0
)
euler
.
y
+=
360.0
f
;
while
(
euler
.
y
>=
360.0
f
)
euler
.
y
-=
360.0
f
;
while
(
euler
.
z
<
0
)
euler
.
z
+=
360.0
f
;
while
(
euler
.
z
>=
360.0
f
)
euler
.
z
-=
360.0
f
;
if
(
!
valid
())
return
;
while
(
euler
.
x
<
0
)
euler
.
x
+=
360.0
f
;
while
(
euler
.
x
>=
360.0
f
)
euler
.
x
-=
360.0
f
;
while
(
euler
.
y
<
0
)
euler
.
y
+=
360.0
f
;
while
(
euler
.
y
>=
360.0
f
)
euler
.
y
-=
360.0
f
;
while
(
euler
.
z
<
0
)
euler
.
z
+=
360.0
f
;
while
(
euler
.
z
>=
360.0
f
)
euler
.
z
-=
360.0
f
;
}
Pose
Pose
::
rotated
(
Vec3
angles
)
{
return
Pose
{
Vec3
{},
angles
,
Vec3
{
1.0
f
,
1.0
f
,
1.0
f
}};
}
Pose
Pose
::
rotated
(
Vec3
angles
)
{
return
Pose
{
Vec3
{},
angles
,
Vec3
{
1.0
f
,
1.0
f
,
1.0
f
}};
}
Pose
Pose
::
moved
(
Vec3
t
)
{
return
Pose
{
t
,
Vec3
{},
Vec3
{
1.0
f
,
1.0
f
,
1.0
f
}};
}
Pose
Pose
::
moved
(
Vec3
t
)
{
return
Pose
{
t
,
Vec3
{},
Vec3
{
1.0
f
,
1.0
f
,
1.0
f
}};
}
Pose
Pose
::
scaled
(
Vec3
s
)
{
return
Pose
{
Vec3
{},
Vec3
{},
s
};
}
Pose
Pose
::
scaled
(
Vec3
s
)
{
return
Pose
{
Vec3
{},
Vec3
{},
s
};
}
Pose
Pose
::
id
()
{
return
Pose
{
Vec3
{},
Vec3
{},
Vec3
{
1.0
f
,
1.0
f
,
1.0
f
}};
}
Pose
Pose
::
id
()
{
return
Pose
{
Vec3
{},
Vec3
{},
Vec3
{
1.0
f
,
1.0
f
,
1.0
f
}};
}
bool
operator
==
(
const
Pose
&
l
,
const
Pose
&
r
)
{
return
l
.
pos
==
r
.
pos
&&
l
.
euler
==
r
.
euler
&&
l
.
scale
==
r
.
scale
;
bool
operator
==
(
const
Pose
&
l
,
const
Pose
&
r
)
{
return
l
.
pos
==
r
.
pos
&&
l
.
euler
==
r
.
euler
&&
l
.
scale
==
r
.
scale
;
}
bool
operator
!=
(
const
Pose
&
l
,
const
Pose
&
r
)
{
return
l
.
pos
!=
r
.
pos
||
l
.
euler
!=
r
.
euler
||
l
.
scale
!=
r
.
scale
;
bool
operator
!=
(
const
Pose
&
l
,
const
Pose
&
r
)
{
return
l
.
pos
!=
r
.
pos
||
l
.
euler
!=
r
.
euler
||
l
.
scale
!=
r
.
scale
;
}
Pose
Anim_Pose
::
at
(
float
t
)
const
{
auto
[
p
,
r
,
s
]
=
splines
.
at
(
t
);
return
Pose
{
p
,
r
.
to_euler
(),
s
};
}
void
Anim_Pose
::
set
(
float
t
,
Pose
p
)
{
splines
.
set
(
t
,
p
.
pos
,
Quat
::
euler
(
p
.
euler
),
p
.
scale
);
auto
[
p
,
r
,
s
]
=
splines
.
at
(
t
);
return
Pose
{
p
,
r
.
to_euler
(),
s
};
}
void
Anim_Pose
::
set
(
float
t
,
Pose
p
)
{
splines
.
set
(
t
,
p
.
pos
,
Quat
::
euler
(
p
.
euler
),
p
.
scale
);
}
src/scene/pose.h
View file @
c2535f0f
#pragma once
#include <set>
#include "../lib/mathlib.h"
#include "../geometry/spline.h"
#include "../lib/mathlib.h"
#include <set>
struct
Pose
{
Vec3
pos
;
Vec3
euler
;
Vec3
scale
=
Vec3
{
1
.
0
f
};
Vec3
pos
;
Vec3
euler
;
Vec3
scale
=
Vec3
{
1
.
0
f
};
Mat4
transform
()
const
;
Mat4
rotation_mat
()
const
;
Quat
rotation_quat
()
const
;
Mat4
transform
()
const
;
Mat4
rotation_mat
()
const
;
Quat
rotation_quat
()
const
;
void
clamp_euler
();
bool
valid
()
const
;
void
clamp_euler
();
bool
valid
()
const
;
static
Pose
rotated
(
Vec3
angles
);
static
Pose
moved
(
Vec3
t
);
static
Pose
scaled
(
Vec3
s
);
static
Pose
id
();
static
Pose
rotated
(
Vec3
angles
);
static
Pose
moved
(
Vec3
t
);
static
Pose
scaled
(
Vec3
s
);
static
Pose
id
();
};
bool
operator
==
(
const
Pose
&
l
,
const
Pose
&
r
);
bool
operator
!=
(
const
Pose
&
l
,
const
Pose
&
r
);
bool
operator
==
(
const
Pose
&
l
,
const
Pose
&
r
);
bool
operator
!=
(
const
Pose
&
l
,
const
Pose
&
r
);
struct
Anim_Pose
{
Pose
at
(
float
t
)
const
;
void
set
(
float
t
,
Pose
p
);
Splines
<
Vec3
,
Quat
,
Vec3
>
splines
;
Pose
at
(
float
t
)
const
;
void
set
(
float
t
,
Pose
p
);
Splines
<
Vec3
,
Quat
,
Vec3
>
splines
;
};
src/scene/renderer.cpp
View file @
c2535f0f
#include <imgui/imgui.h>
#include "../geometry/util.h"
#include "../gui/manager.h"
#include "../lib/mathlib.h"
#include "../geometry/util.h"
#include "renderer.h"
#include "scene.h"
static
const
int
DEFAULT_SAMPLES
=
4
;
Renderer
::
Renderer
(
Vec2
dim
)
:
framebuffer
(
2
,
dim
,
DEFAULT_SAMPLES
,
true
),
id_resolve
(
1
,
dim
,
1
,
false
),
save_buffer
(
1
,
dim
,
DEFAULT_SAMPLES
,
true
),
save_output
(
1
,
dim
,
1
,
false
),
mesh_shader
(
GL
::
Shaders
::
mesh_v
,
GL
::
Shaders
::
mesh_f
),
line_shader
(
GL
::
Shaders
::
line_v
,
GL
::
Shaders
::
line_f
),
inst_shader
(
GL
::
Shaders
::
inst_v
,
GL
::
Shaders
::
mesh_f
),
dome_shader
(
GL
::
Shaders
::
dome_v
,
GL
::
Shaders
::
dome_f
),
_sphere
(
Util
::
sphere_mesh
(
1.0
f
,
3
)),
_cyl
(
Util
::
cyl_mesh
(
1.0
f
,
1.0
f
,
64
,
false
)),
_hemi
(
Util
::
hemi_mesh
(
1.0
f
)),
samples
(
DEFAULT_SAMPLES
),
window_dim
(
dim
),
id_buffer
(
new
GLubyte
[(
int
)
dim
.
x
*
(
int
)
dim
.
y
*
4
])
{}
Renderer
::
Renderer
(
Vec2
dim
)
:
framebuffer
(
2
,
dim
,
DEFAULT_SAMPLES
,
true
),
id_resolve
(
1
,
dim
,
1
,
false
),
save_buffer
(
1
,
dim
,
DEFAULT_SAMPLES
,
true
),
save_output
(
1
,
dim
,
1
,
false
),
mesh_shader
(
GL
::
Shaders
::
mesh_v
,
GL
::
Shaders
::
mesh_f
),
line_shader
(
GL
::
Shaders
::
line_v
,
GL
::
Shaders
::
line_f
),
inst_shader
(
GL
::
Shaders
::
inst_v
,
GL
::
Shaders
::
mesh_f
),
dome_shader
(
GL
::
Shaders
::
dome_v
,
GL
::
Shaders
::
dome_f
),
_sphere
(
Util
::
sphere_mesh
(
1.0
f
,
3
)),
_cyl
(
Util
::
cyl_mesh
(
1.0
f
,
1.0
f
,
64
,
false
)),
_hemi
(
Util
::
hemi_mesh
(
1.0
f
)),
samples
(
DEFAULT_SAMPLES
),
window_dim
(
dim
),
id_buffer
(
new
GLubyte
[(
int
)
dim
.
x
*
(
int
)
dim
.
y
*
4
])
{}
Renderer
::~
Renderer
()
{
delete
[]
id_buffer
;
id_buffer
=
nullptr
;
delete
[]
id_buffer
;
id_buffer
=
nullptr
;
}
Renderer
&
Renderer
::
get
()
{
assert
(
data
);
return
*
data
;
Renderer
&
Renderer
::
get
()
{
assert
(
data
);
return
*
data
;
}
void
Renderer
::
setup
(
Vec2
dim
)
{
data
=
new
Renderer
(
dim
);
}
void
Renderer
::
setup
(
Vec2
dim
)
{
data
=
new
Renderer
(
dim
);
}
void
Renderer
::
update_dim
(
Vec2
dim
)
{
window_dim
=
dim
;
delete
[]
id_buffer
;
id_buffer
=
new
GLubyte
[(
int
)
dim
.
x
*
(
int
)
dim
.
y
*
4
]();
framebuffer
.
resize
(
dim
,
samples
);
save_buffer
.
resize
(
dim
,
save_buffer
.
samples
());
id_resolve
.
resize
(
dim
);
save_output
.
resize
(
dim
);
window_dim
=
dim
;
delete
[]
id_buffer
;
id_buffer
=
new
GLubyte
[(
int
)
dim
.
x
*
(
int
)
dim
.
y
*
4
]();
framebuffer
.
resize
(
dim
,
samples
);
save_buffer
.
resize
(
dim
,
save_buffer
.
samples
());
id_resolve
.
resize
(
dim
);
save_output
.
resize
(
dim
);
}
void
Renderer
::
shutdown
()
{
delete
data
;
data
=
nullptr
;
delete
data
;
data
=
nullptr
;
}
void
Renderer
::
proj
(
const
Mat4
&
proj
)
{
_proj
=
proj
;
}
void
Renderer
::
proj
(
const
Mat4
&
proj
)
{
_proj
=
proj
;
}
void
Renderer
::
complete
()
{
framebuffer
.
blit_to
(
1
,
id_resolve
,
false
);
if
(
!
id_resolve
.
can_read_at
())
id_resolve
.
read
(
0
,
id_buffer
);
framebuffer
.
blit_to
(
1
,
id_resolve
,
false
);
if
(
!
id_resolve
.
can_read_at
())
id_resolve
.
read
(
0
,
id_buffer
);
framebuffer
.
blit_to_screen
(
0
,
window_dim
);
framebuffer
.
blit_to_screen
(
0
,
window_dim
);
}
void
Renderer
::
begin
()
{
framebuffer
.
clear
(
0
,
Vec4
(
Gui
::
Color
::
background
,
1.0
f
));
framebuffer
.
clear
(
1
,
Vec4
{
0.0
f
,
0.0
f
,
0.0
f
,
1.0
f
});
framebuffer
.
clear_d
();
framebuffer
.
bind
();
GL
::
viewport
(
window_dim
);
framebuffer
.
clear
(
0
,
Vec4
(
Gui
::
Color
::
background
,
1.0
f
));
framebuffer
.
clear
(
1
,
Vec4
{
0.0
f
,
0.0
f
,
0.0
f
,
1.0
f
});
framebuffer
.
clear_d
();
framebuffer
.
bind
();
GL
::
viewport
(
window_dim
);
}
void
Renderer
::
save
(
Scene
&
scene
,
const
Camera
&
cam
,
int
w
,
int
h
,
int
s
)
{
Vec2
dim
((
float
)
w
,
(
float
)
h
);
void
Renderer
::
save
(
Scene
&
scene
,
const
Camera
&
cam
,
int
w
,
int
h
,
int
s
)
{
Vec2
dim
((
float
)
w
,
(
float
)
h
);
save_buffer
.
resize
(
dim
,
s
);
save_output
.
resize
(
dim
);
save_buffer
.
clear
(
0
,
Vec4
{
0.0
f
,
0.0
f
,
0.0
f
,
1.0
f
});
save_buffer
.
bind
();
GL
::
viewport
(
dim
);
save_buffer
.
resize
(
dim
,
s
);
save_output
.
resize
(
dim
);
save_buffer
.
clear
(
0
,
Vec4
{
0.0
f
,
0.0
f
,
0.0
f
,
1.0
f
});
save_buffer
.
bind
();
GL
::
viewport
(
dim
);
Mat4
view
=
cam
.
get_view
();
scene
.
for_items
([
&
](
Scene_Item
&
item
)
{
if
(
item
.
is
<
Scene_Light
>
())
return
;
item
.
render
(
view
);
});
Mat4
view
=
cam
.
get_view
();
scene
.
for_items
([
&
](
Scene_Item
&
item
)
{
if
(
item
.
is
<
Scene_Light
>
())
return
;
item
.
render
(
view
);
});
save_buffer
.
blit_to
(
0
,
save_output
,
true
);
save_buffer
.
blit_to
(
0
,
save_output
,
true
);
framebuffer
.
bind
();
GL
::
viewport
(
window_dim
);
framebuffer
.
bind
();
GL
::
viewport
(
window_dim
);
}
void
Renderer
::
saved
(
std
::
vector
<
unsigned
char
>
&
out
)
const
{
save_output
.
flush
();
out
.
resize
(
save_output
.
bytes
());
save_output
.
read
(
0
,
out
.
data
());
void
Renderer
::
saved
(
std
::
vector
<
unsigned
char
>
&
out
)
const
{
save_output
.
flush
();
out
.
resize
(
save_output
.
bytes
());
save_output
.
read
(
0
,
out
.
data
());
}
GLuint
Renderer
::
saved
()
const
{
save_output
.
flush
();
return
save_output
.
get_output
(
0
);
save_output
.
flush
();
return
save_output
.
get_output
(
0
);
}
void
Renderer
::
lines
(
const
GL
::
Lines
&
lines
,
const
Mat4
&
view
,
const
Mat4
&
model
,
float
alpha
)
{
void
Renderer
::
lines
(
const
GL
::
Lines
&
lines
,
const
Mat4
&
view
,
const
Mat4
&
model
,
float
alpha
)
{
Mat4
mvp
=
_proj
*
view
*
model
;
line_shader
.
bind
();
line_shader
.
uniform
(
"mvp"
,
mvp
);
line_shader
.
uniform
(
"alpha"
,
alpha
);
lines
.
render
(
framebuffer
.
is_multisampled
());
Mat4
mvp
=
_proj
*
view
*
model
;
line_shader
.
bind
();
line_shader
.
uniform
(
"mvp"
,
mvp
);
line_shader
.
uniform
(
"alpha"
,
alpha
);
lines
.
render
(
framebuffer
.
is_multisampled
());
}
void
Renderer
::
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
,
const
GL
::
Tex2D
&
tex
)
{
void
Renderer
::
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
,
const
GL
::
Tex2D
&
tex
)
{
tex
.
bind
();
dome_shader
.
bind
();
dome_shader
.
uniform
(
"tex"
,
0
);
dome_shader
.
uniform
(
"use_texture"
,
true
);
dome_shader
.
uniform
(
"color"
,
color
);
dome_shader
.
uniform
(
"cosine"
,
cosine
);
dome_shader
.
uniform
(
"transform"
,
_proj
*
rotation
);
_sphere
.
render
();
tex
.
bind
();
dome_shader
.
bind
();
dome_shader
.
uniform
(
"tex"
,
0
);
dome_shader
.
uniform
(
"use_texture"
,
true
);
dome_shader
.
uniform
(
"color"
,
color
);
dome_shader
.
uniform
(
"cosine"
,
cosine
);
dome_shader
.
uniform
(
"transform"
,
_proj
*
rotation
);
_sphere
.
render
();
}
void
Renderer
::
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
)
{
void
Renderer
::
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
)
{
dome_shader
.
bind
();
dome_shader
.
uniform
(
"use_texture"
,
false
);
dome_shader
.
uniform
(
"color"
,
color
);
dome_shader
.
uniform
(
"cosine"
,
cosine
);
dome_shader
.
uniform
(
"transform"
,
_proj
*
rotation
);
_sphere
.
render
();
dome_shader
.
bind
();
dome_shader
.
uniform
(
"use_texture"
,
false
);
dome_shader
.
uniform
(
"color"
,
color
);
dome_shader
.
uniform
(
"cosine"
,
cosine
);
dome_shader
.
uniform
(
"transform"
,
_proj
*
rotation
);
_sphere
.
render
();
}
void
Renderer
::
sphere
(
MeshOpt
opt
)
{
mesh
(
_sphere
,
opt
);
}
void
Renderer
::
sphere
(
MeshOpt
opt
)
{
mesh
(
_sphere
,
opt
);
}
void
Renderer
::
capsule
(
MeshOpt
opt
,
const
Mat4
&
mdl
,
float
height
,
float
rad
,
BBox
&
box
)
{
void
Renderer
::
capsule
(
MeshOpt
opt
,
const
Mat4
&
mdl
,
float
height
,
float
rad
,
BBox
&
box
)
{
Mat4
T
=
opt
.
modelview
;
Mat4
cyl
=
mdl
*
Mat4
::
scale
(
Vec3
{
rad
,
height
,
rad
});
Mat4
bot
=
mdl
*
Mat4
::
scale
(
Vec3
{
rad
});
Mat4
top
=
mdl
*
Mat4
::
translate
(
Vec3
{
0.0
f
,
height
,
0.0
f
})
*
Mat4
::
euler
(
Vec3
{
180.0
f
,
0.0
f
,
0.0
f
})
*
Mat4
::
scale
(
Vec3
{
rad
});
Mat4
T
=
opt
.
modelview
;
Mat4
cyl
=
mdl
*
Mat4
::
scale
(
Vec3
{
rad
,
height
,
rad
});
Mat4
bot
=
mdl
*
Mat4
::
scale
(
Vec3
{
rad
});
Mat4
top
=
mdl
*
Mat4
::
translate
(
Vec3
{
0.0
f
,
height
,
0.0
f
})
*
Mat4
::
euler
(
Vec3
{
180.0
f
,
0.0
f
,
0.0
f
})
*
Mat4
::
scale
(
Vec3
{
rad
});
opt
.
modelview
=
T
*
cyl
;
mesh
(
_cyl
,
opt
);
opt
.
modelview
=
T
*
bot
;
mesh
(
_hemi
,
opt
);
opt
.
modelview
=
T
*
top
;
mesh
(
_hemi
,
opt
);
BBox
b
=
_cyl
.
bbox
();
b
.
transform
(
cyl
);
box
.
enclose
(
b
);
b
=
_hemi
.
bbox
();
b
.
transform
(
bot
);
box
.
enclose
(
b
);
b
=
_hemi
.
bbox
();
b
.
transform
(
top
);
box
.
enclose
(
b
);
mesh
(
_cyl
,
opt
);
opt
.
modelview
=
T
*
bot
;
mesh
(
_hemi
,
opt
);
opt
.
modelview
=
T
*
top
;
mesh
(
_hemi
,
opt
);
BBox
b
=
_cyl
.
bbox
();
b
.
transform
(
cyl
);
box
.
enclose
(
b
);
b
=
_hemi
.
bbox
();
b
.
transform
(
bot
);
box
.
enclose
(
b
);
b
=
_hemi
.
bbox
();
b
.
transform
(
top
);
box
.
enclose
(
b
);
}
void
Renderer
::
capsule
(
MeshOpt
opt
,
float
height
,
float
rad
)
{
BBox
box
;
capsule
(
opt
,
Mat4
::
I
,
height
,
rad
,
box
);
BBox
box
;
capsule
(
opt
,
Mat4
::
I
,
height
,
rad
,
box
);
}
void
Renderer
::
mesh
(
GL
::
Mesh
&
mesh
,
Renderer
::
MeshOpt
opt
)
{
void
Renderer
::
mesh
(
GL
::
Mesh
&
mesh
,
Renderer
::
MeshOpt
opt
)
{
mesh_shader
.
bind
();
mesh_shader
.
uniform
(
"use_v_id"
,
opt
.
per_vert_id
);
mesh_shader
.
uniform
(
"id"
,
opt
.
id
);
mesh_shader
.
uniform
(
"alpha"
,
opt
.
alpha
);
mesh_shader
.
uniform
(
"mvp"
,
_proj
*
opt
.
modelview
);
mesh_shader
.
uniform
(
"normal"
,
Mat4
::
transpose
(
Mat4
::
inverse
(
opt
.
modelview
)));
mesh_shader
.
uniform
(
"solid"
,
opt
.
solid_color
);
mesh_shader
.
uniform
(
"sel_color"
,
opt
.
sel_color
);
mesh_shader
.
uniform
(
"sel_id"
,
opt
.
sel_id
);
mesh_shader
.
uniform
(
"hov_color"
,
opt
.
hov_color
);
mesh_shader
.
uniform
(
"hov_id"
,
opt
.
hov_id
);
if
(
opt
.
depth_only
)
GL
::
color_mask
(
false
);
if
(
opt
.
wireframe
)
{
mesh_shader
.
uniform
(
"color"
,
Vec3
());
GL
::
enable
(
GL
::
Opt
::
wireframe
);
mesh
.
render
();
GL
::
disable
(
GL
::
Opt
::
wireframe
);
}
mesh_shader
.
uniform
(
"color"
,
opt
.
color
);
mesh
.
render
();
if
(
opt
.
depth_only
)
GL
::
color_mask
(
true
);
mesh_shader
.
uniform
(
"use_v_id"
,
opt
.
per_vert_id
);
mesh_shader
.
uniform
(
"id"
,
opt
.
id
);
mesh_shader
.
uniform
(
"alpha"
,
opt
.
alpha
);
mesh_shader
.
uniform
(
"mvp"
,
_proj
*
opt
.
modelview
);
mesh_shader
.
uniform
(
"normal"
,
Mat4
::
transpose
(
Mat4
::
inverse
(
opt
.
modelview
)));
mesh_shader
.
uniform
(
"solid"
,
opt
.
solid_color
);
mesh_shader
.
uniform
(
"sel_color"
,
opt
.
sel_color
);
mesh_shader
.
uniform
(
"sel_id"
,
opt
.
sel_id
);
mesh_shader
.
uniform
(
"hov_color"
,
opt
.
hov_color
);
mesh_shader
.
uniform
(
"hov_id"
,
opt
.
hov_id
);
if
(
opt
.
depth_only
)
GL
::
color_mask
(
false
);
if
(
opt
.
wireframe
)
{
mesh_shader
.
uniform
(
"color"
,
Vec3
());
GL
::
enable
(
GL
::
Opt
::
wireframe
);
mesh
.
render
();
GL
::
disable
(
GL
::
Opt
::
wireframe
);
}
mesh_shader
.
uniform
(
"color"
,
opt
.
color
);
mesh
.
render
();
if
(
opt
.
depth_only
)
GL
::
color_mask
(
true
);
}
void
Renderer
::
set_samples
(
int
s
)
{
samples
=
s
;
framebuffer
.
resize
(
window_dim
,
samples
);
samples
=
s
;
framebuffer
.
resize
(
window_dim
,
samples
);
}
Scene_ID
Renderer
::
read_id
(
Vec2
pos
)
{
int
x
=
(
int
)
pos
.
x
;
int
y
=
(
int
)(
window_dim
.
y
-
pos
.
y
-
1
);
int
x
=
(
int
)
pos
.
x
;
int
y
=
(
int
)(
window_dim
.
y
-
pos
.
y
-
1
);
if
(
id_resolve
.
can_read_at
())
{
if
(
id_resolve
.
can_read_at
())
{
GLubyte
read
[
4
]
=
{};
id_resolve
.
read_at
(
0
,
x
,
y
,
read
);
return
(
int
)
read
[
0
]
|
(
int
)
read
[
1
]
<<
8
|
(
int
)
read
[
2
]
<<
16
;
GLubyte
read
[
4
]
=
{};
id_resolve
.
read_at
(
0
,
x
,
y
,
read
);
return
(
int
)
read
[
0
]
|
(
int
)
read
[
1
]
<<
8
|
(
int
)
read
[
2
]
<<
16
;
}
else
{
int
idx
=
y
*
(
int
)
window_dim
.
x
*
4
+
x
*
4
;
assert
(
id_buffer
&&
idx
>
0
&&
idx
<=
window_dim
.
x
*
window_dim
.
y
*
4
);
int
a
=
id_buffer
[
idx
];
int
b
=
id_buffer
[
idx
+
1
];
int
c
=
id_buffer
[
idx
+
2
];
}
else
{
return
a
|
b
<<
8
|
c
<<
16
;
}
}
int
idx
=
y
*
(
int
)
window_dim
.
x
*
4
+
x
*
4
;
assert
(
id_buffer
&&
idx
>
0
&&
idx
<=
window_dim
.
x
*
window_dim
.
y
*
4
);
void
Renderer
::
reset_depth
()
{
framebuffer
.
clear_d
()
;
}
int
a
=
id_buffer
[
idx
];
int
b
=
id_buffer
[
idx
+
1
]
;
int
c
=
id_buffer
[
idx
+
2
];
void
Renderer
::
begin_outline
()
{
framebuffer
.
clear_d
();
return
a
|
b
<<
8
|
c
<<
16
;
}
}
void
Renderer
::
end_outline
(
const
Mat4
&
view
,
BBox
box
)
{
void
Renderer
::
reset_depth
()
{
framebuffer
.
clear_d
();
}
void
Renderer
::
begin_outline
()
{
framebuffer
.
clear_d
();
}
void
Renderer
::
end_outline
(
const
Mat4
&
view
,
BBox
box
)
{
Mat4
viewproj
=
_proj
*
view
;
Vec2
min
,
max
;
box
.
screen_rect
(
viewproj
,
min
,
max
);
Mat4
viewproj
=
_proj
*
view
;
Vec2
min
,
max
;
box
.
screen_rect
(
viewproj
,
min
,
max
);
Vec2
thickness
=
Vec2
(
3.0
f
/
window_dim
.
x
,
3.0
f
/
window_dim
.
y
);
GL
::
Effects
::
outline
(
framebuffer
,
framebuffer
,
Gui
::
Color
::
outline
,
min
-
thickness
,
max
+
thickness
);
Vec2
thickness
=
Vec2
(
3.0
f
/
window_dim
.
x
,
3.0
f
/
window_dim
.
y
);
GL
::
Effects
::
outline
(
framebuffer
,
framebuffer
,
Gui
::
Color
::
outline
,
min
-
thickness
,
max
+
thickness
);
}
void
Renderer
::
outline
(
const
Mat4
&
view
,
Scene_Item
&
obj
)
{
void
Renderer
::
outline
(
const
Mat4
&
view
,
Scene_Item
&
obj
)
{
Mat4
viewproj
=
_proj
*
view
;
Mat4
viewproj
=
_proj
*
view
;
framebuffer
.
clear_d
();
obj
.
render
(
view
,
false
,
true
);
framebuffer
.
clear_d
();
obj
.
render
(
view
,
false
,
true
);
Vec2
min
,
max
;
obj
.
bbox
().
screen_rect
(
viewproj
,
min
,
max
);
Vec2
min
,
max
;
obj
.
bbox
().
screen_rect
(
viewproj
,
min
,
max
);
Vec2
thickness
=
Vec2
(
3.0
f
/
window_dim
.
x
,
3.0
f
/
window_dim
.
y
);
GL
::
Effects
::
outline
(
framebuffer
,
framebuffer
,
Gui
::
Color
::
outline
,
min
-
thickness
,
max
+
thickness
);
Vec2
thickness
=
Vec2
(
3.0
f
/
window_dim
.
x
,
3.0
f
/
window_dim
.
y
);
GL
::
Effects
::
outline
(
framebuffer
,
framebuffer
,
Gui
::
Color
::
outline
,
min
-
thickness
,
max
+
thickness
);
}
void
Renderer
::
halfedge_editor
(
Renderer
::
HalfedgeOpt
opt
)
{
auto
[
faces
,
spheres
,
cylinders
,
arrows
]
=
opt
.
editor
.
shapes
();
MeshOpt
fopt
=
MeshOpt
();
fopt
.
modelview
=
opt
.
modelview
;
fopt
.
color
=
opt
.
color
;
fopt
.
per_vert_id
=
true
;
fopt
.
sel_color
=
Gui
::
Color
::
outline
;
fopt
.
sel_id
=
opt
.
editor
.
select_id
();
fopt
.
hov_color
=
Gui
::
Color
::
hover
;
fopt
.
hov_id
=
opt
.
editor
.
hover_id
();
Renderer
::
mesh
(
faces
,
fopt
);
inst_shader
.
bind
();
inst_shader
.
uniform
(
"use_v_id"
,
true
);
inst_shader
.
uniform
(
"use_i_id"
,
true
);
inst_shader
.
uniform
(
"solid"
,
false
);
inst_shader
.
uniform
(
"proj"
,
_proj
);
inst_shader
.
uniform
(
"modelview"
,
opt
.
modelview
);
inst_shader
.
uniform
(
"color"
,
opt
.
color
);
inst_shader
.
uniform
(
"alpha"
,
fopt
.
alpha
);
inst_shader
.
uniform
(
"sel_color"
,
Gui
::
Color
::
outline
);
inst_shader
.
uniform
(
"hov_color"
,
Gui
::
Color
::
hover
);
inst_shader
.
uniform
(
"sel_id"
,
fopt
.
sel_id
);
inst_shader
.
uniform
(
"hov_id"
,
fopt
.
hov_id
);
spheres
.
render
();
cylinders
.
render
();
arrows
.
render
();
auto
[
faces
,
spheres
,
cylinders
,
arrows
]
=
opt
.
editor
.
shapes
();
MeshOpt
fopt
=
MeshOpt
();
fopt
.
modelview
=
opt
.
modelview
;
fopt
.
color
=
opt
.
color
;
fopt
.
per_vert_id
=
true
;
fopt
.
sel_color
=
Gui
::
Color
::
outline
;
fopt
.
sel_id
=
opt
.
editor
.
select_id
();
fopt
.
hov_color
=
Gui
::
Color
::
hover
;
fopt
.
hov_id
=
opt
.
editor
.
hover_id
();
Renderer
::
mesh
(
faces
,
fopt
);
inst_shader
.
bind
();
inst_shader
.
uniform
(
"use_v_id"
,
true
);
inst_shader
.
uniform
(
"use_i_id"
,
true
);
inst_shader
.
uniform
(
"solid"
,
false
);
inst_shader
.
uniform
(
"proj"
,
_proj
);
inst_shader
.
uniform
(
"modelview"
,
opt
.
modelview
);
inst_shader
.
uniform
(
"color"
,
opt
.
color
);
inst_shader
.
uniform
(
"alpha"
,
fopt
.
alpha
);
inst_shader
.
uniform
(
"sel_color"
,
Gui
::
Color
::
outline
);
inst_shader
.
uniform
(
"hov_color"
,
Gui
::
Color
::
hover
);
inst_shader
.
uniform
(
"sel_id"
,
fopt
.
sel_id
);
inst_shader
.
uniform
(
"hov_id"
,
fopt
.
hov_id
);
spheres
.
render
();
cylinders
.
render
();
arrows
.
render
();
}
src/scene/renderer.h
View file @
c2535f0f
...
...
@@ -3,26 +3,28 @@
#include <variant>
#include "scene.h"
#include "../platform/gl.h"
#include "../lib/bbox.h"
#include "../platform/gl.h"
#include "scene.h"
namespace
Gui
{
class
Model
;
}
namespace
Gui
{
class
Model
;
}
// Singleton
class
Renderer
{
public:
static
void
setup
(
Vec2
dim
);
static
void
shutdown
();
static
Renderer
&
get
();
static
Renderer
&
get
();
void
begin
();
void
complete
();
void
reset_depth
();
void
proj
(
const
Mat4
&
proj
);
void
proj
(
const
Mat4
&
proj
);
void
update_dim
(
Vec2
dim
);
void
settings_gui
(
bool
*
open
);
void
settings_gui
(
bool
*
open
);
void
set_samples
(
int
samples
);
unsigned
int
read_id
(
Vec2
pos
);
...
...
@@ -36,47 +38,48 @@ public:
bool
solid_color
=
false
;
bool
depth_only
=
false
;
bool
per_vert_id
=
false
;
};
};
struct
HalfedgeOpt
{
HalfedgeOpt
(
Gui
::
Model
&
e
)
:
editor
(
e
)
{}
Gui
::
Model
&
editor
;
HalfedgeOpt
(
Gui
::
Model
&
e
)
:
editor
(
e
)
{}
Gui
::
Model
&
editor
;
Mat4
modelview
;
Vec3
color
;
};
// NOTE(max): updates & uses the indices in mesh for selection/traversal
void
halfedge_editor
(
HalfedgeOpt
opt
);
void
mesh
(
GL
::
Mesh
&
mesh
,
MeshOpt
opt
);
void
lines
(
const
GL
::
Lines
&
lines
,
const
Mat4
&
view
,
const
Mat4
&
model
=
Mat4
::
I
,
float
alpha
=
1.0
f
);
void
outline
(
const
Mat4
&
view
,
Scene_Item
&
obj
);
void
mesh
(
GL
::
Mesh
&
mesh
,
MeshOpt
opt
);
void
lines
(
const
GL
::
Lines
&
lines
,
const
Mat4
&
view
,
const
Mat4
&
model
=
Mat4
::
I
,
float
alpha
=
1.0
f
);
void
outline
(
const
Mat4
&
view
,
Scene_Item
&
obj
);
void
begin_outline
();
void
end_outline
(
const
Mat4
&
view
,
BBox
box
);
void
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
);
void
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
,
const
GL
::
Tex2D
&
tex
);
void
end_outline
(
const
Mat4
&
view
,
BBox
box
);
void
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
);
void
skydome
(
const
Mat4
&
rotation
,
Vec3
color
,
float
cosine
,
const
GL
::
Tex2D
&
tex
);
void
sphere
(
MeshOpt
opt
);
void
capsule
(
MeshOpt
opt
,
float
height
,
float
rad
);
void
capsule
(
MeshOpt
opt
,
const
Mat4
&
mdl
,
float
height
,
float
rad
,
BBox
&
box
);
void
capsule
(
MeshOpt
opt
,
const
Mat4
&
mdl
,
float
height
,
float
rad
,
BBox
&
box
);
GLuint
saved
()
const
;
void
saved
(
std
::
vector
<
unsigned
char
>
&
data
)
const
;
void
save
(
Scene
&
scene
,
const
Camera
&
cam
,
int
w
,
int
h
,
int
samples
);
void
saved
(
std
::
vector
<
unsigned
char
>
&
data
)
const
;
void
save
(
Scene
&
scene
,
const
Camera
&
cam
,
int
w
,
int
h
,
int
samples
);
private:
Renderer
(
Vec2
dim
);
~
Renderer
();
static
inline
Renderer
*
data
=
nullptr
;
static
inline
Renderer
*
data
=
nullptr
;
GL
::
Framebuffer
framebuffer
,
id_resolve
,
save_buffer
,
save_output
;
GL
::
Shader
mesh_shader
,
line_shader
,
inst_shader
,
dome_shader
;
GL
::
Framebuffer
framebuffer
,
id_resolve
,
save_buffer
,
save_output
;
GL
::
Shader
mesh_shader
,
line_shader
,
inst_shader
,
dome_shader
;
GL
::
Mesh
_sphere
,
_cyl
,
_hemi
;
int
samples
;
Vec2
window_dim
;
GLubyte
*
id_buffer
;
GLubyte
*
id_buffer
;
Mat4
_proj
;
};
src/scene/scene.cpp
View file @
c2535f0f
#include <sstream>
#include <assimp/Importer.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <sstream>
#include "../lib/log.h"
#include "../gui/manager.h"
#include "../gui/render.h"
#include "../lib/log.h"
#include "scene.h"
#include "renderer.h"
#include "scene.h"
#include "undo.h"
namespace
std
{
template
<
typename
T1
,
typename
T2
>
struct
hash
<
pair
<
T1
,
T2
>>
{
uint64_t
operator
()(
const
pair
<
T1
,
T2
>&
p
)
const
{
static
const
hash
<
T1
>
h1
;
static
const
hash
<
T2
>
h2
;
return
h1
(
p
.
first
)
^
h2
(
p
.
second
);
}
};
template
<
typename
T1
,
typename
T2
>
struct
hash
<
pair
<
T1
,
T2
>>
{
uint64_t
operator
()(
const
pair
<
T1
,
T2
>
&
p
)
const
{
static
const
hash
<
T1
>
h1
;
static
const
hash
<
T2
>
h2
;
return
h1
(
p
.
first
)
^
h2
(
p
.
second
);
}
};
};
// namespace std
Scene_Item
::
Scene_Item
(
Scene_Object
&&
obj
)
:
data
(
std
::
move
(
obj
))
{}
Scene_Item
::
Scene_Item
(
Scene_Object
&&
obj
)
:
data
(
std
::
move
(
obj
))
{}
Scene_Item
::
Scene_Item
(
Scene_Light
&&
light
)
:
data
(
std
::
move
(
light
))
{}
Scene_Item
::
Scene_Item
(
Scene_Light
&&
light
)
:
data
(
std
::
move
(
light
))
{}
Scene_Item
::
Scene_Item
(
Scene_Item
&&
src
)
:
data
(
std
::
move
(
src
.
data
))
{}
Scene_Item
::
Scene_Item
(
Scene_Item
&&
src
)
:
data
(
std
::
move
(
src
.
data
))
{}
Scene_Item
&
Scene_Item
::
operator
=
(
Scene_Item
&&
src
)
{
data
=
std
::
move
(
src
.
data
);
return
*
this
;
Scene_Item
&
Scene_Item
::
operator
=
(
Scene_Item
&&
src
)
{
data
=
std
::
move
(
src
.
data
);
return
*
this
;
}
void
Scene_Item
::
set_time
(
float
time
)
{
return
std
::
visit
(
overloaded
{
[
time
](
Scene_Object
&
obj
)
{
obj
.
set_time
(
time
);
},
[
time
](
Scene_Light
&
light
)
{
light
.
set_time
(
time
);
}
},
data
);
return
std
::
visit
(
overloaded
{[
time
](
Scene_Object
&
obj
)
{
obj
.
set_time
(
time
);
},
[
time
](
Scene_Light
&
light
)
{
light
.
set_time
(
time
);
}},
data
);
}
BBox
Scene_Item
::
bbox
()
{
return
std
::
visit
(
overloaded
{
[](
Scene_Object
&
obj
)
{
return
obj
.
bbox
();
},
[](
Scene_Light
&
light
)
{
return
light
.
bbox
();
}
},
data
);
return
std
::
visit
(
overloaded
{[](
Scene_Object
&
obj
)
{
return
obj
.
bbox
();
},
[](
Scene_Light
&
light
)
{
return
light
.
bbox
();
}},
data
);
}
void
Scene_Item
::
render
(
const
Mat4
&
view
,
bool
solid
,
bool
depth_only
,
bool
posed
)
{
std
::
visit
(
overloaded
{
[
&
](
Scene_Object
&
obj
)
{
obj
.
render
(
view
,
solid
,
depth_only
,
posed
);
},
[
&
](
Scene_Light
&
light
)
{
light
.
render
(
view
,
depth_only
,
posed
);
}
},
data
);
void
Scene_Item
::
render
(
const
Mat4
&
view
,
bool
solid
,
bool
depth_only
,
bool
posed
)
{
std
::
visit
(
overloaded
{[
&
](
Scene_Object
&
obj
)
{
obj
.
render
(
view
,
solid
,
depth_only
,
posed
);
},
[
&
](
Scene_Light
&
light
)
{
light
.
render
(
view
,
depth_only
,
posed
);
}},
data
);
}
Scene_ID
Scene_Item
::
id
()
const
{
return
std
::
visit
(
overloaded
{
[](
const
Scene_Object
&
obj
)
{
return
obj
.
id
();
},
[](
const
Scene_Light
&
light
)
{
return
light
.
id
();
}
},
data
);
return
std
::
visit
(
overloaded
{[](
const
Scene_Object
&
obj
)
{
return
obj
.
id
();
},
[](
const
Scene_Light
&
light
)
{
return
light
.
id
();
}},
data
);
}
Anim_Pose
&
Scene_Item
::
animation
()
{
Anim_Pose
&
Scene_Item
::
animation
()
{
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
anim
;
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
anim
;
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
anim
;
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
anim
;
assert
(
false
);
return
l
->
anim
;
assert
(
false
);
return
l
->
anim
;
}
const
Anim_Pose
&
Scene_Item
::
animation
()
const
{
const
Anim_Pose
&
Scene_Item
::
animation
()
const
{
const
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
anim
;
const
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
anim
;
const
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
anim
;
const
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
anim
;
assert
(
false
);
return
l
->
anim
;
assert
(
false
);
return
l
->
anim
;
}
Pose
&
Scene_Item
::
pose
()
{
Pose
&
Scene_Item
::
pose
()
{
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
pose
;
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
pose
;
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
pose
;
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
pose
;
assert
(
false
);
return
l
->
pose
;
assert
(
false
);
return
l
->
pose
;
}
const
Pose
&
Scene_Item
::
pose
()
const
{
const
Pose
&
Scene_Item
::
pose
()
const
{
const
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
pose
;
const
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
pose
;
const
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
o
->
pose
;
const
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
l
->
pose
;
assert
(
false
);
return
l
->
pose
;
assert
(
false
);
return
l
->
pose
;
}
std
::
pair
<
char
*
,
int
>
Scene_Item
::
name
()
{
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
{
o
->
opt
.
name
,
Scene_Object
::
max_name_len
};
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
{
l
->
opt
.
name
,
Scene_Light
::
max_name_len
};
std
::
pair
<
char
*
,
int
>
Scene_Item
::
name
()
{
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
{
o
->
opt
.
name
,
Scene_Object
::
max_name_len
};
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
{
l
->
opt
.
name
,
Scene_Light
::
max_name_len
};
assert
(
false
);
return
{
l
->
opt
.
name
,
Scene_Object
::
max_name_len
};
assert
(
false
);
return
{
l
->
opt
.
name
,
Scene_Object
::
max_name_len
};
}
std
::
string
Scene_Item
::
name
()
const
{
const
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
std
::
string
(
o
->
opt
.
name
);
const
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
std
::
string
(
l
->
opt
.
name
);
const
Scene_Object
*
o
=
std
::
get_if
<
Scene_Object
>
(
&
data
);
if
(
o
)
return
std
::
string
(
o
->
opt
.
name
);
const
Scene_Light
*
l
=
std
::
get_if
<
Scene_Light
>
(
&
data
);
if
(
l
)
return
std
::
string
(
l
->
opt
.
name
);
assert
(
false
);
return
std
::
string
(
l
->
opt
.
name
);
assert
(
false
);
return
std
::
string
(
l
->
opt
.
name
);
}
Scene
::
Scene
(
Scene_ID
start
)
:
next_id
(
start
),
first_id
(
start
)
{
}
Scene
::
Scene
(
Scene_ID
start
)
:
next_id
(
start
),
first_id
(
start
)
{}
Scene_ID
Scene
::
used_ids
()
{
return
next_id
;
}
Scene_ID
Scene
::
used_ids
()
{
return
next_id
;
}
Scene_ID
Scene
::
reserve_id
()
{
return
next_id
++
;
}
Scene_ID
Scene
::
reserve_id
()
{
return
next_id
++
;
}
Scene_ID
Scene
::
add
(
Pose
pose
,
Halfedge_Mesh
&&
mesh
,
std
::
string
n
,
Scene_ID
id
)
{
if
(
!
id
)
id
=
next_id
++
;
assert
(
objs
.
find
(
id
)
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
id
,
Scene_Object
(
id
,
pose
,
std
::
move
(
mesh
),
n
)));
return
id
;
Scene_ID
Scene
::
add
(
Pose
pose
,
Halfedge_Mesh
&&
mesh
,
std
::
string
n
,
Scene_ID
id
)
{
if
(
!
id
)
id
=
next_id
++
;
assert
(
objs
.
find
(
id
)
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
id
,
Scene_Object
(
id
,
pose
,
std
::
move
(
mesh
),
n
)));
return
id
;
}
Scene_ID
Scene
::
add
(
Pose
pose
,
GL
::
Mesh
&&
mesh
,
std
::
string
n
,
Scene_ID
id
)
{
if
(
!
id
)
id
=
next_id
++
;
assert
(
objs
.
find
(
id
)
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
id
,
Scene_Object
(
id
,
pose
,
std
::
move
(
mesh
),
n
)));
return
id
;
Scene_ID
Scene
::
add
(
Pose
pose
,
GL
::
Mesh
&&
mesh
,
std
::
string
n
,
Scene_ID
id
)
{
if
(
!
id
)
id
=
next_id
++
;
assert
(
objs
.
find
(
id
)
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
id
,
Scene_Object
(
id
,
pose
,
std
::
move
(
mesh
),
n
)));
return
id
;
}
std
::
string
Scene
::
set_env_map
(
std
::
string
file
)
{
Scene_ID
id
=
0
;
for_items
([
&
id
](
const
Scene_Item
&
item
)
{
if
(
item
.
is
<
Scene_Light
>
()
&&
item
.
get
<
Scene_Light
>
().
is_env
())
id
=
item
.
id
();
});
Scene_Light
l
(
Light_Type
::
sphere
,
reserve_id
(),
{},
"env_map"
);
std
::
string
err
=
l
.
emissive_load
(
file
);
if
(
err
.
empty
())
{
if
(
id
)
erase
(
id
);
add
(
std
::
move
(
l
));
}
return
err
;
Scene_ID
id
=
0
;
for_items
([
&
id
](
const
Scene_Item
&
item
)
{
if
(
item
.
is
<
Scene_Light
>
()
&&
item
.
get
<
Scene_Light
>
().
is_env
())
id
=
item
.
id
();
});
Scene_Light
l
(
Light_Type
::
sphere
,
reserve_id
(),
{},
"env_map"
);
std
::
string
err
=
l
.
emissive_load
(
file
);
if
(
err
.
empty
())
{
if
(
id
)
erase
(
id
);
add
(
std
::
move
(
l
));
}
return
err
;
}
bool
Scene
::
has_env_light
()
const
{
bool
ret
=
false
;
for_items
([
&
ret
](
const
Scene_Item
&
item
)
{
ret
=
ret
||
(
item
.
is
<
Scene_Light
>
()
&&
item
.
get
<
Scene_Light
>
().
is_env
());
});
return
ret
;
bool
ret
=
false
;
for_items
([
&
ret
](
const
Scene_Item
&
item
)
{
ret
=
ret
||
(
item
.
is
<
Scene_Light
>
()
&&
item
.
get
<
Scene_Light
>
().
is_env
());
});
return
ret
;
}
Scene_ID
Scene
::
add
(
Scene_Light
&&
obj
)
{
assert
(
objs
.
find
(
obj
.
id
())
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
obj
.
id
(),
std
::
move
(
obj
)));
return
obj
.
id
();
Scene_ID
Scene
::
add
(
Scene_Light
&&
obj
)
{
assert
(
objs
.
find
(
obj
.
id
())
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
obj
.
id
(),
std
::
move
(
obj
)));
return
obj
.
id
();
}
Scene_ID
Scene
::
add
(
Scene_Object
&&
obj
)
{
assert
(
objs
.
find
(
obj
.
id
())
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
obj
.
id
(),
std
::
move
(
obj
)));
return
obj
.
id
();
Scene_ID
Scene
::
add
(
Scene_Object
&&
obj
)
{
assert
(
objs
.
find
(
obj
.
id
())
==
objs
.
end
());
objs
.
emplace
(
std
::
make_pair
(
obj
.
id
(),
std
::
move
(
obj
)));
return
obj
.
id
();
}
void
Scene
::
restore
(
Scene_ID
id
)
{
if
(
objs
.
find
(
id
)
!=
objs
.
end
())
return
;
assert
(
erased
.
find
(
id
)
!=
erased
.
end
());
objs
.
insert
({
id
,
std
::
move
(
erased
[
id
])});
erased
.
erase
(
id
);
if
(
objs
.
find
(
id
)
!=
objs
.
end
())
return
;
assert
(
erased
.
find
(
id
)
!=
erased
.
end
());
objs
.
insert
({
id
,
std
::
move
(
erased
[
id
])});
erased
.
erase
(
id
);
}
void
Scene
::
erase
(
Scene_ID
id
)
{
assert
(
erased
.
find
(
id
)
==
erased
.
end
());
assert
(
objs
.
find
(
id
)
!=
objs
.
end
());
assert
(
erased
.
find
(
id
)
==
erased
.
end
());
assert
(
objs
.
find
(
id
)
!=
objs
.
end
());
erased
.
insert
({
id
,
std
::
move
(
objs
[
id
])});
objs
.
erase
(
id
);
erased
.
insert
({
id
,
std
::
move
(
objs
[
id
])});
objs
.
erase
(
id
);
}
void
Scene
::
for_items
(
std
::
function
<
void
(
const
Scene_Item
&
)
>
func
)
const
{
for
(
const
auto
&
obj
:
objs
)
{
func
(
obj
.
second
);
}
void
Scene
::
for_items
(
std
::
function
<
void
(
const
Scene_Item
&
)
>
func
)
const
{
for
(
const
auto
&
obj
:
objs
)
{
func
(
obj
.
second
);
}
}
void
Scene
::
for_items
(
std
::
function
<
void
(
Scene_Item
&
)
>
func
)
{
for
(
auto
&
obj
:
objs
)
{
func
(
obj
.
second
);
}
void
Scene
::
for_items
(
std
::
function
<
void
(
Scene_Item
&
)
>
func
)
{
for
(
auto
&
obj
:
objs
)
{
func
(
obj
.
second
);
}
}
size_t
Scene
::
size
()
{
return
objs
.
size
();
}
size_t
Scene
::
size
()
{
return
objs
.
size
();
}
bool
Scene
::
empty
()
{
return
objs
.
size
()
==
0
;
}
bool
Scene
::
empty
()
{
return
objs
.
size
()
==
0
;
}
Scene_Maybe
Scene
::
get
(
Scene_ID
id
)
{
auto
entry
=
objs
.
find
(
id
);
if
(
entry
==
objs
.
end
())
return
std
::
nullopt
;
return
entry
->
second
;
auto
entry
=
objs
.
find
(
id
);
if
(
entry
==
objs
.
end
())
return
std
::
nullopt
;
return
entry
->
second
;
}
Scene_Light
&
Scene
::
get_light
(
Scene_ID
id
)
{
auto
entry
=
objs
.
find
(
id
);
assert
(
entry
!=
objs
.
end
());
assert
(
entry
->
second
.
is
<
Scene_Light
>
());
return
entry
->
second
.
get
<
Scene_Light
>
();
Scene_Light
&
Scene
::
get_light
(
Scene_ID
id
)
{
auto
entry
=
objs
.
find
(
id
);
assert
(
entry
!=
objs
.
end
());
assert
(
entry
->
second
.
is
<
Scene_Light
>
());
return
entry
->
second
.
get
<
Scene_Light
>
();
}
Scene_Object
&
Scene
::
get_obj
(
Scene_ID
id
)
{
auto
entry
=
objs
.
find
(
id
);
assert
(
entry
!=
objs
.
end
());
assert
(
entry
->
second
.
is
<
Scene_Object
>
());
return
entry
->
second
.
get
<
Scene_Object
>
();
Scene_Object
&
Scene
::
get_obj
(
Scene_ID
id
)
{
auto
entry
=
objs
.
find
(
id
);
assert
(
entry
!=
objs
.
end
());
assert
(
entry
->
second
.
is
<
Scene_Object
>
());
return
entry
->
second
.
get
<
Scene_Object
>
();
}
void
Scene
::
clear
(
Undo
&
undo
)
{
next_id
=
first_id
;
objs
.
clear
();
erased
.
clear
();
undo
.
reset
();
void
Scene
::
clear
(
Undo
&
undo
)
{
next_id
=
first_id
;
objs
.
clear
();
erased
.
clear
();
undo
.
reset
();
}
static
const
std
::
string
FAKE_NAME
=
"FAKE-S3D-FAKE-MESH"
;
static
aiVector3D
vecVec
(
Vec3
v
)
{
return
aiVector3D
(
v
.
x
,
v
.
y
,
v
.
z
);
}
static
aiVector3D
vecVec
(
Vec3
v
)
{
return
aiVector3D
(
v
.
x
,
v
.
y
,
v
.
z
);
}
static
Quat
aiQuat
(
aiQuaternion
aiv
)
{
return
Quat
(
aiv
.
x
,
aiv
.
y
,
aiv
.
z
,
aiv
.
w
);
}
static
Quat
aiQuat
(
aiQuaternion
aiv
)
{
return
Quat
(
aiv
.
x
,
aiv
.
y
,
aiv
.
z
,
aiv
.
w
);
}
static
Vec3
aiVec
(
aiVector3D
aiv
)
{
return
Vec3
(
aiv
.
x
,
aiv
.
y
,
aiv
.
z
);
}
static
Vec3
aiVec
(
aiVector3D
aiv
)
{
return
Vec3
(
aiv
.
x
,
aiv
.
y
,
aiv
.
z
);
}
static
Spectrum
aiSpec
(
aiColor3D
aiv
)
{
return
Spectrum
(
aiv
.
r
,
aiv
.
g
,
aiv
.
b
);
}
static
Spectrum
aiSpec
(
aiColor3D
aiv
)
{
return
Spectrum
(
aiv
.
r
,
aiv
.
g
,
aiv
.
b
);
}
static
aiMatrix4x4
matMat
(
const
Mat4
&
T
)
{
return
{
T
[
0
][
0
],
T
[
1
][
0
],
T
[
2
][
0
],
T
[
3
][
0
],
T
[
0
][
1
],
T
[
1
][
1
],
T
[
2
][
1
],
T
[
3
][
1
],
T
[
0
][
2
],
T
[
1
][
2
],
T
[
2
][
2
],
T
[
3
][
2
],
T
[
0
][
3
],
T
[
1
][
3
],
T
[
2
][
3
],
T
[
3
][
3
]};
static
aiMatrix4x4
matMat
(
const
Mat4
&
T
)
{
return
{
T
[
0
][
0
],
T
[
1
][
0
],
T
[
2
][
0
],
T
[
3
][
0
],
T
[
0
][
1
],
T
[
1
][
1
],
T
[
2
][
1
],
T
[
3
][
1
],
T
[
0
][
2
],
T
[
1
][
2
],
T
[
2
][
2
],
T
[
3
][
2
],
T
[
0
][
3
],
T
[
1
][
3
],
T
[
2
][
3
],
T
[
3
][
3
]};
}
static
Mat4
aiMat
(
aiMatrix4x4
T
)
{
return
Mat4
{
Vec4
{
T
[
0
][
0
],
T
[
1
][
0
],
T
[
2
][
0
],
T
[
3
][
0
]},
Vec4
{
T
[
0
][
1
],
T
[
1
][
1
],
T
[
2
][
1
],
T
[
3
][
1
]},
Vec4
{
T
[
0
][
2
],
T
[
1
][
2
],
T
[
2
][
2
],
T
[
3
][
2
]},
Vec4
{
T
[
0
][
3
],
T
[
1
][
3
],
T
[
2
][
3
],
T
[
3
][
3
]}};
return
Mat4
{
Vec4
{
T
[
0
][
0
],
T
[
1
][
0
],
T
[
2
][
0
],
T
[
3
][
0
]},
Vec4
{
T
[
0
][
1
],
T
[
1
][
1
],
T
[
2
][
1
],
T
[
3
][
1
]},
Vec4
{
T
[
0
][
2
],
T
[
1
][
2
],
T
[
2
][
2
],
T
[
3
][
2
]},
Vec4
{
T
[
0
][
3
],
T
[
1
][
3
],
T
[
2
][
3
],
T
[
3
][
3
]}};
}
static
aiMatrix4x4
node_transform
(
const
aiNode
*
node
)
{
aiMatrix4x4
T
;
while
(
node
)
{
T
=
T
*
node
->
mTransformation
;
node
=
node
->
mParent
;
}
return
T
;
static
aiMatrix4x4
node_transform
(
const
aiNode
*
node
)
{
aiMatrix4x4
T
;
while
(
node
)
{
T
=
T
*
node
->
mTransformation
;
node
=
node
->
mParent
;
}
return
T
;
}
static
void
load_node
(
Scene
&
scobj
,
std
::
vector
<
std
::
string
>&
errors
,
std
::
unordered_map
<
aiNode
*
,
Scene_ID
>&
node_to_obj
,
std
::
unordered_map
<
aiNode
*
,
Joint
*>&
node_to_bone
,
const
aiScene
*
scene
,
aiNode
*
node
,
aiMatrix4x4
transform
)
{
transform
=
transform
*
node
->
mTransformation
;
for
(
unsigned
int
i
=
0
;
i
<
node
->
mNumMeshes
;
i
++
)
{
const
aiMesh
*
mesh
=
scene
->
mMeshes
[
node
->
mMeshes
[
i
]];
std
::
string
name
;
bool
do_flip
=
false
,
do_smooth
=
false
;
if
(
mesh
->
mName
.
length
)
{
name
=
std
::
string
(
mesh
->
mName
.
C_Str
());
if
(
name
.
find
(
FAKE_NAME
)
!=
std
::
string
::
npos
)
continue
;
size_t
special
=
name
.
find
(
"-S3D-"
);
if
(
special
!=
std
::
string
::
npos
)
{
if
(
name
.
find
(
"FLIPPED"
)
!=
std
::
string
::
npos
)
do_flip
=
true
;
if
(
name
.
find
(
"SMOOTHED"
)
!=
std
::
string
::
npos
)
do_smooth
=
true
;
name
=
name
.
substr
(
0
,
special
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
'_'
,
' '
);
}
}
if
(
!
mesh
->
HasNormals
())
{
errors
.
push_back
(
"Mesh has no normals."
);
continue
;
}
std
::
vector
<
Vec3
>
verts
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumVertices
;
j
++
)
{
const
aiVector3D
&
pos
=
mesh
->
mVertices
[
j
];
verts
.
push_back
(
Vec3
(
pos
.
x
,
pos
.
y
,
pos
.
z
));
}
std
::
vector
<
std
::
vector
<
Halfedge_Mesh
::
Index
>>
polys
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumFaces
;
j
++
)
{
const
aiFace
&
face
=
mesh
->
mFaces
[
j
];
std
::
vector
<
Halfedge_Mesh
::
Index
>
poly
;
for
(
unsigned
int
k
=
0
;
k
<
face
.
mNumIndices
;
k
++
)
{
poly
.
push_back
(
face
.
mIndices
[
k
]);
}
polys
.
push_back
(
poly
);
}
aiVector3D
ascale
,
arot
,
apos
;
transform
.
Decompose
(
ascale
,
arot
,
apos
);
Vec3
pos
=
aiVec
(
apos
);
Vec3
rot
=
aiVec
(
arot
);
Vec3
scale
=
aiVec
(
ascale
);
Pose
p
=
{
pos
,
Degrees
(
rot
).
range
(
0.0
f
,
360.0
f
),
scale
};
Material
::
Options
material
;
const
aiMaterial
&
ai_mat
=
*
scene
->
mMaterials
[
mesh
->
mMaterialIndex
];
aiColor3D
albedo
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_DIFFUSE
,
albedo
);
material
.
albedo
=
aiSpec
(
albedo
);
aiColor3D
emissive
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_EMISSIVE
,
emissive
);
material
.
emissive
=
aiSpec
(
emissive
);
aiColor3D
reflectance
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_REFLECTIVE
,
reflectance
);
material
.
reflectance
=
aiSpec
(
reflectance
);
aiColor3D
transmittance
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_TRANSPARENT
,
transmittance
);
material
.
transmittance
=
aiSpec
(
transmittance
);
ai_mat
.
Get
(
AI_MATKEY_REFRACTI
,
material
.
ior
);
ai_mat
.
Get
(
AI_MATKEY_SHININESS
,
material
.
intensity
);
aiString
ai_type
;
ai_mat
.
Get
(
AI_MATKEY_NAME
,
ai_type
);
std
::
string
type
(
ai_type
.
C_Str
());
if
(
type
.
find
(
"lambertian"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
lambertian
;
}
else
if
(
type
.
find
(
"mirror"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
mirror
;
}
else
if
(
type
.
find
(
"refract"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
refract
;
}
else
if
(
type
.
find
(
"glass"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
glass
;
}
else
if
(
type
.
find
(
"diffuse_light"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
diffuse_light
;
}
else
{
material
=
Material
::
Options
();
}
material
.
emissive
*=
1.0
f
/
material
.
intensity
;
Scene_Object
new_obj
;
// Horrible hack
if
(
type
.
find
(
"SPHERESHAPE"
)
!=
std
::
string
::
npos
)
{
aiColor3D
specular
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_SPECULAR
,
specular
);
Scene_Object
obj
(
scobj
.
reserve_id
(),
p
,
GL
::
Mesh
());
obj
.
material
.
opt
=
material
;
obj
.
opt
.
shape_type
=
PT
::
Shape_Type
::
sphere
;
obj
.
opt
.
shape
=
PT
::
Shape
(
PT
::
Sphere
(
specular
.
r
));
new_obj
=
std
::
move
(
obj
);
}
else
{
Halfedge_Mesh
hemesh
;
std
::
string
err
=
hemesh
.
from_poly
(
polys
,
verts
);
if
(
!
err
.
empty
())
{
std
::
vector
<
GL
::
Mesh
::
Vert
>
mesh_verts
;
std
::
vector
<
GL
::
Mesh
::
Index
>
mesh_inds
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumVertices
;
j
++
)
{
const
aiVector3D
&
vpos
=
mesh
->
mVertices
[
j
];
const
aiVector3D
&
vnorm
=
mesh
->
mNormals
[
j
];
mesh_verts
.
push_back
({
aiVec
(
vpos
),
aiVec
(
vnorm
),
0
});
}
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumFaces
;
j
++
)
{
const
aiFace
&
face
=
mesh
->
mFaces
[
j
];
unsigned
int
start
=
face
.
mIndices
[
0
];
for
(
size_t
k
=
1
;
k
<=
face
.
mNumIndices
-
2
;
k
++
)
{
mesh_inds
.
push_back
(
start
);
mesh_inds
.
push_back
(
face
.
mIndices
[
k
]);
mesh_inds
.
push_back
(
face
.
mIndices
[
k
+
1
]);
}
}
errors
.
push_back
(
err
);
Scene_Object
obj
(
scobj
.
reserve_id
(),
p
,
GL
::
Mesh
(
std
::
move
(
mesh_verts
),
std
::
move
(
mesh_inds
)),
name
);
obj
.
material
.
opt
=
material
;
new_obj
=
std
::
move
(
obj
);
}
else
{
if
(
do_flip
)
hemesh
.
flip
();
Scene_Object
obj
(
scobj
.
reserve_id
(),
p
,
std
::
move
(
hemesh
),
name
);
obj
.
material
.
opt
=
material
;
obj
.
opt
.
smooth_normals
=
do_smooth
;
new_obj
=
std
::
move
(
obj
);
}
}
if
(
mesh
->
mNumBones
)
{
Skeleton
&
skeleton
=
new_obj
.
armature
;
aiNode
*
arm_node
=
mesh
->
mBones
[
0
]
->
mArmature
;
{
aiVector3D
t
,
r
,
s
;
arm_node
->
mTransformation
.
Decompose
(
s
,
r
,
t
);
skeleton
.
base
()
=
aiVec
(
t
);
}
std
::
unordered_map
<
aiNode
*
,
aiBone
*>
node_to_aibone
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumBones
;
j
++
)
{
node_to_aibone
[
mesh
->
mBones
[
j
]
->
mNode
]
=
mesh
->
mBones
[
j
];
}
std
::
function
<
void
(
Joint
*
,
aiNode
*
)
>
build_tree
;
build_tree
=
[
&
](
Joint
*
p
,
aiNode
*
node
)
{
aiBone
*
bone
=
node_to_aibone
[
node
];
aiVector3D
t
,
r
,
s
;
bone
->
mOffsetMatrix
.
Decompose
(
s
,
r
,
t
);
Joint
*
c
=
skeleton
.
add_child
(
p
,
aiVec
(
t
));
node_to_bone
[
node
]
=
c
;
c
->
pose
=
aiVec
(
r
);
c
->
radius
=
bone
->
mWeights
[
0
].
mWeight
;
for
(
unsigned
int
j
=
0
;
j
<
node
->
mNumChildren
;
j
++
)
build_tree
(
c
,
node
->
mChildren
[
j
]);
};
for
(
unsigned
int
j
=
0
;
j
<
arm_node
->
mNumChildren
;
j
++
)
{
aiNode
*
root_node
=
arm_node
->
mChildren
[
j
];
aiBone
*
root_bone
=
node_to_aibone
[
root_node
];
aiVector3D
t
,
r
,
s
;
root_bone
->
mOffsetMatrix
.
Decompose
(
s
,
r
,
t
);
Joint
*
root
=
skeleton
.
add_root
(
aiVec
(
t
));
node_to_bone
[
root_node
]
=
root
;
root
->
pose
=
aiVec
(
r
);
root
->
radius
=
root_bone
->
mWeights
[
0
].
mWeight
;
for
(
unsigned
int
k
=
0
;
k
<
root_node
->
mNumChildren
;
k
++
)
build_tree
(
root
,
root_node
->
mChildren
[
k
]);
}
}
node_to_obj
[
node
]
=
new_obj
.
id
();
scobj
.
add
(
std
::
move
(
new_obj
));
}
for
(
unsigned
int
i
=
0
;
i
<
node
->
mNumChildren
;
i
++
)
{
load_node
(
scobj
,
errors
,
node_to_obj
,
node_to_bone
,
scene
,
node
->
mChildren
[
i
],
transform
);
}
static
void
load_node
(
Scene
&
scobj
,
std
::
vector
<
std
::
string
>
&
errors
,
std
::
unordered_map
<
aiNode
*
,
Scene_ID
>
&
node_to_obj
,
std
::
unordered_map
<
aiNode
*
,
Joint
*>
&
node_to_bone
,
const
aiScene
*
scene
,
aiNode
*
node
,
aiMatrix4x4
transform
)
{
transform
=
transform
*
node
->
mTransformation
;
for
(
unsigned
int
i
=
0
;
i
<
node
->
mNumMeshes
;
i
++
)
{
const
aiMesh
*
mesh
=
scene
->
mMeshes
[
node
->
mMeshes
[
i
]];
std
::
string
name
;
bool
do_flip
=
false
,
do_smooth
=
false
;
if
(
mesh
->
mName
.
length
)
{
name
=
std
::
string
(
mesh
->
mName
.
C_Str
());
if
(
name
.
find
(
FAKE_NAME
)
!=
std
::
string
::
npos
)
continue
;
size_t
special
=
name
.
find
(
"-S3D-"
);
if
(
special
!=
std
::
string
::
npos
)
{
if
(
name
.
find
(
"FLIPPED"
)
!=
std
::
string
::
npos
)
do_flip
=
true
;
if
(
name
.
find
(
"SMOOTHED"
)
!=
std
::
string
::
npos
)
do_smooth
=
true
;
name
=
name
.
substr
(
0
,
special
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
'_'
,
' '
);
}
}
if
(
!
mesh
->
HasNormals
())
{
errors
.
push_back
(
"Mesh has no normals."
);
continue
;
}
std
::
vector
<
Vec3
>
verts
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumVertices
;
j
++
)
{
const
aiVector3D
&
pos
=
mesh
->
mVertices
[
j
];
verts
.
push_back
(
Vec3
(
pos
.
x
,
pos
.
y
,
pos
.
z
));
}
std
::
vector
<
std
::
vector
<
Halfedge_Mesh
::
Index
>>
polys
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumFaces
;
j
++
)
{
const
aiFace
&
face
=
mesh
->
mFaces
[
j
];
std
::
vector
<
Halfedge_Mesh
::
Index
>
poly
;
for
(
unsigned
int
k
=
0
;
k
<
face
.
mNumIndices
;
k
++
)
{
poly
.
push_back
(
face
.
mIndices
[
k
]);
}
polys
.
push_back
(
poly
);
}
aiVector3D
ascale
,
arot
,
apos
;
transform
.
Decompose
(
ascale
,
arot
,
apos
);
Vec3
pos
=
aiVec
(
apos
);
Vec3
rot
=
aiVec
(
arot
);
Vec3
scale
=
aiVec
(
ascale
);
Pose
p
=
{
pos
,
Degrees
(
rot
).
range
(
0.0
f
,
360.0
f
),
scale
};
Material
::
Options
material
;
const
aiMaterial
&
ai_mat
=
*
scene
->
mMaterials
[
mesh
->
mMaterialIndex
];
aiColor3D
albedo
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_DIFFUSE
,
albedo
);
material
.
albedo
=
aiSpec
(
albedo
);
aiColor3D
emissive
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_EMISSIVE
,
emissive
);
material
.
emissive
=
aiSpec
(
emissive
);
aiColor3D
reflectance
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_REFLECTIVE
,
reflectance
);
material
.
reflectance
=
aiSpec
(
reflectance
);
aiColor3D
transmittance
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_TRANSPARENT
,
transmittance
);
material
.
transmittance
=
aiSpec
(
transmittance
);
ai_mat
.
Get
(
AI_MATKEY_REFRACTI
,
material
.
ior
);
ai_mat
.
Get
(
AI_MATKEY_SHININESS
,
material
.
intensity
);
aiString
ai_type
;
ai_mat
.
Get
(
AI_MATKEY_NAME
,
ai_type
);
std
::
string
type
(
ai_type
.
C_Str
());
if
(
type
.
find
(
"lambertian"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
lambertian
;
}
else
if
(
type
.
find
(
"mirror"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
mirror
;
}
else
if
(
type
.
find
(
"refract"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
refract
;
}
else
if
(
type
.
find
(
"glass"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
glass
;
}
else
if
(
type
.
find
(
"diffuse_light"
)
!=
std
::
string
::
npos
)
{
material
.
type
=
Material_Type
::
diffuse_light
;
}
else
{
material
=
Material
::
Options
();
}
material
.
emissive
*=
1.0
f
/
material
.
intensity
;
Scene_Object
new_obj
;
// Horrible hack
if
(
type
.
find
(
"SPHERESHAPE"
)
!=
std
::
string
::
npos
)
{
aiColor3D
specular
;
ai_mat
.
Get
(
AI_MATKEY_COLOR_SPECULAR
,
specular
);
Scene_Object
obj
(
scobj
.
reserve_id
(),
p
,
GL
::
Mesh
());
obj
.
material
.
opt
=
material
;
obj
.
opt
.
shape_type
=
PT
::
Shape_Type
::
sphere
;
obj
.
opt
.
shape
=
PT
::
Shape
(
PT
::
Sphere
(
specular
.
r
));
new_obj
=
std
::
move
(
obj
);
}
else
{
Halfedge_Mesh
hemesh
;
std
::
string
err
=
hemesh
.
from_poly
(
polys
,
verts
);
if
(
!
err
.
empty
())
{
std
::
vector
<
GL
::
Mesh
::
Vert
>
mesh_verts
;
std
::
vector
<
GL
::
Mesh
::
Index
>
mesh_inds
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumVertices
;
j
++
)
{
const
aiVector3D
&
vpos
=
mesh
->
mVertices
[
j
];
const
aiVector3D
&
vnorm
=
mesh
->
mNormals
[
j
];
mesh_verts
.
push_back
({
aiVec
(
vpos
),
aiVec
(
vnorm
),
0
});
}
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumFaces
;
j
++
)
{
const
aiFace
&
face
=
mesh
->
mFaces
[
j
];
unsigned
int
start
=
face
.
mIndices
[
0
];
for
(
size_t
k
=
1
;
k
<=
face
.
mNumIndices
-
2
;
k
++
)
{
mesh_inds
.
push_back
(
start
);
mesh_inds
.
push_back
(
face
.
mIndices
[
k
]);
mesh_inds
.
push_back
(
face
.
mIndices
[
k
+
1
]);
}
}
errors
.
push_back
(
err
);
Scene_Object
obj
(
scobj
.
reserve_id
(),
p
,
GL
::
Mesh
(
std
::
move
(
mesh_verts
),
std
::
move
(
mesh_inds
)),
name
);
obj
.
material
.
opt
=
material
;
new_obj
=
std
::
move
(
obj
);
}
else
{
if
(
do_flip
)
hemesh
.
flip
();
Scene_Object
obj
(
scobj
.
reserve_id
(),
p
,
std
::
move
(
hemesh
),
name
);
obj
.
material
.
opt
=
material
;
obj
.
opt
.
smooth_normals
=
do_smooth
;
new_obj
=
std
::
move
(
obj
);
}
}
if
(
mesh
->
mNumBones
)
{
Skeleton
&
skeleton
=
new_obj
.
armature
;
aiNode
*
arm_node
=
mesh
->
mBones
[
0
]
->
mArmature
;
{
aiVector3D
t
,
r
,
s
;
arm_node
->
mTransformation
.
Decompose
(
s
,
r
,
t
);
skeleton
.
base
()
=
aiVec
(
t
);
}
std
::
unordered_map
<
aiNode
*
,
aiBone
*>
node_to_aibone
;
for
(
unsigned
int
j
=
0
;
j
<
mesh
->
mNumBones
;
j
++
)
{
node_to_aibone
[
mesh
->
mBones
[
j
]
->
mNode
]
=
mesh
->
mBones
[
j
];
}
std
::
function
<
void
(
Joint
*
,
aiNode
*
)
>
build_tree
;
build_tree
=
[
&
](
Joint
*
p
,
aiNode
*
node
)
{
aiBone
*
bone
=
node_to_aibone
[
node
];
aiVector3D
t
,
r
,
s
;
bone
->
mOffsetMatrix
.
Decompose
(
s
,
r
,
t
);
Joint
*
c
=
skeleton
.
add_child
(
p
,
aiVec
(
t
));
node_to_bone
[
node
]
=
c
;
c
->
pose
=
aiVec
(
r
);
c
->
radius
=
bone
->
mWeights
[
0
].
mWeight
;
for
(
unsigned
int
j
=
0
;
j
<
node
->
mNumChildren
;
j
++
)
build_tree
(
c
,
node
->
mChildren
[
j
]);
};
for
(
unsigned
int
j
=
0
;
j
<
arm_node
->
mNumChildren
;
j
++
)
{
aiNode
*
root_node
=
arm_node
->
mChildren
[
j
];
aiBone
*
root_bone
=
node_to_aibone
[
root_node
];
aiVector3D
t
,
r
,
s
;
root_bone
->
mOffsetMatrix
.
Decompose
(
s
,
r
,
t
);
Joint
*
root
=
skeleton
.
add_root
(
aiVec
(
t
));
node_to_bone
[
root_node
]
=
root
;
root
->
pose
=
aiVec
(
r
);
root
->
radius
=
root_bone
->
mWeights
[
0
].
mWeight
;
for
(
unsigned
int
k
=
0
;
k
<
root_node
->
mNumChildren
;
k
++
)
build_tree
(
root
,
root_node
->
mChildren
[
k
]);
}
}
node_to_obj
[
node
]
=
new_obj
.
id
();
scobj
.
add
(
std
::
move
(
new_obj
));
}
for
(
unsigned
int
i
=
0
;
i
<
node
->
mNumChildren
;
i
++
)
{
load_node
(
scobj
,
errors
,
node_to_obj
,
node_to_bone
,
scene
,
node
->
mChildren
[
i
],
transform
);
}
}
std
::
string
Scene
::
load
(
bool
new_scene
,
Undo
&
undo
,
Gui
::
Manager
&
gui
,
std
::
string
file
)
{
if
(
new_scene
)
{
clear
(
undo
);
gui
.
get_animate
().
clear
();
gui
.
get_rig
().
clear
();
}
Assimp
::
Importer
importer
;
const
aiScene
*
scene
=
importer
.
ReadFile
(
file
.
c_str
(),
aiProcess_GenSmoothNormals
|
aiProcess_PopulateArmatureData
|
aiProcess_ValidateDataStructure
|
aiProcess_OptimizeMeshes
|
aiProcess_FindInstances
|
aiProcess_FindDegenerates
|
aiProcess_JoinIdenticalVertices
|
aiProcess_FindInvalidData
);
if
(
!
scene
)
{
return
"Parsing scene "
+
file
+
": "
+
std
::
string
(
importer
.
GetErrorString
());
}
std
::
vector
<
std
::
string
>
errors
;
std
::
unordered_map
<
aiNode
*
,
Scene_ID
>
node_to_obj
;
std
::
unordered_map
<
aiNode
*
,
Joint
*>
node_to_bone
;
scene
->
mRootNode
->
mTransformation
=
aiMatrix4x4
();
// Load objects
load_node
(
*
this
,
errors
,
node_to_obj
,
node_to_bone
,
scene
,
scene
->
mRootNode
,
aiMatrix4x4
());
// Load camera
if
(
new_scene
&&
scene
->
mNumCameras
)
{
const
aiCamera
&
aiCam
=
*
scene
->
mCameras
[
0
];
Mat4
cam_transform
=
aiMat
(
node_transform
(
scene
->
mRootNode
->
FindNode
(
aiCam
.
mName
)));
Vec3
pos
=
cam_transform
*
aiVec
(
aiCam
.
mPosition
);
Vec3
center
=
cam_transform
*
aiVec
(
aiCam
.
mLookAt
);
gui
.
get_render
().
load_cam
(
pos
,
center
,
aiCam
.
mAspect
,
aiCam
.
mHorizontalFOV
);
}
// Load lights
for
(
unsigned
int
i
=
0
;
i
<
scene
->
mNumLights
;
i
++
)
{
const
aiLight
&
ailight
=
*
scene
->
mLights
[
i
];
const
aiNode
*
node
=
scene
->
mRootNode
->
FindNode
(
ailight
.
mName
);
Mat4
trans
=
aiMat
(
node_transform
(
node
));
Vec3
pos
=
trans
*
aiVec
(
ailight
.
mPosition
);
Vec3
dir
=
trans
.
rotate
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
Pose
p
;
p
.
pos
=
pos
;
p
.
euler
=
Mat4
::
rotate_to
(
dir
).
to_euler
();
bool
was_exported_from_s3d
=
false
;
float
export_power
=
1.0
f
;
bool
is_sphere
=
false
,
is_hemisphere
=
false
,
is_area
=
false
;
std
::
string
name
=
std
::
string
(
node
->
mName
.
C_Str
());
size_t
special
=
name
.
find
(
"-S3D-"
);
if
(
special
!=
std
::
string
::
npos
)
{
was_exported_from_s3d
=
true
;
export_power
=
ailight
.
mAttenuationQuadratic
;
std
::
string
left
=
name
.
substr
(
special
+
4
);
name
=
name
.
substr
(
0
,
special
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
'_'
,
' '
);
if
(
left
.
find
(
"HEMISPHERE"
)
!=
std
::
string
::
npos
)
is_hemisphere
=
true
;
else
if
(
left
.
find
(
"SPHERE"
)
!=
std
::
string
::
npos
)
is_sphere
=
true
;
else
if
(
left
.
find
(
"AREA"
)
!=
std
::
string
::
npos
)
is_area
=
true
;
}
aiColor3D
color
;
Scene_Light
light
(
Light_Type
::
point
,
reserve_id
(),
p
,
name
);
switch
(
ailight
.
mType
)
{
case
aiLightSource_DIRECTIONAL
:
{
light
.
opt
.
type
=
Light_Type
::
directional
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
case
aiLightSource_POINT
:
{
light
.
opt
.
type
=
Light_Type
::
point
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
case
aiLightSource_SPOT
:
{
light
.
opt
.
type
=
Light_Type
::
spot
;
light
.
opt
.
angle_bounds
.
x
=
ailight
.
mAngleInnerCone
;
light
.
opt
.
angle_bounds
.
y
=
ailight
.
mAngleOuterCone
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
case
aiLightSource_AMBIENT
:
{
if
(
is_hemisphere
)
light
.
opt
.
type
=
Light_Type
::
hemisphere
;
else
if
(
is_sphere
)
{
light
.
opt
.
type
=
Light_Type
::
sphere
;
if
(
ailight
.
mEnvMap
.
length
)
{
light
.
opt
.
has_emissive_map
=
true
;
light
.
emissive_load
(
std
::
string
(
ailight
.
mEnvMap
.
C_Str
()));
}
}
else
if
(
is_area
)
{
light
.
opt
.
type
=
Light_Type
::
rectangle
;
light
.
opt
.
size
.
x
=
ailight
.
mAttenuationConstant
;
light
.
opt
.
size
.
y
=
ailight
.
mAttenuationLinear
;
}
color
=
ailight
.
mColorAmbient
;
}
break
;
case
aiLightSource_AREA
:
{
light
.
opt
.
type
=
Light_Type
::
rectangle
;
light
.
opt
.
size
.
x
=
ailight
.
mSize
.
x
;
light
.
opt
.
size
.
y
=
ailight
.
mSize
.
y
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
default:
continue
;
}
float
power
=
std
::
max
(
color
.
r
,
std
::
max
(
color
.
g
,
color
.
b
));
light
.
opt
.
spectrum
=
Spectrum
(
color
.
r
,
color
.
g
,
color
.
b
)
*
(
1.0
f
/
power
);
light
.
opt
.
intensity
=
power
;
if
(
was_exported_from_s3d
)
{
float
factor
=
power
/
export_power
;
light
.
opt
.
spectrum
*=
factor
;
light
.
opt
.
intensity
=
export_power
;
}
if
(
!
light
.
is_env
()
||
!
has_env_light
())
{
node_to_obj
[(
aiNode
*
)
node
]
=
light
.
id
();
std
::
string
anim_name
=
std
::
string
(
node
->
mName
.
C_Str
())
+
"-LIGHT_ANIM_NODE"
;
aiNode
*
anim_node
=
scene
->
mRootNode
->
FindNode
(
aiString
(
anim_name
));
node_to_obj
[
anim_node
]
=
light
.
id
();
add
(
std
::
move
(
light
));
}
}
// animations
for
(
unsigned
int
i
=
0
;
i
<
scene
->
mNumAnimations
;
i
++
)
{
aiAnimation
&
anim
=
*
scene
->
mAnimations
[
i
];
for
(
unsigned
int
j
=
0
;
j
<
anim
.
mNumChannels
;
j
++
)
{
aiNodeAnim
&
node_anim
=
*
anim
.
mChannels
[
j
];
if
(
node_anim
.
mNodeName
==
aiString
(
"camera_node"
))
{
Gui
::
Anim_Camera
&
cam
=
gui
.
get_animate
().
camera
();
unsigned
int
keys
=
std
::
min
(
node_anim
.
mNumPositionKeys
,
std
::
min
(
node_anim
.
mNumRotationKeys
,
node_anim
.
mNumScalingKeys
));
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mPositionKeys
[
k
].
mTime
;
Vec3
pos
=
aiVec
(
node_anim
.
mPositionKeys
[
k
].
mValue
);
Quat
rot
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
);
Vec3
v
=
aiVec
(
node_anim
.
mScalingKeys
[
k
].
mValue
);
cam
.
splines
.
set
(
t
,
pos
,
rot
,
v
.
x
,
v
.
y
);
}
}
else
if
(
std
::
string
(
node_anim
.
mNodeName
.
C_Str
()).
find
(
"LIGHT_ANIM_NODE"
)
!=
std
::
string
::
npos
)
{
aiNode
*
node
=
scene
->
mRootNode
->
FindNode
(
node_anim
.
mNodeName
);
auto
entry
=
node_to_obj
.
find
(
node
);
if
(
entry
!=
node_to_obj
.
end
())
{
Scene_Light
&
l
=
get_light
(
entry
->
second
);
Scene_Light
::
Anim_Light
&
light
=
l
.
lanim
;
unsigned
int
keys
=
std
::
min
(
node_anim
.
mNumPositionKeys
,
std
::
min
(
node_anim
.
mNumRotationKeys
,
node_anim
.
mNumScalingKeys
));
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mPositionKeys
[
k
].
mTime
;
Vec3
spec
=
aiVec
(
node_anim
.
mPositionKeys
[
k
].
mValue
);
Vec3
angle
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
).
to_euler
();
Vec3
inten_sz
=
aiVec
(
node_anim
.
mScalingKeys
[
k
].
mValue
);
light
.
splines
.
set
(
t
,
Spectrum
{
spec
.
x
,
spec
.
y
,
spec
.
z
},
inten_sz
.
x
-
1.0
f
,
Vec2
{
angle
.
x
,
angle
.
z
},
Vec2
{
inten_sz
.
y
-
1.0
f
,
inten_sz
.
z
-
1.0
f
});
}
}
}
else
{
aiNode
*
node
=
scene
->
mRootNode
->
FindNode
(
node_anim
.
mNodeName
);
auto
jentry
=
node_to_bone
.
find
(
node
);
if
(
jentry
!=
node_to_bone
.
end
())
{
Joint
*
jt
=
jentry
->
second
;
unsigned
int
keys
=
node_anim
.
mNumRotationKeys
;
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mRotationKeys
[
k
].
mTime
;
Quat
rot
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
);
jt
->
anim
.
set
(
t
,
rot
);
}
}
else
{
auto
entry
=
node_to_obj
.
find
(
node
);
if
(
entry
!=
node_to_obj
.
end
())
{
Scene_Item
&
item
=
*
get
(
entry
->
second
);
Anim_Pose
&
pose
=
item
.
animation
();
unsigned
int
keys
=
std
::
min
(
node_anim
.
mNumPositionKeys
,
std
::
min
(
node_anim
.
mNumRotationKeys
,
node_anim
.
mNumScalingKeys
));
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mPositionKeys
[
k
].
mTime
;
Vec3
pos
=
aiVec
(
node_anim
.
mPositionKeys
[
k
].
mValue
);
Quat
rot
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
);
Vec3
scl
=
aiVec
(
node_anim
.
mScalingKeys
[
k
].
mValue
);
pose
.
splines
.
set
(
t
,
pos
,
rot
,
scl
);
}
}
}
}
}
if
(
anim
.
mDuration
>
0.0
f
)
{
gui
.
get_animate
().
set
((
int
)
std
::
ceil
(
anim
.
mDuration
),
(
int
)
std
::
round
(
anim
.
mTicksPerSecond
));
}
gui
.
get_animate
().
refresh
(
*
this
);
}
std
::
stringstream
stream
;
if
(
errors
.
size
())
{
stream
<<
"Meshes with errors may not be edit-able in the model mode."
<<
std
::
endl
<<
std
::
endl
;
}
for
(
size_t
i
=
0
;
i
<
errors
.
size
();
i
++
)
{
stream
<<
"Loading mesh "
<<
i
<<
": "
<<
errors
[
i
]
<<
std
::
endl
;
}
return
stream
.
str
();
std
::
string
Scene
::
load
(
bool
new_scene
,
Undo
&
undo
,
Gui
::
Manager
&
gui
,
std
::
string
file
)
{
if
(
new_scene
)
{
clear
(
undo
);
gui
.
get_animate
().
clear
();
gui
.
get_rig
().
clear
();
}
Assimp
::
Importer
importer
;
const
aiScene
*
scene
=
importer
.
ReadFile
(
file
.
c_str
(),
aiProcess_GenSmoothNormals
|
aiProcess_PopulateArmatureData
|
aiProcess_ValidateDataStructure
|
aiProcess_OptimizeMeshes
|
aiProcess_FindInstances
|
aiProcess_FindDegenerates
|
aiProcess_JoinIdenticalVertices
|
aiProcess_FindInvalidData
);
if
(
!
scene
)
{
return
"Parsing scene "
+
file
+
": "
+
std
::
string
(
importer
.
GetErrorString
());
}
std
::
vector
<
std
::
string
>
errors
;
std
::
unordered_map
<
aiNode
*
,
Scene_ID
>
node_to_obj
;
std
::
unordered_map
<
aiNode
*
,
Joint
*>
node_to_bone
;
scene
->
mRootNode
->
mTransformation
=
aiMatrix4x4
();
// Load objects
load_node
(
*
this
,
errors
,
node_to_obj
,
node_to_bone
,
scene
,
scene
->
mRootNode
,
aiMatrix4x4
());
// Load camera
if
(
new_scene
&&
scene
->
mNumCameras
)
{
const
aiCamera
&
aiCam
=
*
scene
->
mCameras
[
0
];
Mat4
cam_transform
=
aiMat
(
node_transform
(
scene
->
mRootNode
->
FindNode
(
aiCam
.
mName
)));
Vec3
pos
=
cam_transform
*
aiVec
(
aiCam
.
mPosition
);
Vec3
center
=
cam_transform
*
aiVec
(
aiCam
.
mLookAt
);
gui
.
get_render
().
load_cam
(
pos
,
center
,
aiCam
.
mAspect
,
aiCam
.
mHorizontalFOV
);
}
// Load lights
for
(
unsigned
int
i
=
0
;
i
<
scene
->
mNumLights
;
i
++
)
{
const
aiLight
&
ailight
=
*
scene
->
mLights
[
i
];
const
aiNode
*
node
=
scene
->
mRootNode
->
FindNode
(
ailight
.
mName
);
Mat4
trans
=
aiMat
(
node_transform
(
node
));
Vec3
pos
=
trans
*
aiVec
(
ailight
.
mPosition
);
Vec3
dir
=
trans
.
rotate
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
Pose
p
;
p
.
pos
=
pos
;
p
.
euler
=
Mat4
::
rotate_to
(
dir
).
to_euler
();
bool
was_exported_from_s3d
=
false
;
float
export_power
=
1.0
f
;
bool
is_sphere
=
false
,
is_hemisphere
=
false
,
is_area
=
false
;
std
::
string
name
=
std
::
string
(
node
->
mName
.
C_Str
());
size_t
special
=
name
.
find
(
"-S3D-"
);
if
(
special
!=
std
::
string
::
npos
)
{
was_exported_from_s3d
=
true
;
export_power
=
ailight
.
mAttenuationQuadratic
;
std
::
string
left
=
name
.
substr
(
special
+
4
);
name
=
name
.
substr
(
0
,
special
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
'_'
,
' '
);
if
(
left
.
find
(
"HEMISPHERE"
)
!=
std
::
string
::
npos
)
is_hemisphere
=
true
;
else
if
(
left
.
find
(
"SPHERE"
)
!=
std
::
string
::
npos
)
is_sphere
=
true
;
else
if
(
left
.
find
(
"AREA"
)
!=
std
::
string
::
npos
)
is_area
=
true
;
}
aiColor3D
color
;
Scene_Light
light
(
Light_Type
::
point
,
reserve_id
(),
p
,
name
);
switch
(
ailight
.
mType
)
{
case
aiLightSource_DIRECTIONAL
:
{
light
.
opt
.
type
=
Light_Type
::
directional
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
case
aiLightSource_POINT
:
{
light
.
opt
.
type
=
Light_Type
::
point
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
case
aiLightSource_SPOT
:
{
light
.
opt
.
type
=
Light_Type
::
spot
;
light
.
opt
.
angle_bounds
.
x
=
ailight
.
mAngleInnerCone
;
light
.
opt
.
angle_bounds
.
y
=
ailight
.
mAngleOuterCone
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
case
aiLightSource_AMBIENT
:
{
if
(
is_hemisphere
)
light
.
opt
.
type
=
Light_Type
::
hemisphere
;
else
if
(
is_sphere
)
{
light
.
opt
.
type
=
Light_Type
::
sphere
;
if
(
ailight
.
mEnvMap
.
length
)
{
light
.
opt
.
has_emissive_map
=
true
;
light
.
emissive_load
(
std
::
string
(
ailight
.
mEnvMap
.
C_Str
()));
}
}
else
if
(
is_area
)
{
light
.
opt
.
type
=
Light_Type
::
rectangle
;
light
.
opt
.
size
.
x
=
ailight
.
mAttenuationConstant
;
light
.
opt
.
size
.
y
=
ailight
.
mAttenuationLinear
;
}
color
=
ailight
.
mColorAmbient
;
}
break
;
case
aiLightSource_AREA
:
{
light
.
opt
.
type
=
Light_Type
::
rectangle
;
light
.
opt
.
size
.
x
=
ailight
.
mSize
.
x
;
light
.
opt
.
size
.
y
=
ailight
.
mSize
.
y
;
color
=
ailight
.
mColorDiffuse
;
}
break
;
default:
continue
;
}
float
power
=
std
::
max
(
color
.
r
,
std
::
max
(
color
.
g
,
color
.
b
));
light
.
opt
.
spectrum
=
Spectrum
(
color
.
r
,
color
.
g
,
color
.
b
)
*
(
1.0
f
/
power
);
light
.
opt
.
intensity
=
power
;
if
(
was_exported_from_s3d
)
{
float
factor
=
power
/
export_power
;
light
.
opt
.
spectrum
*=
factor
;
light
.
opt
.
intensity
=
export_power
;
}
if
(
!
light
.
is_env
()
||
!
has_env_light
())
{
node_to_obj
[(
aiNode
*
)
node
]
=
light
.
id
();
std
::
string
anim_name
=
std
::
string
(
node
->
mName
.
C_Str
())
+
"-LIGHT_ANIM_NODE"
;
aiNode
*
anim_node
=
scene
->
mRootNode
->
FindNode
(
aiString
(
anim_name
));
node_to_obj
[
anim_node
]
=
light
.
id
();
add
(
std
::
move
(
light
));
}
}
// animations
for
(
unsigned
int
i
=
0
;
i
<
scene
->
mNumAnimations
;
i
++
)
{
aiAnimation
&
anim
=
*
scene
->
mAnimations
[
i
];
for
(
unsigned
int
j
=
0
;
j
<
anim
.
mNumChannels
;
j
++
)
{
aiNodeAnim
&
node_anim
=
*
anim
.
mChannels
[
j
];
if
(
node_anim
.
mNodeName
==
aiString
(
"camera_node"
))
{
Gui
::
Anim_Camera
&
cam
=
gui
.
get_animate
().
camera
();
unsigned
int
keys
=
std
::
min
(
node_anim
.
mNumPositionKeys
,
std
::
min
(
node_anim
.
mNumRotationKeys
,
node_anim
.
mNumScalingKeys
));
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mPositionKeys
[
k
].
mTime
;
Vec3
pos
=
aiVec
(
node_anim
.
mPositionKeys
[
k
].
mValue
);
Quat
rot
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
);
Vec3
v
=
aiVec
(
node_anim
.
mScalingKeys
[
k
].
mValue
);
cam
.
splines
.
set
(
t
,
pos
,
rot
,
v
.
x
,
v
.
y
);
}
}
else
if
(
std
::
string
(
node_anim
.
mNodeName
.
C_Str
()).
find
(
"LIGHT_ANIM_NODE"
)
!=
std
::
string
::
npos
)
{
aiNode
*
node
=
scene
->
mRootNode
->
FindNode
(
node_anim
.
mNodeName
);
auto
entry
=
node_to_obj
.
find
(
node
);
if
(
entry
!=
node_to_obj
.
end
())
{
Scene_Light
&
l
=
get_light
(
entry
->
second
);
Scene_Light
::
Anim_Light
&
light
=
l
.
lanim
;
unsigned
int
keys
=
std
::
min
(
node_anim
.
mNumPositionKeys
,
std
::
min
(
node_anim
.
mNumRotationKeys
,
node_anim
.
mNumScalingKeys
));
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mPositionKeys
[
k
].
mTime
;
Vec3
spec
=
aiVec
(
node_anim
.
mPositionKeys
[
k
].
mValue
);
Vec3
angle
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
).
to_euler
();
Vec3
inten_sz
=
aiVec
(
node_anim
.
mScalingKeys
[
k
].
mValue
);
light
.
splines
.
set
(
t
,
Spectrum
{
spec
.
x
,
spec
.
y
,
spec
.
z
},
inten_sz
.
x
-
1.0
f
,
Vec2
{
angle
.
x
,
angle
.
z
},
Vec2
{
inten_sz
.
y
-
1.0
f
,
inten_sz
.
z
-
1.0
f
});
}
}
}
else
{
aiNode
*
node
=
scene
->
mRootNode
->
FindNode
(
node_anim
.
mNodeName
);
auto
jentry
=
node_to_bone
.
find
(
node
);
if
(
jentry
!=
node_to_bone
.
end
())
{
Joint
*
jt
=
jentry
->
second
;
unsigned
int
keys
=
node_anim
.
mNumRotationKeys
;
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mRotationKeys
[
k
].
mTime
;
Quat
rot
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
);
jt
->
anim
.
set
(
t
,
rot
);
}
}
else
{
auto
entry
=
node_to_obj
.
find
(
node
);
if
(
entry
!=
node_to_obj
.
end
())
{
Scene_Item
&
item
=
*
get
(
entry
->
second
);
Anim_Pose
&
pose
=
item
.
animation
();
unsigned
int
keys
=
std
::
min
(
node_anim
.
mNumPositionKeys
,
std
::
min
(
node_anim
.
mNumRotationKeys
,
node_anim
.
mNumScalingKeys
));
for
(
unsigned
int
k
=
0
;
k
<
keys
;
k
++
)
{
float
t
=
(
float
)
node_anim
.
mPositionKeys
[
k
].
mTime
;
Vec3
pos
=
aiVec
(
node_anim
.
mPositionKeys
[
k
].
mValue
);
Quat
rot
=
aiQuat
(
node_anim
.
mRotationKeys
[
k
].
mValue
);
Vec3
scl
=
aiVec
(
node_anim
.
mScalingKeys
[
k
].
mValue
);
pose
.
splines
.
set
(
t
,
pos
,
rot
,
scl
);
}
}
}
}
}
if
(
anim
.
mDuration
>
0.0
f
)
{
gui
.
get_animate
().
set
((
int
)
std
::
ceil
(
anim
.
mDuration
),
(
int
)
std
::
round
(
anim
.
mTicksPerSecond
));
}
gui
.
get_animate
().
refresh
(
*
this
);
}
std
::
stringstream
stream
;
if
(
errors
.
size
())
{
stream
<<
"Meshes with errors may not be edit-able in the model mode."
<<
std
::
endl
<<
std
::
endl
;
}
for
(
size_t
i
=
0
;
i
<
errors
.
size
();
i
++
)
{
stream
<<
"Loading mesh "
<<
i
<<
": "
<<
errors
[
i
]
<<
std
::
endl
;
}
return
stream
.
str
();
}
std
::
string
Scene
::
write
(
std
::
string
file
,
const
Camera
&
render_cam
,
const
Gui
::
Animate
&
animation
)
{
bool
no_real_meshes
=
false
;
size_t
n_meshes
=
0
,
n_lights
=
0
,
n_anims
=
0
;
size_t
n_armatures
=
0
;
for_items
([
&
](
Scene_Item
&
item
)
{
if
(
item
.
is
<
Scene_Object
>
())
{
Scene_Object
&
obj
=
item
.
get
<
Scene_Object
>
();
n_meshes
++
;
if
(
obj
.
anim
.
splines
.
any
())
n_anims
++
;
if
(
obj
.
armature
.
has_bones
())
n_armatures
++
;
obj
.
armature
.
for_joints
([
&
](
Joint
*
j
)
{
if
(
j
->
anim
.
any
())
n_anims
++
;
});
}
else
if
(
item
.
is
<
Scene_Light
>
())
{
if
(
item
.
animation
().
splines
.
any
())
n_anims
++
;
if
(
item
.
get
<
Scene_Light
>
().
lanim
.
splines
.
any
())
n_anims
++
;
n_lights
++
;
}
});
size_t
camera_anim
=
n_anims
;
if
(
animation
.
camera
().
splines
.
any
())
n_anims
++
;
if
(
!
n_meshes
)
{
no_real_meshes
=
true
;
n_meshes
=
1
;
}
aiScene
scene
;
scene
.
mRootNode
=
new
aiNode
();
// materials
scene
.
mMaterials
=
new
aiMaterial
*
[
n_meshes
]();
scene
.
mNumMaterials
=
(
unsigned
int
)
n_meshes
;
// camera
const
Gui
::
Anim_Camera
&
anim_cam
=
animation
.
camera
();
Camera
cam
=
render_cam
;
if
(
anim_cam
.
splines
.
any
())
{
cam
=
animation
.
camera
().
at
(
0.0
f
);
}
scene
.
mCameras
=
new
aiCamera
*
[
1
]();
scene
.
mCameras
[
0
]
=
new
aiCamera
();
scene
.
mCameras
[
0
]
->
mAspect
=
cam
.
get_ar
();
scene
.
mCameras
[
0
]
->
mClipPlaneNear
=
cam
.
get_near
();
scene
.
mCameras
[
0
]
->
mClipPlaneFar
=
100.0
f
;
scene
.
mCameras
[
0
]
->
mLookAt
=
aiVector3D
(
0.0
f
,
0.0
f
,
-
1.0
f
);
scene
.
mCameras
[
0
]
->
mHorizontalFOV
=
Radians
(
cam
.
get_h_fov
());
scene
.
mCameras
[
0
]
->
mName
=
aiString
(
"camera_node"
);
scene
.
mNumCameras
=
1
;
// nodes
size_t
cam_idx
=
n_meshes
+
n_armatures
+
n_lights
*
2
;
size_t
n_nodes
=
cam_idx
+
1
;
scene
.
mRootNode
->
mNumMeshes
=
0
;
scene
.
mRootNode
->
mNumChildren
=
(
unsigned
int
)(
n_nodes
);
scene
.
mRootNode
->
mChildren
=
new
aiNode
*
[
n_nodes
]();
// camera node
Mat4
view
=
cam
.
get_view
().
inverse
();
scene
.
mRootNode
->
mChildren
[
cam_idx
]
=
new
aiNode
();
scene
.
mRootNode
->
mChildren
[
cam_idx
]
->
mNumMeshes
=
0
;
scene
.
mRootNode
->
mChildren
[
cam_idx
]
->
mName
=
aiString
(
"camera_node"
);
scene
.
mRootNode
->
mChildren
[
cam_idx
]
->
mTransformation
=
matMat
(
view
);
// meshes
scene
.
mMeshes
=
new
aiMesh
*
[
n_meshes
]();
scene
.
mNumMeshes
=
(
unsigned
int
)
n_meshes
;
// lights
scene
.
mLights
=
new
aiLight
*
[
n_lights
]();
scene
.
mNumLights
=
(
unsigned
int
)
n_lights
;
size_t
mesh_idx
=
0
,
light_idx
=
0
,
node_idx
=
0
;
if
(
no_real_meshes
)
{
aiMesh
*
ai_mesh
=
new
aiMesh
();
aiNode
*
ai_node
=
new
aiNode
();
scene
.
mMeshes
[
mesh_idx
++
]
=
ai_mesh
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node
;
ai_node
->
mNumMeshes
=
1
;
ai_node
->
mMeshes
=
new
unsigned
int
((
unsigned
int
)
0
);
ai_mesh
->
mVertices
=
new
aiVector3D
[
3
];
ai_mesh
->
mNormals
=
new
aiVector3D
[
3
];
ai_mesh
->
mNumVertices
=
(
unsigned
int
)
3
;
ai_mesh
->
mVertices
[
0
]
=
vecVec
(
Vec3
{
1.0
f
,
0.0
f
,
0.0
f
});
ai_mesh
->
mVertices
[
1
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mVertices
[
2
]
=
vecVec
(
Vec3
{
0.0
f
,
0.0
f
,
1.0
f
});
ai_mesh
->
mNormals
[
0
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mNormals
[
1
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mNormals
[
2
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mFaces
=
new
aiFace
[
1
];
ai_mesh
->
mNumFaces
=
1u
;
ai_mesh
->
mFaces
[
0
].
mIndices
=
new
unsigned
int
[
3
];
ai_mesh
->
mFaces
[
0
].
mNumIndices
=
3
;
ai_mesh
->
mFaces
[
0
].
mIndices
[
0
]
=
0
;
ai_mesh
->
mFaces
[
0
].
mIndices
[
1
]
=
1
;
ai_mesh
->
mFaces
[
0
].
mIndices
[
2
]
=
2
;
ai_mesh
->
mName
=
aiString
(
FAKE_NAME
);
ai_node
->
mName
=
aiString
(
FAKE_NAME
);
scene
.
mMaterials
[
0
]
=
new
aiMaterial
();
}
std
::
unordered_map
<
Scene_ID
,
aiNode
*>
item_nodes
;
std
::
unordered_map
<
std
::
pair
<
Scene_ID
,
Scene_ID
>
,
aiNode
*>
bone_nodes
;
for
(
auto
&
entry
:
objs
)
{
if
(
entry
.
second
.
is
<
Scene_Object
>
())
{
Scene_Object
&
obj
=
entry
.
second
.
get
<
Scene_Object
>
();
if
(
obj
.
is_shape
())
{
obj
.
try_make_editable
(
obj
.
opt
.
shape_type
);
}
aiMesh
*
ai_mesh
=
new
aiMesh
();
aiNode
*
ai_node
=
new
aiNode
();
aiMaterial
*
ai_mat
=
new
aiMaterial
();
size_t
idx
=
mesh_idx
++
;
scene
.
mMaterials
[
idx
]
=
ai_mat
;
scene
.
mMeshes
[
idx
]
=
ai_mesh
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node
;
ai_mesh
->
mMaterialIndex
=
(
unsigned
int
)
idx
;
ai_node
->
mNumMeshes
=
1
;
ai_node
->
mMeshes
=
new
unsigned
int
((
unsigned
int
)
idx
);
if
(
obj
.
is_editable
())
{
Halfedge_Mesh
&
mesh
=
obj
.
get_mesh
();
size_t
n_verts
=
mesh
.
n_vertices
();
size_t
n_faces
=
mesh
.
n_faces
();
ai_mesh
->
mVertices
=
new
aiVector3D
[
n_verts
];
ai_mesh
->
mNormals
=
new
aiVector3D
[
n_verts
];
ai_mesh
->
mNumVertices
=
(
unsigned
int
)
n_verts
;
ai_mesh
->
mFaces
=
new
aiFace
[
n_faces
];
ai_mesh
->
mNumFaces
=
(
unsigned
int
)(
n_faces
);
std
::
unordered_map
<
size_t
,
size_t
>
id_to_idx
;
size_t
vert_idx
=
0
;
for
(
auto
v
=
mesh
.
vertices_begin
();
v
!=
mesh
.
vertices_end
();
v
++
)
{
id_to_idx
[
v
->
id
()]
=
vert_idx
;
Vec3
n
=
mesh
.
flipped
()
?
-
v
->
normal
()
:
v
->
normal
();
ai_mesh
->
mVertices
[
vert_idx
]
=
vecVec
(
v
->
pos
);
ai_mesh
->
mNormals
[
vert_idx
]
=
vecVec
(
n
);
vert_idx
++
;
}
size_t
face_idx
=
0
;
for
(
auto
f
=
mesh
.
faces_begin
();
f
!=
mesh
.
faces_end
();
f
++
)
{
aiFace
&
face
=
ai_mesh
->
mFaces
[
face_idx
];
face
.
mIndices
=
new
unsigned
int
[
f
->
degree
()];
face
.
mNumIndices
=
f
->
degree
();
size_t
i_idx
=
0
;
auto
h
=
f
->
halfedge
();
do
{
face
.
mIndices
[
i_idx
]
=
(
unsigned
int
)
id_to_idx
[
h
->
vertex
()
->
id
()];
h
=
h
->
next
();
i_idx
++
;
}
while
(
h
!=
f
->
halfedge
());
face_idx
++
;
}
}
else
{
const
auto
&
verts
=
obj
.
mesh
().
verts
();
const
auto
&
elems
=
obj
.
mesh
().
indices
();
ai_mesh
->
mVertices
=
new
aiVector3D
[
verts
.
size
()];
ai_mesh
->
mNormals
=
new
aiVector3D
[
verts
.
size
()];
ai_mesh
->
mNumVertices
=
(
unsigned
int
)
verts
.
size
();
int
j
=
0
;
for
(
GL
::
Mesh
::
Vert
v
:
verts
)
{
ai_mesh
->
mVertices
[
j
]
=
vecVec
(
v
.
pos
);
ai_mesh
->
mNormals
[
j
]
=
vecVec
(
v
.
norm
);
j
++
;
}
ai_mesh
->
mFaces
=
new
aiFace
[
elems
.
size
()
/
3
];
ai_mesh
->
mNumFaces
=
(
unsigned
int
)(
elems
.
size
()
/
3
);
for
(
size_t
i
=
0
;
i
<
elems
.
size
();
i
+=
3
)
{
aiFace
&
face
=
ai_mesh
->
mFaces
[
i
/
3
];
face
.
mIndices
=
new
unsigned
int
[
3
];
face
.
mNumIndices
=
3
;
face
.
mIndices
[
0
]
=
elems
[
i
];
face
.
mIndices
[
1
]
=
elems
[
i
+
1
];
face
.
mIndices
[
2
]
=
elems
[
i
+
2
];
}
}
std
::
string
name
(
obj
.
opt
.
name
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
' '
,
'_'
);
name
+=
"-S3D-"
+
std
::
to_string
(
obj
.
id
());
if
(
obj
.
get_mesh
().
flipped
())
name
+=
"-FLIPPED"
;
if
(
obj
.
opt
.
smooth_normals
)
name
+=
"-SMOOTHED"
;
ai_mesh
->
mName
=
aiString
(
name
);
Mat4
trans
=
obj
.
pose
.
transform
();
item_nodes
[
obj
.
id
()]
=
ai_node
;
ai_node
->
mName
=
aiString
(
name
);
ai_node
->
mTransformation
=
matMat
(
trans
);
const
Material
::
Options
&
opt
=
obj
.
material
.
opt
;
std
::
string
mat_name
;
switch
(
opt
.
type
)
{
case
Material_Type
::
lambertian
:
{
mat_name
=
"lambertian"
;
}
break
;
case
Material_Type
::
mirror
:
{
mat_name
=
"mirror"
;
}
break
;
case
Material_Type
::
refract
:
{
mat_name
=
"refract"
;
}
break
;
case
Material_Type
::
glass
:
{
mat_name
=
"glass"
;
}
break
;
case
Material_Type
::
diffuse_light
:
{
mat_name
=
"diffuse_light"
;
}
break
;
default:
break
;
}
// Horrible hack
if
(
obj
.
opt
.
shape_type
==
PT
::
Shape_Type
::
sphere
)
{
mat_name
+=
"-SPHERESHAPE"
;
ai_mat
->
AddProperty
(
new
aiColor3D
(
obj
.
opt
.
shape
.
get
<
PT
::
Sphere
>
().
radius
),
1
,
AI_MATKEY_COLOR_SPECULAR
);
}
Spectrum
emissive
=
obj
.
material
.
emissive
();
// diffuse -> albedo
// reflective -> reflectance
// transparent -> transmittance
// emissive -> emissive
// refracti -> index of refraction
// shininess -> intensity
ai_mat
->
AddProperty
(
new
aiString
(
mat_name
),
AI_MATKEY_NAME
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
opt
.
albedo
.
r
,
opt
.
albedo
.
g
,
opt
.
albedo
.
b
),
1
,
AI_MATKEY_COLOR_DIFFUSE
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
opt
.
reflectance
.
r
,
opt
.
reflectance
.
g
,
opt
.
reflectance
.
b
),
1
,
AI_MATKEY_COLOR_REFLECTIVE
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
opt
.
transmittance
.
r
,
opt
.
transmittance
.
g
,
opt
.
transmittance
.
b
),
1
,
AI_MATKEY_COLOR_TRANSPARENT
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
emissive
.
r
,
emissive
.
g
,
emissive
.
b
),
1
,
AI_MATKEY_COLOR_EMISSIVE
);
ai_mat
->
AddProperty
(
new
float
(
opt
.
ior
),
1
,
AI_MATKEY_REFRACTI
);
ai_mat
->
AddProperty
(
new
float
(
opt
.
intensity
),
1
,
AI_MATKEY_SHININESS
);
if
(
obj
.
armature
.
has_bones
())
{
ai_mesh
->
mNumBones
=
obj
.
armature
.
n_bones
();
ai_mesh
->
mBones
=
new
aiBone
*
[
ai_mesh
->
mNumBones
];
size_t
bone_idx
=
0
;
std
::
string
prefix
=
"S3D-joint-"
+
std
::
to_string
(
obj
.
id
())
+
"-"
;
aiNode
*
arm_node
=
new
aiNode
();
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
arm_node
;
arm_node
->
mName
=
aiString
(
prefix
+
"armature"
);
arm_node
->
mTransformation
=
matMat
(
Mat4
::
translate
(
obj
.
armature
.
base_pos
));
arm_node
->
mNumChildren
=
(
unsigned
int
)
obj
.
armature
.
roots
.
size
();
arm_node
->
mChildren
=
new
aiNode
*
[
obj
.
armature
.
roots
.
size
()];
std
::
unordered_map
<
Joint
*
,
aiNode
*>
j_to_node
;
std
::
function
<
void
(
std
::
string
,
aiNode
*
,
Joint
*
)
>
joint_tree
;
joint_tree
=
[
&
joint_tree
,
&
j_to_node
](
std
::
string
n
,
aiNode
*
node
,
Joint
*
j
)
{
std
::
string
name
=
n
+
std
::
to_string
(
j
->
_id
);
node
->
mName
=
aiString
(
name
);
j_to_node
[
j
]
=
node
;
if
(
j
->
children
.
size
())
{
node
->
mNumChildren
=
(
unsigned
int
)
j
->
children
.
size
();
node
->
mChildren
=
new
aiNode
*
[
j
->
children
.
size
()];
size_t
i
=
0
;
for
(
Joint
*
c
:
j
->
children
)
{
node
->
mChildren
[
i
]
=
new
aiNode
();
joint_tree
(
n
,
node
->
mChildren
[
i
],
c
);
i
++
;
}
}
};
size_t
i
=
0
;
for
(
Joint
*
j
:
obj
.
armature
.
roots
)
{
aiNode
*
root_node
=
new
aiNode
();
arm_node
->
mChildren
[
i
]
=
root_node
;
j_to_node
[
j
]
=
root_node
;
joint_tree
(
prefix
,
root_node
,
j
);
i
++
;
}
for
(
auto
[
j
,
n
]
:
j_to_node
)
{
bone_nodes
.
insert
({{
obj
.
id
(),
j
->
_id
},
n
});
}
obj
.
armature
.
for_joints
([
&
](
Joint
*
j
)
{
aiBone
*
bone
=
new
aiBone
();
ai_mesh
->
mBones
[
bone_idx
++
]
=
bone
;
bone
->
mOffsetMatrix
=
matMat
(
Mat4
::
translate
(
j
->
extent
)
*
Mat4
::
euler
(
j
->
pose
));
bone
->
mNode
=
j_to_node
[
j
];
std
::
string
name
=
prefix
+
std
::
to_string
(
j
->
_id
);
bone
->
mName
=
aiString
(
name
);
bone
->
mNumWeights
=
1
;
bone
->
mWeights
=
new
aiVertexWeight
[
1
];
bone
->
mWeights
[
0
].
mWeight
=
j
->
radius
;
});
}
}
else
if
(
entry
.
second
.
is
<
Scene_Light
>
())
{
const
Scene_Light
&
light
=
entry
.
second
.
get
<
Scene_Light
>
();
std
::
string
name
(
light
.
opt
.
name
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
' '
,
'_'
);
aiLight
*
ai_light
=
new
aiLight
();
aiNode
*
ai_node
=
new
aiNode
();
aiNode
*
ai_node_light
=
new
aiNode
();
scene
.
mLights
[
light_idx
++
]
=
ai_light
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node_light
;
switch
(
light
.
opt
.
type
)
{
case
Light_Type
::
directional
:
{
ai_light
->
mType
=
aiLightSource_DIRECTIONAL
;
name
+=
"-S3D-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
sphere
:
{
ai_light
->
mType
=
aiLightSource_AMBIENT
;
name
+=
"-S3D-SPHERE-"
+
std
::
to_string
(
light
.
id
());
if
(
light
.
opt
.
has_emissive_map
)
{
ai_light
->
mEnvMap
=
aiString
(
light
.
emissive_loaded
());
}
}
break
;
case
Light_Type
::
hemisphere
:
{
ai_light
->
mType
=
aiLightSource_AMBIENT
;
name
+=
"-S3D-HEMISPHERE-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
point
:
{
ai_light
->
mType
=
aiLightSource_POINT
;
name
+=
"-S3D-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
spot
:
{
ai_light
->
mType
=
aiLightSource_SPOT
;
name
+=
"-S3D-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
rectangle
:
{
// the collada exporter literally just ignores area lights ????????
ai_light
->
mType
=
aiLightSource_AMBIENT
;
name
+=
"-S3D-AREA-"
+
std
::
to_string
(
light
.
id
());
ai_light
->
mAttenuationConstant
=
light
.
opt
.
size
.
x
;
ai_light
->
mAttenuationLinear
=
light
.
opt
.
size
.
y
;
}
break
;
default:
break
;
}
Spectrum
r
=
light
.
radiance
();
ai_light
->
mName
=
aiString
(
name
);
ai_light
->
mPosition
=
aiVector3D
(
0.0
f
,
0.0
f
,
0.0
f
);
ai_light
->
mDirection
=
aiVector3D
(
0.0
f
,
1.0
f
,
0.0
f
);
ai_light
->
mUp
=
aiVector3D
(
0.0
f
,
1.0
f
,
0.0
f
);
ai_light
->
mSize
=
aiVector2D
(
light
.
opt
.
size
.
x
,
light
.
opt
.
size
.
y
);
ai_light
->
mAngleInnerCone
=
light
.
opt
.
angle_bounds
.
x
;
ai_light
->
mAngleOuterCone
=
light
.
opt
.
angle_bounds
.
y
;
ai_light
->
mColorDiffuse
=
aiColor3D
(
r
.
r
,
r
.
g
,
r
.
b
);
ai_light
->
mColorAmbient
=
aiColor3D
(
r
.
r
,
r
.
g
,
r
.
b
);
ai_light
->
mColorDiffuse
=
aiColor3D
(
r
.
r
,
r
.
g
,
r
.
b
);
ai_light
->
mAttenuationQuadratic
=
light
.
opt
.
intensity
;
Mat4
T
=
light
.
pose
.
transform
();
item_nodes
[
light
.
id
()]
=
ai_node
;
ai_node
->
mName
=
aiString
(
name
);
ai_node
->
mNumMeshes
=
0
;
ai_node
->
mMeshes
=
nullptr
;
ai_node
->
mTransformation
=
matMat
(
T
);
ai_node_light
->
mName
=
aiString
(
name
+
"-LIGHT_ANIM_NODE"
);
ai_node_light
->
mNumMeshes
=
0
;
ai_node_light
->
mMeshes
=
nullptr
;
ai_node_light
->
mTransformation
=
aiMatrix4x4
();
}
}
{
scene
.
mNumAnimations
=
1
;
scene
.
mAnimations
=
new
aiAnimation
*
[
1
];
scene
.
mAnimations
[
0
]
=
new
aiAnimation
();
aiAnimation
&
ai_anim
=
*
scene
.
mAnimations
[
0
];
ai_anim
.
mName
=
aiString
(
"Scotty3D-animate"
);
ai_anim
.
mDuration
=
(
double
)
animation
.
n_frames
();
ai_anim
.
mTicksPerSecond
=
(
double
)
animation
.
fps
();
ai_anim
.
mNumChannels
=
(
unsigned
int
)
n_anims
;
ai_anim
.
mChannels
=
new
aiNodeAnim
*
[
n_anims
];
auto
write_keyframes
=
[](
aiNodeAnim
*
node
,
std
::
string
name
,
auto
pt
,
auto
rot
,
auto
scl
)
{
node
->
mNodeName
=
aiString
(
name
);
node
->
mNumPositionKeys
=
(
unsigned
int
)
pt
.
size
();
node
->
mNumRotationKeys
=
(
unsigned
int
)
rot
.
size
();
node
->
mNumScalingKeys
=
(
unsigned
int
)
scl
.
size
();
node
->
mPositionKeys
=
new
aiVectorKey
[
pt
.
size
()];
node
->
mRotationKeys
=
new
aiQuatKey
[
rot
.
size
()];
node
->
mScalingKeys
=
new
aiVectorKey
[
scl
.
size
()];
size_t
i
=
0
;
for
(
auto
&
e
:
pt
)
{
node
->
mPositionKeys
[
i
]
=
aiVectorKey
(
e
.
first
,
vecVec
(
e
.
second
));
i
++
;
}
i
=
0
;
for
(
auto
&
e
:
rot
)
{
node
->
mRotationKeys
[
i
]
=
aiQuatKey
(
e
.
first
,
aiQuaternion
(
e
.
second
.
w
,
e
.
second
.
x
,
e
.
second
.
y
,
e
.
second
.
z
));
i
++
;
}
i
=
0
;
for
(
auto
&
e
:
scl
)
{
node
->
mScalingKeys
[
i
]
=
aiVectorKey
(
e
.
first
,
vecVec
(
e
.
second
));
i
++
;
}
};
if
(
anim_cam
.
splines
.
any
())
{
ai_anim
.
mChannels
[
camera_anim
]
=
new
aiNodeAnim
();
std
::
set
<
float
>
keys
=
anim_cam
.
splines
.
keys
();
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
for
(
float
k
:
keys
)
{
auto
[
p
,
r
,
fov
,
ar
]
=
anim_cam
.
splines
.
at
(
k
);
points
[
k
]
=
p
;
rotations
[
k
]
=
r
;
scales
[
k
]
=
Vec3
{
fov
,
ar
,
1.0
f
};
}
write_keyframes
(
ai_anim
.
mChannels
[
camera_anim
],
"camera_node"
,
points
,
rotations
,
scales
);
}
size_t
anim_idx
=
0
;
for
(
auto
&
entry
:
objs
)
{
Scene_Item
&
item
=
entry
.
second
;
const
Anim_Pose
&
pose
=
item
.
animation
();
if
(
pose
.
splines
.
any
())
{
std
::
set
<
float
>
keys
=
pose
.
splines
.
keys
();
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
for
(
float
k
:
keys
)
{
auto
[
p
,
r
,
s
]
=
pose
.
splines
.
at
(
k
);
points
[
k
]
=
p
;
rotations
[
k
]
=
r
;
scales
[
k
]
=
s
;
}
aiNode
*
node
=
item_nodes
[
item
.
id
()];
ai_anim
.
mChannels
[
anim_idx
]
=
new
aiNodeAnim
();
write_keyframes
(
ai_anim
.
mChannels
[
anim_idx
],
std
::
string
(
node
->
mName
.
C_Str
()),
points
,
rotations
,
scales
);
anim_idx
++
;
}
if
(
item
.
is
<
Scene_Object
>
())
{
Skeleton
&
skel
=
item
.
get
<
Scene_Object
>
().
armature
;
if
(
skel
.
has_keyframes
())
{
skel
.
for_joints
([
&
](
Joint
*
j
)
{
std
::
set
<
float
>
keys
=
j
->
anim
.
keys
();
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
for
(
float
k
:
keys
)
{
points
[
k
]
=
Vec3
{
0.0
f
};
rotations
[
k
]
=
j
->
anim
.
at
(
k
);
scales
[
k
]
=
Vec3
{
1.0
f
};
}
aiNode
*
node
=
bone_nodes
[{
item
.
id
(),
j
->
_id
}];
ai_anim
.
mChannels
[
anim_idx
]
=
new
aiNodeAnim
();
write_keyframes
(
ai_anim
.
mChannels
[
anim_idx
],
std
::
string
(
node
->
mName
.
C_Str
()),
points
,
rotations
,
scales
);
anim_idx
++
;
});
}
}
else
if
(
item
.
is
<
Scene_Light
>
())
{
aiNode
*
node
=
item_nodes
[
item
.
id
()];
std
::
string
name
(
node
->
mName
.
C_Str
());
name
+=
"-LIGHT_ANIM_NODE"
;
const
Scene_Light
::
Anim_Light
&
light
=
item
.
get
<
Scene_Light
>
().
lanim
;
if
(
light
.
splines
.
any
())
{
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
std
::
set
<
float
>
keys
=
light
.
splines
.
keys
();
for
(
float
k
:
keys
)
{
auto
[
spec
,
inten
,
angle
,
size
]
=
light
.
splines
.
at
(
k
);
points
[
k
]
=
Vec3
{
spec
.
r
,
spec
.
g
,
spec
.
b
};
rotations
[
k
]
=
Quat
::
euler
(
Vec3
{
angle
.
x
,
0.0
f
,
angle
.
y
});
scales
[
k
]
=
Vec3
{
inten
+
1.0
f
,
size
.
x
+
1.0
f
,
size
.
y
+
1.0
f
};
}
ai_anim
.
mChannels
[
anim_idx
]
=
new
aiNodeAnim
();
write_keyframes
(
ai_anim
.
mChannels
[
anim_idx
],
name
,
points
,
rotations
,
scales
);
anim_idx
++
;
}
}
}
}
// Note: exporter/scene destructor should free everything
Assimp
::
Exporter
exporter
;
if
(
exporter
.
Export
(
&
scene
,
"collada"
,
file
.
c_str
()))
{
return
std
::
string
(
exporter
.
GetErrorString
());
}
return
{};
std
::
string
Scene
::
write
(
std
::
string
file
,
const
Camera
&
render_cam
,
const
Gui
::
Animate
&
animation
)
{
bool
no_real_meshes
=
false
;
size_t
n_meshes
=
0
,
n_lights
=
0
,
n_anims
=
0
;
size_t
n_armatures
=
0
;
for_items
([
&
](
Scene_Item
&
item
)
{
if
(
item
.
is
<
Scene_Object
>
())
{
Scene_Object
&
obj
=
item
.
get
<
Scene_Object
>
();
n_meshes
++
;
if
(
obj
.
anim
.
splines
.
any
())
n_anims
++
;
if
(
obj
.
armature
.
has_bones
())
n_armatures
++
;
obj
.
armature
.
for_joints
([
&
](
Joint
*
j
)
{
if
(
j
->
anim
.
any
())
n_anims
++
;
});
}
else
if
(
item
.
is
<
Scene_Light
>
())
{
if
(
item
.
animation
().
splines
.
any
())
n_anims
++
;
if
(
item
.
get
<
Scene_Light
>
().
lanim
.
splines
.
any
())
n_anims
++
;
n_lights
++
;
}
});
size_t
camera_anim
=
n_anims
;
if
(
animation
.
camera
().
splines
.
any
())
n_anims
++
;
if
(
!
n_meshes
)
{
no_real_meshes
=
true
;
n_meshes
=
1
;
}
aiScene
scene
;
scene
.
mRootNode
=
new
aiNode
();
// materials
scene
.
mMaterials
=
new
aiMaterial
*
[
n_meshes
]();
scene
.
mNumMaterials
=
(
unsigned
int
)
n_meshes
;
// camera
const
Gui
::
Anim_Camera
&
anim_cam
=
animation
.
camera
();
Camera
cam
=
render_cam
;
if
(
anim_cam
.
splines
.
any
())
{
cam
=
animation
.
camera
().
at
(
0.0
f
);
}
scene
.
mCameras
=
new
aiCamera
*
[
1
]();
scene
.
mCameras
[
0
]
=
new
aiCamera
();
scene
.
mCameras
[
0
]
->
mAspect
=
cam
.
get_ar
();
scene
.
mCameras
[
0
]
->
mClipPlaneNear
=
cam
.
get_near
();
scene
.
mCameras
[
0
]
->
mClipPlaneFar
=
100.0
f
;
scene
.
mCameras
[
0
]
->
mLookAt
=
aiVector3D
(
0.0
f
,
0.0
f
,
-
1.0
f
);
scene
.
mCameras
[
0
]
->
mHorizontalFOV
=
Radians
(
cam
.
get_h_fov
());
scene
.
mCameras
[
0
]
->
mName
=
aiString
(
"camera_node"
);
scene
.
mNumCameras
=
1
;
// nodes
size_t
cam_idx
=
n_meshes
+
n_armatures
+
n_lights
*
2
;
size_t
n_nodes
=
cam_idx
+
1
;
scene
.
mRootNode
->
mNumMeshes
=
0
;
scene
.
mRootNode
->
mNumChildren
=
(
unsigned
int
)(
n_nodes
);
scene
.
mRootNode
->
mChildren
=
new
aiNode
*
[
n_nodes
]();
// camera node
Mat4
view
=
cam
.
get_view
().
inverse
();
scene
.
mRootNode
->
mChildren
[
cam_idx
]
=
new
aiNode
();
scene
.
mRootNode
->
mChildren
[
cam_idx
]
->
mNumMeshes
=
0
;
scene
.
mRootNode
->
mChildren
[
cam_idx
]
->
mName
=
aiString
(
"camera_node"
);
scene
.
mRootNode
->
mChildren
[
cam_idx
]
->
mTransformation
=
matMat
(
view
);
// meshes
scene
.
mMeshes
=
new
aiMesh
*
[
n_meshes
]();
scene
.
mNumMeshes
=
(
unsigned
int
)
n_meshes
;
// lights
scene
.
mLights
=
new
aiLight
*
[
n_lights
]();
scene
.
mNumLights
=
(
unsigned
int
)
n_lights
;
size_t
mesh_idx
=
0
,
light_idx
=
0
,
node_idx
=
0
;
if
(
no_real_meshes
)
{
aiMesh
*
ai_mesh
=
new
aiMesh
();
aiNode
*
ai_node
=
new
aiNode
();
scene
.
mMeshes
[
mesh_idx
++
]
=
ai_mesh
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node
;
ai_node
->
mNumMeshes
=
1
;
ai_node
->
mMeshes
=
new
unsigned
int
((
unsigned
int
)
0
);
ai_mesh
->
mVertices
=
new
aiVector3D
[
3
];
ai_mesh
->
mNormals
=
new
aiVector3D
[
3
];
ai_mesh
->
mNumVertices
=
(
unsigned
int
)
3
;
ai_mesh
->
mVertices
[
0
]
=
vecVec
(
Vec3
{
1.0
f
,
0.0
f
,
0.0
f
});
ai_mesh
->
mVertices
[
1
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mVertices
[
2
]
=
vecVec
(
Vec3
{
0.0
f
,
0.0
f
,
1.0
f
});
ai_mesh
->
mNormals
[
0
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mNormals
[
1
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mNormals
[
2
]
=
vecVec
(
Vec3
{
0.0
f
,
1.0
f
,
0.0
f
});
ai_mesh
->
mFaces
=
new
aiFace
[
1
];
ai_mesh
->
mNumFaces
=
1u
;
ai_mesh
->
mFaces
[
0
].
mIndices
=
new
unsigned
int
[
3
];
ai_mesh
->
mFaces
[
0
].
mNumIndices
=
3
;
ai_mesh
->
mFaces
[
0
].
mIndices
[
0
]
=
0
;
ai_mesh
->
mFaces
[
0
].
mIndices
[
1
]
=
1
;
ai_mesh
->
mFaces
[
0
].
mIndices
[
2
]
=
2
;
ai_mesh
->
mName
=
aiString
(
FAKE_NAME
);
ai_node
->
mName
=
aiString
(
FAKE_NAME
);
scene
.
mMaterials
[
0
]
=
new
aiMaterial
();
}
std
::
unordered_map
<
Scene_ID
,
aiNode
*>
item_nodes
;
std
::
unordered_map
<
std
::
pair
<
Scene_ID
,
Scene_ID
>
,
aiNode
*>
bone_nodes
;
for
(
auto
&
entry
:
objs
)
{
if
(
entry
.
second
.
is
<
Scene_Object
>
())
{
Scene_Object
&
obj
=
entry
.
second
.
get
<
Scene_Object
>
();
if
(
obj
.
is_shape
())
{
obj
.
try_make_editable
(
obj
.
opt
.
shape_type
);
}
aiMesh
*
ai_mesh
=
new
aiMesh
();
aiNode
*
ai_node
=
new
aiNode
();
aiMaterial
*
ai_mat
=
new
aiMaterial
();
size_t
idx
=
mesh_idx
++
;
scene
.
mMaterials
[
idx
]
=
ai_mat
;
scene
.
mMeshes
[
idx
]
=
ai_mesh
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node
;
ai_mesh
->
mMaterialIndex
=
(
unsigned
int
)
idx
;
ai_node
->
mNumMeshes
=
1
;
ai_node
->
mMeshes
=
new
unsigned
int
((
unsigned
int
)
idx
);
if
(
obj
.
is_editable
())
{
Halfedge_Mesh
&
mesh
=
obj
.
get_mesh
();
size_t
n_verts
=
mesh
.
n_vertices
();
size_t
n_faces
=
mesh
.
n_faces
();
ai_mesh
->
mVertices
=
new
aiVector3D
[
n_verts
];
ai_mesh
->
mNormals
=
new
aiVector3D
[
n_verts
];
ai_mesh
->
mNumVertices
=
(
unsigned
int
)
n_verts
;
ai_mesh
->
mFaces
=
new
aiFace
[
n_faces
];
ai_mesh
->
mNumFaces
=
(
unsigned
int
)(
n_faces
);
std
::
unordered_map
<
size_t
,
size_t
>
id_to_idx
;
size_t
vert_idx
=
0
;
for
(
auto
v
=
mesh
.
vertices_begin
();
v
!=
mesh
.
vertices_end
();
v
++
)
{
id_to_idx
[
v
->
id
()]
=
vert_idx
;
Vec3
n
=
mesh
.
flipped
()
?
-
v
->
normal
()
:
v
->
normal
();
ai_mesh
->
mVertices
[
vert_idx
]
=
vecVec
(
v
->
pos
);
ai_mesh
->
mNormals
[
vert_idx
]
=
vecVec
(
n
);
vert_idx
++
;
}
size_t
face_idx
=
0
;
for
(
auto
f
=
mesh
.
faces_begin
();
f
!=
mesh
.
faces_end
();
f
++
)
{
aiFace
&
face
=
ai_mesh
->
mFaces
[
face_idx
];
face
.
mIndices
=
new
unsigned
int
[
f
->
degree
()];
face
.
mNumIndices
=
f
->
degree
();
size_t
i_idx
=
0
;
auto
h
=
f
->
halfedge
();
do
{
face
.
mIndices
[
i_idx
]
=
(
unsigned
int
)
id_to_idx
[
h
->
vertex
()
->
id
()];
h
=
h
->
next
();
i_idx
++
;
}
while
(
h
!=
f
->
halfedge
());
face_idx
++
;
}
}
else
{
const
auto
&
verts
=
obj
.
mesh
().
verts
();
const
auto
&
elems
=
obj
.
mesh
().
indices
();
ai_mesh
->
mVertices
=
new
aiVector3D
[
verts
.
size
()];
ai_mesh
->
mNormals
=
new
aiVector3D
[
verts
.
size
()];
ai_mesh
->
mNumVertices
=
(
unsigned
int
)
verts
.
size
();
int
j
=
0
;
for
(
GL
::
Mesh
::
Vert
v
:
verts
)
{
ai_mesh
->
mVertices
[
j
]
=
vecVec
(
v
.
pos
);
ai_mesh
->
mNormals
[
j
]
=
vecVec
(
v
.
norm
);
j
++
;
}
ai_mesh
->
mFaces
=
new
aiFace
[
elems
.
size
()
/
3
];
ai_mesh
->
mNumFaces
=
(
unsigned
int
)(
elems
.
size
()
/
3
);
for
(
size_t
i
=
0
;
i
<
elems
.
size
();
i
+=
3
)
{
aiFace
&
face
=
ai_mesh
->
mFaces
[
i
/
3
];
face
.
mIndices
=
new
unsigned
int
[
3
];
face
.
mNumIndices
=
3
;
face
.
mIndices
[
0
]
=
elems
[
i
];
face
.
mIndices
[
1
]
=
elems
[
i
+
1
];
face
.
mIndices
[
2
]
=
elems
[
i
+
2
];
}
}
std
::
string
name
(
obj
.
opt
.
name
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
' '
,
'_'
);
name
+=
"-S3D-"
+
std
::
to_string
(
obj
.
id
());
if
(
obj
.
get_mesh
().
flipped
())
name
+=
"-FLIPPED"
;
if
(
obj
.
opt
.
smooth_normals
)
name
+=
"-SMOOTHED"
;
ai_mesh
->
mName
=
aiString
(
name
);
Mat4
trans
=
obj
.
pose
.
transform
();
item_nodes
[
obj
.
id
()]
=
ai_node
;
ai_node
->
mName
=
aiString
(
name
);
ai_node
->
mTransformation
=
matMat
(
trans
);
const
Material
::
Options
&
opt
=
obj
.
material
.
opt
;
std
::
string
mat_name
;
switch
(
opt
.
type
)
{
case
Material_Type
::
lambertian
:
{
mat_name
=
"lambertian"
;
}
break
;
case
Material_Type
::
mirror
:
{
mat_name
=
"mirror"
;
}
break
;
case
Material_Type
::
refract
:
{
mat_name
=
"refract"
;
}
break
;
case
Material_Type
::
glass
:
{
mat_name
=
"glass"
;
}
break
;
case
Material_Type
::
diffuse_light
:
{
mat_name
=
"diffuse_light"
;
}
break
;
default:
break
;
}
// Horrible hack
if
(
obj
.
opt
.
shape_type
==
PT
::
Shape_Type
::
sphere
)
{
mat_name
+=
"-SPHERESHAPE"
;
ai_mat
->
AddProperty
(
new
aiColor3D
(
obj
.
opt
.
shape
.
get
<
PT
::
Sphere
>
().
radius
),
1
,
AI_MATKEY_COLOR_SPECULAR
);
}
Spectrum
emissive
=
obj
.
material
.
emissive
();
// diffuse -> albedo
// reflective -> reflectance
// transparent -> transmittance
// emissive -> emissive
// refracti -> index of refraction
// shininess -> intensity
ai_mat
->
AddProperty
(
new
aiString
(
mat_name
),
AI_MATKEY_NAME
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
opt
.
albedo
.
r
,
opt
.
albedo
.
g
,
opt
.
albedo
.
b
),
1
,
AI_MATKEY_COLOR_DIFFUSE
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
opt
.
reflectance
.
r
,
opt
.
reflectance
.
g
,
opt
.
reflectance
.
b
),
1
,
AI_MATKEY_COLOR_REFLECTIVE
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
opt
.
transmittance
.
r
,
opt
.
transmittance
.
g
,
opt
.
transmittance
.
b
),
1
,
AI_MATKEY_COLOR_TRANSPARENT
);
ai_mat
->
AddProperty
(
new
aiColor3D
(
emissive
.
r
,
emissive
.
g
,
emissive
.
b
),
1
,
AI_MATKEY_COLOR_EMISSIVE
);
ai_mat
->
AddProperty
(
new
float
(
opt
.
ior
),
1
,
AI_MATKEY_REFRACTI
);
ai_mat
->
AddProperty
(
new
float
(
opt
.
intensity
),
1
,
AI_MATKEY_SHININESS
);
if
(
obj
.
armature
.
has_bones
())
{
ai_mesh
->
mNumBones
=
obj
.
armature
.
n_bones
();
ai_mesh
->
mBones
=
new
aiBone
*
[
ai_mesh
->
mNumBones
];
size_t
bone_idx
=
0
;
std
::
string
prefix
=
"S3D-joint-"
+
std
::
to_string
(
obj
.
id
())
+
"-"
;
aiNode
*
arm_node
=
new
aiNode
();
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
arm_node
;
arm_node
->
mName
=
aiString
(
prefix
+
"armature"
);
arm_node
->
mTransformation
=
matMat
(
Mat4
::
translate
(
obj
.
armature
.
base_pos
));
arm_node
->
mNumChildren
=
(
unsigned
int
)
obj
.
armature
.
roots
.
size
();
arm_node
->
mChildren
=
new
aiNode
*
[
obj
.
armature
.
roots
.
size
()];
std
::
unordered_map
<
Joint
*
,
aiNode
*>
j_to_node
;
std
::
function
<
void
(
std
::
string
,
aiNode
*
,
Joint
*
)
>
joint_tree
;
joint_tree
=
[
&
joint_tree
,
&
j_to_node
](
std
::
string
n
,
aiNode
*
node
,
Joint
*
j
)
{
std
::
string
name
=
n
+
std
::
to_string
(
j
->
_id
);
node
->
mName
=
aiString
(
name
);
j_to_node
[
j
]
=
node
;
if
(
j
->
children
.
size
())
{
node
->
mNumChildren
=
(
unsigned
int
)
j
->
children
.
size
();
node
->
mChildren
=
new
aiNode
*
[
j
->
children
.
size
()];
size_t
i
=
0
;
for
(
Joint
*
c
:
j
->
children
)
{
node
->
mChildren
[
i
]
=
new
aiNode
();
joint_tree
(
n
,
node
->
mChildren
[
i
],
c
);
i
++
;
}
}
};
size_t
i
=
0
;
for
(
Joint
*
j
:
obj
.
armature
.
roots
)
{
aiNode
*
root_node
=
new
aiNode
();
arm_node
->
mChildren
[
i
]
=
root_node
;
j_to_node
[
j
]
=
root_node
;
joint_tree
(
prefix
,
root_node
,
j
);
i
++
;
}
for
(
auto
[
j
,
n
]
:
j_to_node
)
{
bone_nodes
.
insert
({{
obj
.
id
(),
j
->
_id
},
n
});
}
obj
.
armature
.
for_joints
([
&
](
Joint
*
j
)
{
aiBone
*
bone
=
new
aiBone
();
ai_mesh
->
mBones
[
bone_idx
++
]
=
bone
;
bone
->
mOffsetMatrix
=
matMat
(
Mat4
::
translate
(
j
->
extent
)
*
Mat4
::
euler
(
j
->
pose
));
bone
->
mNode
=
j_to_node
[
j
];
std
::
string
name
=
prefix
+
std
::
to_string
(
j
->
_id
);
bone
->
mName
=
aiString
(
name
);
bone
->
mNumWeights
=
1
;
bone
->
mWeights
=
new
aiVertexWeight
[
1
];
bone
->
mWeights
[
0
].
mWeight
=
j
->
radius
;
});
}
}
else
if
(
entry
.
second
.
is
<
Scene_Light
>
())
{
const
Scene_Light
&
light
=
entry
.
second
.
get
<
Scene_Light
>
();
std
::
string
name
(
light
.
opt
.
name
);
std
::
replace
(
name
.
begin
(),
name
.
end
(),
' '
,
'_'
);
aiLight
*
ai_light
=
new
aiLight
();
aiNode
*
ai_node
=
new
aiNode
();
aiNode
*
ai_node_light
=
new
aiNode
();
scene
.
mLights
[
light_idx
++
]
=
ai_light
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node
;
scene
.
mRootNode
->
mChildren
[
node_idx
++
]
=
ai_node_light
;
switch
(
light
.
opt
.
type
)
{
case
Light_Type
::
directional
:
{
ai_light
->
mType
=
aiLightSource_DIRECTIONAL
;
name
+=
"-S3D-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
sphere
:
{
ai_light
->
mType
=
aiLightSource_AMBIENT
;
name
+=
"-S3D-SPHERE-"
+
std
::
to_string
(
light
.
id
());
if
(
light
.
opt
.
has_emissive_map
)
{
ai_light
->
mEnvMap
=
aiString
(
light
.
emissive_loaded
());
}
}
break
;
case
Light_Type
::
hemisphere
:
{
ai_light
->
mType
=
aiLightSource_AMBIENT
;
name
+=
"-S3D-HEMISPHERE-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
point
:
{
ai_light
->
mType
=
aiLightSource_POINT
;
name
+=
"-S3D-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
spot
:
{
ai_light
->
mType
=
aiLightSource_SPOT
;
name
+=
"-S3D-"
+
std
::
to_string
(
light
.
id
());
}
break
;
case
Light_Type
::
rectangle
:
{
// the collada exporter literally just ignores area lights ????????
ai_light
->
mType
=
aiLightSource_AMBIENT
;
name
+=
"-S3D-AREA-"
+
std
::
to_string
(
light
.
id
());
ai_light
->
mAttenuationConstant
=
light
.
opt
.
size
.
x
;
ai_light
->
mAttenuationLinear
=
light
.
opt
.
size
.
y
;
}
break
;
default:
break
;
}
Spectrum
r
=
light
.
radiance
();
ai_light
->
mName
=
aiString
(
name
);
ai_light
->
mPosition
=
aiVector3D
(
0.0
f
,
0.0
f
,
0.0
f
);
ai_light
->
mDirection
=
aiVector3D
(
0.0
f
,
1.0
f
,
0.0
f
);
ai_light
->
mUp
=
aiVector3D
(
0.0
f
,
1.0
f
,
0.0
f
);
ai_light
->
mSize
=
aiVector2D
(
light
.
opt
.
size
.
x
,
light
.
opt
.
size
.
y
);
ai_light
->
mAngleInnerCone
=
light
.
opt
.
angle_bounds
.
x
;
ai_light
->
mAngleOuterCone
=
light
.
opt
.
angle_bounds
.
y
;
ai_light
->
mColorDiffuse
=
aiColor3D
(
r
.
r
,
r
.
g
,
r
.
b
);
ai_light
->
mColorAmbient
=
aiColor3D
(
r
.
r
,
r
.
g
,
r
.
b
);
ai_light
->
mColorDiffuse
=
aiColor3D
(
r
.
r
,
r
.
g
,
r
.
b
);
ai_light
->
mAttenuationQuadratic
=
light
.
opt
.
intensity
;
Mat4
T
=
light
.
pose
.
transform
();
item_nodes
[
light
.
id
()]
=
ai_node
;
ai_node
->
mName
=
aiString
(
name
);
ai_node
->
mNumMeshes
=
0
;
ai_node
->
mMeshes
=
nullptr
;
ai_node
->
mTransformation
=
matMat
(
T
);
ai_node_light
->
mName
=
aiString
(
name
+
"-LIGHT_ANIM_NODE"
);
ai_node_light
->
mNumMeshes
=
0
;
ai_node_light
->
mMeshes
=
nullptr
;
ai_node_light
->
mTransformation
=
aiMatrix4x4
();
}
}
{
scene
.
mNumAnimations
=
1
;
scene
.
mAnimations
=
new
aiAnimation
*
[
1
];
scene
.
mAnimations
[
0
]
=
new
aiAnimation
();
aiAnimation
&
ai_anim
=
*
scene
.
mAnimations
[
0
];
ai_anim
.
mName
=
aiString
(
"Scotty3D-animate"
);
ai_anim
.
mDuration
=
(
double
)
animation
.
n_frames
();
ai_anim
.
mTicksPerSecond
=
(
double
)
animation
.
fps
();
ai_anim
.
mNumChannels
=
(
unsigned
int
)
n_anims
;
ai_anim
.
mChannels
=
new
aiNodeAnim
*
[
n_anims
];
auto
write_keyframes
=
[](
aiNodeAnim
*
node
,
std
::
string
name
,
auto
pt
,
auto
rot
,
auto
scl
)
{
node
->
mNodeName
=
aiString
(
name
);
node
->
mNumPositionKeys
=
(
unsigned
int
)
pt
.
size
();
node
->
mNumRotationKeys
=
(
unsigned
int
)
rot
.
size
();
node
->
mNumScalingKeys
=
(
unsigned
int
)
scl
.
size
();
node
->
mPositionKeys
=
new
aiVectorKey
[
pt
.
size
()];
node
->
mRotationKeys
=
new
aiQuatKey
[
rot
.
size
()];
node
->
mScalingKeys
=
new
aiVectorKey
[
scl
.
size
()];
size_t
i
=
0
;
for
(
auto
&
e
:
pt
)
{
node
->
mPositionKeys
[
i
]
=
aiVectorKey
(
e
.
first
,
vecVec
(
e
.
second
));
i
++
;
}
i
=
0
;
for
(
auto
&
e
:
rot
)
{
node
->
mRotationKeys
[
i
]
=
aiQuatKey
(
e
.
first
,
aiQuaternion
(
e
.
second
.
w
,
e
.
second
.
x
,
e
.
second
.
y
,
e
.
second
.
z
));
i
++
;
}
i
=
0
;
for
(
auto
&
e
:
scl
)
{
node
->
mScalingKeys
[
i
]
=
aiVectorKey
(
e
.
first
,
vecVec
(
e
.
second
));
i
++
;
}
};
if
(
anim_cam
.
splines
.
any
())
{
ai_anim
.
mChannels
[
camera_anim
]
=
new
aiNodeAnim
();
std
::
set
<
float
>
keys
=
anim_cam
.
splines
.
keys
();
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
for
(
float
k
:
keys
)
{
auto
[
p
,
r
,
fov
,
ar
]
=
anim_cam
.
splines
.
at
(
k
);
points
[
k
]
=
p
;
rotations
[
k
]
=
r
;
scales
[
k
]
=
Vec3
{
fov
,
ar
,
1.0
f
};
}
write_keyframes
(
ai_anim
.
mChannels
[
camera_anim
],
"camera_node"
,
points
,
rotations
,
scales
);
}
size_t
anim_idx
=
0
;
for
(
auto
&
entry
:
objs
)
{
Scene_Item
&
item
=
entry
.
second
;
const
Anim_Pose
&
pose
=
item
.
animation
();
if
(
pose
.
splines
.
any
())
{
std
::
set
<
float
>
keys
=
pose
.
splines
.
keys
();
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
for
(
float
k
:
keys
)
{
auto
[
p
,
r
,
s
]
=
pose
.
splines
.
at
(
k
);
points
[
k
]
=
p
;
rotations
[
k
]
=
r
;
scales
[
k
]
=
s
;
}
aiNode
*
node
=
item_nodes
[
item
.
id
()];
ai_anim
.
mChannels
[
anim_idx
]
=
new
aiNodeAnim
();
write_keyframes
(
ai_anim
.
mChannels
[
anim_idx
],
std
::
string
(
node
->
mName
.
C_Str
()),
points
,
rotations
,
scales
);
anim_idx
++
;
}
if
(
item
.
is
<
Scene_Object
>
())
{
Skeleton
&
skel
=
item
.
get
<
Scene_Object
>
().
armature
;
if
(
skel
.
has_keyframes
())
{
skel
.
for_joints
([
&
](
Joint
*
j
)
{
std
::
set
<
float
>
keys
=
j
->
anim
.
keys
();
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
for
(
float
k
:
keys
)
{
points
[
k
]
=
Vec3
{
0.0
f
};
rotations
[
k
]
=
j
->
anim
.
at
(
k
);
scales
[
k
]
=
Vec3
{
1.0
f
};
}
aiNode
*
node
=
bone_nodes
[{
item
.
id
(),
j
->
_id
}];
ai_anim
.
mChannels
[
anim_idx
]
=
new
aiNodeAnim
();
write_keyframes
(
ai_anim
.
mChannels
[
anim_idx
],
std
::
string
(
node
->
mName
.
C_Str
()),
points
,
rotations
,
scales
);
anim_idx
++
;
});
}
}
else
if
(
item
.
is
<
Scene_Light
>
())
{
aiNode
*
node
=
item_nodes
[
item
.
id
()];
std
::
string
name
(
node
->
mName
.
C_Str
());
name
+=
"-LIGHT_ANIM_NODE"
;
const
Scene_Light
::
Anim_Light
&
light
=
item
.
get
<
Scene_Light
>
().
lanim
;
if
(
light
.
splines
.
any
())
{
std
::
unordered_map
<
float
,
Vec3
>
points
,
scales
;
std
::
unordered_map
<
float
,
Quat
>
rotations
;
std
::
set
<
float
>
keys
=
light
.
splines
.
keys
();
for
(
float
k
:
keys
)
{
auto
[
spec
,
inten
,
angle
,
size
]
=
light
.
splines
.
at
(
k
);
points
[
k
]
=
Vec3
{
spec
.
r
,
spec
.
g
,
spec
.
b
};
rotations
[
k
]
=
Quat
::
euler
(
Vec3
{
angle
.
x
,
0.0
f
,
angle
.
y
});
scales
[
k
]
=
Vec3
{
inten
+
1.0
f
,
size
.
x
+
1.0
f
,
size
.
y
+
1.0
f
};
}
ai_anim
.
mChannels
[
anim_idx
]
=
new
aiNodeAnim
();
write_keyframes
(
ai_anim
.
mChannels
[
anim_idx
],
name
,
points
,
rotations
,
scales
);
anim_idx
++
;
}
}
}
}
// Note: exporter/scene destructor should free everything
Assimp
::
Exporter
exporter
;
if
(
exporter
.
Export
(
&
scene
,
"collada"
,
file
.
c_str
()))
{
return
std
::
string
(
exporter
.
GetErrorString
());
}
return
{};
}
src/scene/scene.h
View file @
c2535f0f
#pragma once
#include <functional>
#include <map>
#include <optional>
#include <functional>
#include "../geometry/halfedge.h"
#include "../lib/mathlib.h"
#include "../util/camera.h"
#include "../platform/gl.h"
#include "../
geometry/halfedge
.h"
#include "../
util/camera
.h"
#include "light.h"
#include "object.h"
class
Undo
;
class
Halfedge_Editor
;
namespace
Gui
{
class
Manager
;
class
Animate
;
}
namespace
Gui
{
class
Manager
;
class
Animate
;
}
// namespace Gui
class
Scene_Item
{
public:
Scene_Item
()
=
default
;
Scene_Item
(
Scene_Object
&&
obj
);
Scene_Item
(
Scene_Light
&&
light
);
Scene_Item
(
Scene_Item
&&
src
);
Scene_Item
(
const
Scene_Item
&
src
)
=
delete
;
Scene_Item
&
operator
=
(
Scene_Item
&&
src
);
Scene_Item
&
operator
=
(
const
Scene_Item
&
src
)
=
delete
;
BBox
bbox
();
void
render
(
const
Mat4
&
view
,
bool
solid
=
false
,
bool
depth_only
=
false
,
bool
posed
=
true
);
Scene_ID
id
()
const
;
Pose
&
pose
();
const
Pose
&
pose
()
const
;
Anim_Pose
&
animation
();
const
Anim_Pose
&
animation
()
const
;
void
set_time
(
float
time
);
std
::
string
name
()
const
;
std
::
pair
<
char
*
,
int
>
name
();
template
<
typename
T
>
bool
is
()
const
{
return
std
::
holds_alternative
<
T
>
(
data
);
}
template
<
typename
T
>
T
&
get
()
{
return
std
::
get
<
T
>
(
data
);
}
template
<
typename
T
>
const
T
&
get
()
const
{
return
std
::
get
<
T
>
(
data
);
}
Scene_Item
()
=
default
;
Scene_Item
(
Scene_Object
&&
obj
);
Scene_Item
(
Scene_Light
&&
light
);
Scene_Item
(
Scene_Item
&&
src
);
Scene_Item
(
const
Scene_Item
&
src
)
=
delete
;
Scene_Item
&
operator
=
(
Scene_Item
&&
src
);
Scene_Item
&
operator
=
(
const
Scene_Item
&
src
)
=
delete
;
BBox
bbox
();
void
render
(
const
Mat4
&
view
,
bool
solid
=
false
,
bool
depth_only
=
false
,
bool
posed
=
true
);
Scene_ID
id
()
const
;
Pose
&
pose
();
const
Pose
&
pose
()
const
;
Anim_Pose
&
animation
();
const
Anim_Pose
&
animation
()
const
;
void
set_time
(
float
time
);
std
::
string
name
()
const
;
std
::
pair
<
char
*
,
int
>
name
();
template
<
typename
T
>
bool
is
()
const
{
return
std
::
holds_alternative
<
T
>
(
data
);
}
template
<
typename
T
>
T
&
get
()
{
return
std
::
get
<
T
>
(
data
);
}
template
<
typename
T
>
const
T
&
get
()
const
{
return
std
::
get
<
T
>
(
data
);
}
private:
std
::
variant
<
Scene_Object
,
Scene_Light
>
data
;
std
::
variant
<
Scene_Object
,
Scene_Light
>
data
;
};
using
Scene_Maybe
=
std
::
optional
<
std
::
reference_wrapper
<
Scene_Item
>>
;
...
...
@@ -66,34 +60,34 @@ public:
Scene
(
Scene_ID
start
);
~
Scene
()
=
default
;
std
::
string
write
(
std
::
string
file
,
const
Camera
&
cam
,
const
Gui
::
Animate
&
animation
);
std
::
string
load
(
bool
new_scene
,
Undo
&
undo
,
Gui
::
Manager
&
gui
,
std
::
string
file
);
void
clear
(
Undo
&
undo
);
std
::
string
write
(
std
::
string
file
,
const
Camera
&
cam
,
const
Gui
::
Animate
&
animation
);
std
::
string
load
(
bool
new_scene
,
Undo
&
undo
,
Gui
::
Manager
&
gui
,
std
::
string
file
);
void
clear
(
Undo
&
undo
);
bool
empty
();
size_t
size
();
Scene_ID
add
(
Scene_Object
&&
obj
);
Scene_ID
add
(
Scene_Light
&&
obj
);
Scene_ID
add
(
Pose
pose
,
GL
::
Mesh
&&
mesh
,
std
::
string
n
=
{},
Scene_ID
id
=
0
);
Scene_ID
add
(
Pose
pose
,
Halfedge_Mesh
&&
mesh
,
std
::
string
n
=
{},
Scene_ID
id
=
0
);
Scene_ID
reserve_id
();
Scene_ID
used_ids
();
void
erase
(
Scene_ID
id
);
void
restore
(
Scene_ID
id
);
void
for_items
(
std
::
function
<
void
(
Scene_Item
&
)
>
func
);
void
for_items
(
std
::
function
<
void
(
const
Scene_Item
&
)
>
func
)
const
;
Scene_ID
add
(
Scene_Object
&&
obj
);
Scene_ID
add
(
Scene_Light
&&
obj
);
Scene_ID
add
(
Pose
pose
,
GL
::
Mesh
&&
mesh
,
std
::
string
n
=
{},
Scene_ID
id
=
0
);
Scene_ID
add
(
Pose
pose
,
Halfedge_Mesh
&&
mesh
,
std
::
string
n
=
{},
Scene_ID
id
=
0
);
Scene_ID
reserve_id
();
Scene_ID
used_ids
();
void
erase
(
Scene_ID
id
);
void
restore
(
Scene_ID
id
);
void
for_items
(
std
::
function
<
void
(
Scene_Item
&
)
>
func
);
void
for_items
(
std
::
function
<
void
(
const
Scene_Item
&
)
>
func
)
const
;
Scene_Maybe
get
(
Scene_ID
id
);
Scene_Object
&
get_obj
(
Scene_ID
id
);
Scene_Light
&
get_light
(
Scene_ID
id
);
std
::
string
set_env_map
(
std
::
string
file
);
Scene_Object
&
get_obj
(
Scene_ID
id
);
Scene_Light
&
get_light
(
Scene_ID
id
);
std
::
string
set_env_map
(
std
::
string
file
);
bool
has_env_light
()
const
;
bool
has_env_light
()
const
;
private:
std
::
map
<
Scene_ID
,
Scene_Item
>
objs
;
std
::
map
<
Scene_ID
,
Scene_Item
>
erased
;
Scene_ID
next_id
,
first_id
;
std
::
map
<
Scene_ID
,
Scene_Item
>
objs
;
std
::
map
<
Scene_ID
,
Scene_Item
>
erased
;
Scene_ID
next_id
,
first_id
;
};
src/scene/skeleton.cpp
View file @
c2535f0f
#include "skeleton.h"
#include "renderer.h"
#include "../gui/manager.h"
#include "renderer.h"
bool
Joint
::
is_root
()
const
{
return
!
parent
;
}
bool
Joint
::
is_root
()
const
{
return
!
parent
;
}
void
Joint
::
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
)
{
void
Joint
::
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
)
{
func
(
this
);
for
(
Joint
*
j
:
children
)
j
->
for_joints
(
func
);
for
(
Joint
*
j
:
children
)
j
->
for_joints
(
func
);
}
Skeleton
::
Skeleton
()
{
Skeleton
::
Skeleton
()
{
root_id
=
Gui
::
n_Widget_IDs
;
next_id
=
Gui
::
n_Widget_IDs
+
1
;
}
Skeleton
::
Skeleton
(
unsigned
int
obj_id
)
{
Skeleton
::
Skeleton
(
unsigned
int
obj_id
)
{
root_id
=
obj_id
+
1
;
next_id
=
root_id
+
1
;
}
Skeleton
::~
Skeleton
()
{
for
(
Joint
*
j
:
roots
)
delete
j
;
for
(
Joint
*
j
:
erased
)
delete
j
;
Skeleton
::~
Skeleton
()
{
for
(
Joint
*
j
:
roots
)
delete
j
;
for
(
Joint
*
j
:
erased
)
delete
j
;
}
bool
Skeleton
::
set_time
(
float
time
)
{
bool
ret
=
false
;
for_joints
([
&
ret
,
time
](
Joint
*
j
)
{
if
(
j
->
anim
.
any
())
{
for_joints
([
&
ret
,
time
](
Joint
*
j
)
{
if
(
j
->
anim
.
any
())
{
j
->
pose
=
j
->
anim
.
at
(
time
).
to_euler
();
ret
=
true
;
}
...
...
@@ -38,13 +39,14 @@ bool Skeleton::set_time(float time) {
return
ret
;
}
void
Skeleton
::
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
)
{
for
(
Joint
*
r
:
roots
)
r
->
for_joints
(
func
);
void
Skeleton
::
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
)
{
for
(
Joint
*
r
:
roots
)
r
->
for_joints
(
func
);
}
Joint
*
Skeleton
::
add_root
(
Vec3
extent
)
{
Joint
*
j
=
new
Joint
(
next_id
++
,
nullptr
,
extent
);
for
(
float
f
:
keys
())
{
Joint
*
Skeleton
::
add_root
(
Vec3
extent
)
{
Joint
*
j
=
new
Joint
(
next_id
++
,
nullptr
,
extent
);
for
(
float
f
:
keys
())
{
j
->
anim
.
set
(
f
,
Quat
{});
}
roots
.
insert
(
j
);
...
...
@@ -52,40 +54,39 @@ Joint* Skeleton::add_root(Vec3 extent) {
}
void
Skeleton
::
crop
(
float
t
)
{
for_joints
([
t
](
Joint
*
j
)
{
j
->
anim
.
crop
(
t
);
});
for_joints
([
t
](
Joint
*
j
)
{
j
->
anim
.
crop
(
t
);
});
}
Joint
*
Skeleton
::
get_joint
(
unsigned
int
id
)
{
Joint
*
j
=
nullptr
;
for_joints
([
&
](
Joint
*
jt
)
{
if
(
jt
->
_id
==
id
)
j
=
jt
;
Joint
*
Skeleton
::
get_joint
(
unsigned
int
id
)
{
Joint
*
j
=
nullptr
;
for_joints
([
&
](
Joint
*
jt
)
{
if
(
jt
->
_id
==
id
)
j
=
jt
;
});
return
j
;
}
void
Skeleton
::
render
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
unsigned
int
offset
)
{
void
Skeleton
::
render
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
unsigned
int
offset
)
{
Renderer
&
R
=
Renderer
::
get
();
Renderer
&
R
=
Renderer
::
get
();
Mat4
V
=
view
*
Mat4
::
translate
(
base_pos
);
for_joints
([
&
](
Joint
*
j
)
{
for_joints
([
&
](
Joint
*
j
)
{
Renderer
::
MeshOpt
opt
;
opt
.
modelview
=
V
*
(
posed
?
j
->
joint_to_posed
()
:
j
->
joint_to_bind
())
*
Mat4
::
rotate_to
(
j
->
extent
);
opt
.
modelview
=
V
*
(
posed
?
j
->
joint_to_posed
()
:
j
->
joint_to_bind
())
*
Mat4
::
rotate_to
(
j
->
extent
);
opt
.
id
=
j
->
_id
+
offset
;
opt
.
alpha
=
0.8
f
;
opt
.
color
=
Gui
::
Color
::
hover
;
R
.
capsule
(
opt
,
j
->
extent
.
norm
(),
j
->
radius
);
});
if
(
select
)
{
if
(
select
)
{
R
.
begin_outline
();
Mat4
model
=
Mat4
::
translate
(
base_pos
)
*
(
posed
?
select
->
joint_to_posed
()
:
select
->
joint_to_bind
())
*
Mat4
::
rotate_to
(
select
->
extent
);
Mat4
model
=
Mat4
::
translate
(
base_pos
)
*
(
posed
?
select
->
joint_to_posed
()
:
select
->
joint_to_bind
())
*
Mat4
::
rotate_to
(
select
->
extent
);
Renderer
::
MeshOpt
opt
;
opt
.
modelview
=
view
;
...
...
@@ -104,24 +105,25 @@ void Skeleton::render(const Mat4& view, Joint* select, bool root, bool posed, un
opt
.
color
=
root
?
Gui
::
Color
::
outline
:
Gui
::
Color
::
hover
;
R
.
sphere
(
opt
);
for_joints
([
&
](
Joint
*
j
)
{
for_joints
([
&
](
Joint
*
j
)
{
Renderer
::
MeshOpt
opt
;
opt
.
modelview
=
V
*
(
posed
?
j
->
joint_to_posed
()
:
j
->
joint_to_bind
())
*
Mat4
::
translate
(
j
->
extent
)
*
Mat4
::
scale
(
Vec3
{
j
->
radius
*
0.25
f
});
opt
.
modelview
=
V
*
(
posed
?
j
->
joint_to_posed
()
:
j
->
joint_to_bind
())
*
Mat4
::
translate
(
j
->
extent
)
*
Mat4
::
scale
(
Vec3
{
j
->
radius
*
0.25
f
});
opt
.
id
=
j
->
_id
+
offset
;
opt
.
color
=
select
==
j
?
Gui
::
Color
::
outline
:
Gui
::
Color
::
hover
;
R
.
sphere
(
opt
);
});
}
void
Skeleton
::
outline
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
BBox
&
box
,
unsigned
int
offset
)
{
Renderer
&
R
=
Renderer
::
get
();
void
Skeleton
::
outline
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
BBox
&
box
,
unsigned
int
offset
)
{
Renderer
&
R
=
Renderer
::
get
();
Mat4
base_t
=
Mat4
::
translate
(
base_pos
);
for_joints
([
&
](
Joint
*
j
)
{
Mat4
model
=
base_t
*
(
posed
?
j
->
joint_to_posed
()
:
j
->
joint_to_bind
())
*
Mat4
::
rotate_to
(
j
->
extent
);
for_joints
([
&
](
Joint
*
j
)
{
Mat4
model
=
base_t
*
(
posed
?
j
->
joint_to_posed
()
:
j
->
joint_to_bind
())
*
Mat4
::
rotate_to
(
j
->
extent
);
Renderer
::
MeshOpt
opt
;
opt
.
modelview
=
view
;
opt
.
id
=
j
->
_id
+
offset
;
...
...
@@ -131,39 +133,31 @@ void Skeleton::outline(const Mat4& view, Joint* select, bool root, bool posed, B
});
}
bool
Skeleton
::
is_root_id
(
unsigned
int
id
)
{
return
id
==
root_id
;
}
bool
Skeleton
::
is_root_id
(
unsigned
int
id
)
{
return
id
==
root_id
;
}
Joint
*
Skeleton
::
parent
(
Joint
*
j
)
{
return
j
->
parent
;
}
Joint
*
Skeleton
::
parent
(
Joint
*
j
)
{
return
j
->
parent
;
}
bool
Skeleton
::
has_bones
()
const
{
return
!
roots
.
empty
();
}
bool
Skeleton
::
has_bones
()
const
{
return
!
roots
.
empty
();
}
unsigned
int
Skeleton
::
n_bones
()
{
unsigned
int
n
=
0
;
for_joints
([
&
n
](
Joint
*
)
{
n
++
;
});
for_joints
([
&
n
](
Joint
*
)
{
n
++
;
});
return
n
;
}
Vec3
&
Skeleton
::
base
()
{
return
base_pos
;
}
Vec3
&
Skeleton
::
base
()
{
return
base_pos
;
}
Joint
*
Skeleton
::
add_child
(
Joint
*
j
,
Vec3
e
)
{
Joint
*
c
=
new
Joint
(
next_id
++
,
j
,
e
);
for
(
float
f
:
keys
())
{
Joint
*
Skeleton
::
add_child
(
Joint
*
j
,
Vec3
e
)
{
Joint
*
c
=
new
Joint
(
next_id
++
,
j
,
e
);
for
(
float
f
:
keys
())
{
c
->
anim
.
set
(
f
,
Quat
{});
}
j
->
children
.
insert
(
c
);
return
c
;
}
void
Skeleton
::
restore
(
Joint
*
j
)
{
if
(
j
->
parent
)
{
void
Skeleton
::
restore
(
Joint
*
j
)
{
if
(
j
->
parent
)
{
j
->
parent
->
children
.
insert
(
j
);
}
else
{
roots
.
insert
(
j
);
...
...
@@ -171,8 +165,8 @@ void Skeleton::restore(Joint* j) {
erased
.
erase
(
j
);
}
void
Skeleton
::
erase
(
Joint
*
j
)
{
if
(
j
->
parent
)
{
void
Skeleton
::
erase
(
Joint
*
j
)
{
if
(
j
->
parent
)
{
j
->
parent
->
children
.
erase
(
j
);
}
else
{
roots
.
erase
(
j
);
...
...
@@ -180,45 +174,33 @@ void Skeleton::erase(Joint* j) {
erased
.
insert
(
j
);
}
Vec3
Skeleton
::
posed_base_of
(
Joint
*
j
)
{
return
j
->
is_root
()
?
base
()
:
posed_end_of
(
parent
(
j
));
}
Vec3
Skeleton
::
posed_base_of
(
Joint
*
j
)
{
return
j
->
is_root
()
?
base
()
:
posed_end_of
(
parent
(
j
));
}
Vec3
Skeleton
::
base_of
(
Joint
*
j
)
{
return
j
->
is_root
()
?
base
()
:
end_of
(
parent
(
j
));
}
Vec3
Skeleton
::
base_of
(
Joint
*
j
)
{
return
j
->
is_root
()
?
base
()
:
end_of
(
parent
(
j
));
}
void
Skeleton
::
set
(
float
t
)
{
for_joints
([
t
](
Joint
*
j
)
{
j
->
anim
.
set
(
t
,
Quat
::
euler
(
j
->
pose
));
});
for_joints
([
t
](
Joint
*
j
)
{
j
->
anim
.
set
(
t
,
Quat
::
euler
(
j
->
pose
));
});
}
void
Skeleton
::
erase
(
float
t
)
{
for_joints
([
t
](
Joint
*
j
)
{
j
->
anim
.
erase
(
t
);
});
for_joints
([
t
](
Joint
*
j
)
{
j
->
anim
.
erase
(
t
);
});
}
bool
Skeleton
::
has_keyframes
()
{
bool
frame
=
false
;
for_joints
([
&
frame
](
Joint
*
j
)
{
frame
=
frame
||
j
->
anim
.
any
();
});
for_joints
([
&
frame
](
Joint
*
j
)
{
frame
=
frame
||
j
->
anim
.
any
();
});
return
frame
;
}
std
::
unordered_map
<
unsigned
int
,
Vec3
>
Skeleton
::
now
()
{
std
::
unordered_map
<
unsigned
int
,
Vec3
>
ret
;
for_joints
([
&
ret
](
Joint
*
j
)
{
ret
[
j
->
_id
]
=
j
->
pose
;
});
for_joints
([
&
ret
](
Joint
*
j
)
{
ret
[
j
->
_id
]
=
j
->
pose
;
});
return
ret
;
}
std
::
set
<
float
>
Skeleton
::
keys
()
{
std
::
set
<
float
>
ret
;
for_joints
([
&
ret
](
Joint
*
j
)
{
for_joints
([
&
ret
](
Joint
*
j
)
{
std
::
set
<
float
>
k
=
j
->
anim
.
keys
();
ret
.
insert
(
k
.
begin
(),
k
.
end
());
});
...
...
@@ -227,14 +209,10 @@ std::set<float> Skeleton::keys() {
std
::
unordered_map
<
unsigned
int
,
Vec3
>
Skeleton
::
at
(
float
t
)
{
std
::
unordered_map
<
unsigned
int
,
Vec3
>
ret
;
for_joints
([
&
ret
,
t
](
Joint
*
j
)
{
ret
[
j
->
_id
]
=
j
->
anim
.
at
(
t
).
to_euler
();
});
for_joints
([
&
ret
,
t
](
Joint
*
j
)
{
ret
[
j
->
_id
]
=
j
->
anim
.
at
(
t
).
to_euler
();
});
return
ret
;
}
void
Skeleton
::
set
(
float
t
,
const
std
::
unordered_map
<
unsigned
int
,
Vec3
>&
data
)
{
for_joints
([
&
data
,
t
](
Joint
*
j
)
{
j
->
anim
.
set
(
t
,
Quat
::
euler
(
data
.
at
(
j
->
_id
)));
});
void
Skeleton
::
set
(
float
t
,
const
std
::
unordered_map
<
unsigned
int
,
Vec3
>
&
data
)
{
for_joints
([
&
data
,
t
](
Joint
*
j
)
{
j
->
anim
.
set
(
t
,
Quat
::
euler
(
data
.
at
(
j
->
_id
)));
});
}
src/scene/skeleton.h
View file @
c2535f0f
#pragma once
#include <functional>
#include <set>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <
functional
>
#include <
vector
>
#include "../lib/mathlib.h"
#include "../geometry/spline.h"
#include "../lib/mathlib.h"
#include "../platform/gl.h"
class
Joint
{
public:
Joint
(
unsigned
int
id
)
:
_id
(
id
)
{}
Joint
(
unsigned
int
id
,
Joint
*
parent
,
Vec3
extent
)
:
_id
(
id
),
parent
(
parent
),
extent
(
extent
)
{}
~
Joint
()
{
for
(
Joint
*
j
:
children
)
delete
j
;
}
Joint
(
unsigned
int
id
,
Joint
*
parent
,
Vec3
extent
)
:
_id
(
id
),
parent
(
parent
),
extent
(
extent
)
{}
~
Joint
()
{
for
(
Joint
*
j
:
children
)
delete
j
;
}
Joint
(
const
Joint
&
src
)
=
delete
;
Joint
(
Joint
&&
src
)
=
default
;
Joint
(
const
Joint
&
src
)
=
delete
;
Joint
(
Joint
&&
src
)
=
default
;
void
operator
=
(
const
Joint
&
src
)
=
delete
;
Joint
&
operator
=
(
Joint
&&
src
)
=
default
;
void
operator
=
(
const
Joint
&
src
)
=
delete
;
Joint
&
operator
=
(
Joint
&&
src
)
=
default
;
unsigned
int
id
()
const
{
return
_id
;
}
// Checks if this joint is a root node
bool
is_root
()
const
;
// Euler angles representing the current joint rotation
Vec3
pose
;
// The vector representing the direction and length of the bone.
// This is specified in Joint space, and defines the origin of child bones.
// This is specified in Joint space, and defines the origin of child bones.
Vec3
extent
=
Vec3
(
0.0
f
,
1.0
f
,
0.0
f
);
// The distance at which the joint segment should stop effecting vertices
float
radius
=
0.25
f
;
...
...
@@ -51,12 +54,12 @@ private:
Mat4
joint_to_posed
()
const
;
// Pointer to parent joint in the joint heirarchy
Joint
*
parent
=
nullptr
;
Joint
*
parent
=
nullptr
;
// Set of child joints - owned by this joint (could be shared_ptr and everything else weak_ptr)
std
::
unordered_set
<
Joint
*>
children
;
std
::
unordered_set
<
Joint
*>
children
;
void
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
);
void
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
);
unsigned
int
_id
=
0
;
Spline
<
Quat
>
anim
;
...
...
@@ -71,39 +74,42 @@ public:
Skeleton
(
unsigned
int
obj_id
);
~
Skeleton
();
Skeleton
(
const
Skeleton
&
src
)
=
delete
;
Skeleton
(
Skeleton
&&
src
)
=
default
;
Skeleton
(
const
Skeleton
&
src
)
=
delete
;
Skeleton
(
Skeleton
&&
src
)
=
default
;
void
operator
=
(
const
Skeleton
&
src
)
=
delete
;
Skeleton
&
operator
=
(
Skeleton
&&
src
)
=
default
;
void
operator
=
(
const
Skeleton
&
src
)
=
delete
;
Skeleton
&
operator
=
(
Skeleton
&&
src
)
=
default
;
Vec3
&
base
();
Vec3
&
base
();
bool
has_bones
()
const
;
unsigned
int
n_bones
();
Joint
*
parent
(
Joint
*
j
);
Joint
*
get_joint
(
unsigned
int
id
);
void
erase
(
Joint
*
j
);
void
restore
(
Joint
*
j
);
Vec3
end_of
(
Joint
*
j
);
Vec3
base_of
(
Joint
*
j
);
Vec3
posed_end_of
(
Joint
*
j
);
Vec3
posed_base_of
(
Joint
*
j
);
void
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
);
Mat4
joint_to_bind
(
const
Joint
*
j
)
const
;
Mat4
joint_to_posed
(
const
Joint
*
j
)
const
;
Joint
*
add_root
(
Vec3
extent
);
Joint
*
add_child
(
Joint
*
j
,
Vec3
extent
);
Joint
*
parent
(
Joint
*
j
);
Joint
*
get_joint
(
unsigned
int
id
);
void
erase
(
Joint
*
j
);
void
restore
(
Joint
*
j
);
Vec3
end_of
(
Joint
*
j
);
Vec3
base_of
(
Joint
*
j
);
Vec3
posed_end_of
(
Joint
*
j
);
Vec3
posed_base_of
(
Joint
*
j
);
void
for_joints
(
std
::
function
<
void
(
Joint
*
)
>
func
);
Mat4
joint_to_bind
(
const
Joint
*
j
)
const
;
Mat4
joint_to_posed
(
const
Joint
*
j
)
const
;
Joint
*
add_root
(
Vec3
extent
);
Joint
*
add_child
(
Joint
*
j
,
Vec3
extent
);
bool
is_root_id
(
unsigned
int
id
);
bool
set_time
(
float
time
);
void
render
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
unsigned
int
offset
=
0
);
void
outline
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
BBox
&
box
,
unsigned
int
offset
=
0
);
void
find_joints
(
const
GL
::
Mesh
&
src
,
std
::
unordered_map
<
unsigned
int
,
std
::
vector
<
Joint
*>>&
map
);
void
skin
(
const
GL
::
Mesh
&
input
,
GL
::
Mesh
&
output
,
const
std
::
unordered_map
<
unsigned
int
,
std
::
vector
<
Joint
*>>&
map
);
void
render
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
unsigned
int
offset
=
0
);
void
outline
(
const
Mat4
&
view
,
Joint
*
select
,
bool
root
,
bool
posed
,
BBox
&
box
,
unsigned
int
offset
=
0
);
void
find_joints
(
const
GL
::
Mesh
&
src
,
std
::
unordered_map
<
unsigned
int
,
std
::
vector
<
Joint
*>>
&
map
);
void
skin
(
const
GL
::
Mesh
&
input
,
GL
::
Mesh
&
output
,
const
std
::
unordered_map
<
unsigned
int
,
std
::
vector
<
Joint
*>>
&
map
);
void
set
(
float
t
);
void
crop
(
float
t
);
...
...
@@ -112,11 +118,11 @@ public:
std
::
set
<
float
>
keys
();
std
::
unordered_map
<
unsigned
int
,
Vec3
>
now
();
std
::
unordered_map
<
unsigned
int
,
Vec3
>
at
(
float
t
);
void
set
(
float
t
,
const
std
::
unordered_map
<
unsigned
int
,
Vec3
>
&
data
);
void
set
(
float
t
,
const
std
::
unordered_map
<
unsigned
int
,
Vec3
>
&
data
);
private:
Vec3
base_pos
;
unsigned
int
root_id
,
next_id
;
std
::
unordered_set
<
Joint
*>
roots
,
erased
;
std
::
unordered_set
<
Joint
*>
roots
,
erased
;
friend
class
Scene
;
};
src/scene/undo.cpp
View file @
c2535f0f
#include "undo.h"
#include "../gui/manager.h"
#include "../gui/animate.h"
#include "../gui/manager.h"
#include "../gui/rig.h"
Undo
::
Undo
(
Scene
&
sc
,
Gui
::
Manager
&
man
)
:
scene
(
sc
),
gui
(
man
)
{}
Undo
::
Undo
(
Scene
&
sc
,
Gui
::
Manager
&
man
)
:
scene
(
sc
),
gui
(
man
)
{}
void
Undo
::
reset
()
{
undos
=
{};
redos
=
{};
}
template
<
typename
R
,
typename
U
>
class
Action
:
public
Action_Base
{
template
<
typename
R
,
typename
U
>
class
Action
:
public
Action_Base
{
public:
Action
(
R
&&
r
,
U
&&
u
)
:
_undo
(
std
::
forward
<
decltype
(
u
)
>
(
u
)),
_redo
(
std
::
forward
<
decltype
(
r
)
>
(
r
))
{};
Action
(
R
&&
r
,
U
&&
u
)
:
_undo
(
std
::
forward
<
decltype
(
u
)
>
(
u
)),
_redo
(
std
::
forward
<
decltype
(
r
)
>
(
r
)){};
~
Action
()
{}
private:
U
_undo
;
R
_redo
;
void
undo
()
{
_undo
();}
void
redo
()
{
_redo
();}
void
undo
()
{
_undo
();
}
void
redo
()
{
_redo
();
}
};
template
<
typename
R
,
typename
U
>
void
Undo
::
action
(
R
&&
redo
,
U
&&
undo
)
{
action
(
std
::
make_unique
<
Action
<
R
,
U
>>
(
std
::
move
(
redo
),
std
::
move
(
undo
)));
template
<
typename
R
,
typename
U
>
void
Undo
::
action
(
R
&&
redo
,
U
&&
undo
)
{
action
(
std
::
make_unique
<
Action
<
R
,
U
>>
(
std
::
move
(
redo
),
std
::
move
(
undo
)));
}
void
Undo
::
update_mesh_full
(
Scene_ID
id
,
Halfedge_Mesh
&&
old_mesh
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
void
Undo
::
update_mesh_full
(
Scene_ID
id
,
Halfedge_Mesh
&&
old_mesh
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Halfedge_Mesh
new_mesh
;
obj
.
copy_mesh
(
new_mesh
);
action
([
id
,
this
,
nm
=
std
::
move
(
new_mesh
)]()
mutable
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
set_mesh
(
nm
);
},
[
id
,
this
,
om
=
std
::
move
(
old_mesh
)]()
mutable
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
set_mesh
(
om
);
});
action
(
[
id
,
this
,
nm
=
std
::
move
(
new_mesh
)]()
mutable
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
set_mesh
(
nm
);
},
[
id
,
this
,
om
=
std
::
move
(
old_mesh
)]()
mutable
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
set_mesh
(
om
);
});
}
void
Undo
::
move_root
(
Scene_ID
id
,
Vec3
old
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Vec3
np
=
obj
.
armature
.
base
();
action
([
this
,
id
,
np
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
base
()
=
np
;
obj
.
set_skel_dirty
();
},
[
this
,
id
,
op
=
old
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
base
()
=
op
;
obj
.
set_skel_dirty
();
});
action
(
[
this
,
id
,
np
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
base
()
=
np
;
obj
.
set_skel_dirty
();
},
[
this
,
id
,
op
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
base
()
=
op
;
obj
.
set_skel_dirty
();
});
}
void
Undo
::
del_bone
(
Scene_ID
id
,
Joint
*
j
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
void
Undo
::
del_bone
(
Scene_ID
id
,
Joint
*
j
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
erase
(
j
);
obj
.
set_skel_dirty
();
action
([
this
,
id
,
j
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
erase
(
j
);
gui
.
get_rig
().
invalidate
(
j
);
obj
.
set_skel_dirty
();
},
[
this
,
id
,
j
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
restore
(
j
);
obj
.
set_skel_dirty
();
});
action
(
[
this
,
id
,
j
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
erase
(
j
);
gui
.
get_rig
().
invalidate
(
j
);
obj
.
set_skel_dirty
();
},
[
this
,
id
,
j
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
restore
(
j
);
obj
.
set_skel_dirty
();
});
}
void
Undo
::
move_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
)
{
action
([
this
,
id
,
j
,
ne
=
j
->
extent
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
extent
=
ne
;
obj
.
set_skel_dirty
();
},
[
this
,
id
,
j
,
oe
=
old
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
extent
=
oe
;
obj
.
set_skel_dirty
();
});
void
Undo
::
move_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
)
{
action
(
[
this
,
id
,
j
,
ne
=
j
->
extent
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
extent
=
ne
;
obj
.
set_skel_dirty
();
},
[
this
,
id
,
j
,
oe
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
extent
=
oe
;
obj
.
set_skel_dirty
();
});
}
void
Undo
::
pose_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
)
{
action
([
this
,
id
,
j
,
ne
=
j
->
pose
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
pose
=
ne
;
obj
.
set_pose_dirty
();
},
[
this
,
id
,
j
,
oe
=
old
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
pose
=
oe
;
obj
.
set_pose_dirty
();
});
void
Undo
::
pose_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
)
{
action
(
[
this
,
id
,
j
,
ne
=
j
->
pose
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
pose
=
ne
;
obj
.
set_pose_dirty
();
},
[
this
,
id
,
j
,
oe
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
j
->
pose
=
oe
;
obj
.
set_pose_dirty
();
});
}
void
Undo
::
add_bone
(
Scene_ID
id
,
Joint
*
j
)
{
action
([
this
,
id
,
j
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
restore
(
j
);
obj
.
set_skel_dirty
();
},
[
this
,
id
,
j
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
erase
(
j
);
gui
.
get_rig
().
invalidate
(
j
);
obj
.
set_skel_dirty
();
});
void
Undo
::
add_bone
(
Scene_ID
id
,
Joint
*
j
)
{
action
(
[
this
,
id
,
j
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
restore
(
j
);
obj
.
set_skel_dirty
();
},
[
this
,
id
,
j
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
armature
.
erase
(
j
);
gui
.
get_rig
().
invalidate
(
j
);
obj
.
set_skel_dirty
();
});
}
void
Undo
::
del_obj
(
Scene_ID
id
)
{
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
action
(
[
id
,
this
](){
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
},
[
id
,
this
](){
scene
.
restore
(
id
);
});
action
(
[
id
,
this
]()
{
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
},
[
id
,
this
]()
{
scene
.
restore
(
id
);
});
}
Scene_Object
&
Undo
::
add_obj
(
GL
::
Mesh
&&
mesh
,
std
::
string
name
)
{
Scene_Object
&
Undo
::
add_obj
(
GL
::
Mesh
&&
mesh
,
std
::
string
name
)
{
Scene_ID
id
=
scene
.
add
({},
std
::
move
(
mesh
),
name
);
scene
.
restore
(
id
);
action
([
id
,
this
](){
scene
.
restore
(
id
);
},
[
id
,
this
](){
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
});
action
([
id
,
this
]()
{
scene
.
restore
(
id
);
},
[
id
,
this
]()
{
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
});
return
scene
.
get_obj
(
id
);
}
Scene_Object
&
Undo
::
add_obj
(
Halfedge_Mesh
&&
mesh
,
std
::
string
name
)
{
Scene_Object
&
Undo
::
add_obj
(
Halfedge_Mesh
&&
mesh
,
std
::
string
name
)
{
Scene_ID
id
=
scene
.
add
({},
std
::
move
(
mesh
),
name
);
scene
.
restore
(
id
);
action
([
id
,
this
](){
scene
.
restore
(
id
);
},
[
id
,
this
](){
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
});
action
([
id
,
this
]()
{
scene
.
restore
(
id
);
},
[
id
,
this
]()
{
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
});
return
scene
.
get_obj
(
id
);
}
void
Undo
::
add_light
(
Scene_Light
&&
light
)
{
void
Undo
::
add_light
(
Scene_Light
&&
light
)
{
Scene_ID
id
=
scene
.
add
(
std
::
move
(
light
));
scene
.
restore
(
id
);
action
([
id
,
this
](){
scene
.
restore
(
id
);
},
[
id
,
this
](){
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
});
action
([
id
,
this
]()
{
scene
.
restore
(
id
);
},
[
id
,
this
]()
{
scene
.
erase
(
id
);
gui
.
invalidate_obj
(
id
);
});
}
void
Undo
::
update_light
(
Scene_ID
id
,
Scene_Light
::
Options
old
)
{
Scene_Light
&
light
=
scene
.
get_light
(
id
);
action
([
id
,
this
,
no
=
light
.
opt
](){
Scene_Light
&
light
=
scene
.
get_light
(
id
);
light
.
opt
=
no
;
light
.
dirty
();
},
[
id
,
this
,
oo
=
old
](){
Scene_Light
&
light
=
scene
.
get_light
(
id
);
light
.
opt
=
oo
;
light
.
dirty
();
});
Scene_Light
&
light
=
scene
.
get_light
(
id
);
action
(
[
id
,
this
,
no
=
light
.
opt
]()
{
Scene_Light
&
light
=
scene
.
get_light
(
id
);
light
.
opt
=
no
;
light
.
dirty
();
},
[
id
,
this
,
oo
=
old
]()
{
Scene_Light
&
light
=
scene
.
get_light
(
id
);
light
.
opt
=
oo
;
light
.
dirty
();
});
}
void
Undo
::
update_material
(
Scene_ID
id
,
Material
::
Options
old
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
action
([
id
,
this
,
nm
=
obj
.
material
.
opt
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
material
.
opt
=
nm
;
},
[
id
,
this
,
om
=
old
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
material
.
opt
=
om
;
});
action
(
[
id
,
this
,
nm
=
obj
.
material
.
opt
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
material
.
opt
=
nm
;
},
[
id
,
this
,
om
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
material
.
opt
=
om
;
});
}
void
Undo
::
update_object
(
Scene_ID
id
,
Scene_Object
::
Options
old
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
if
(
obj
.
opt
.
shape_type
!=
PT
::
Shape_Type
::
none
&&
old
.
shape_type
==
PT
::
Shape_Type
::
none
)
{
if
(
obj
.
opt
.
shape_type
!=
PT
::
Shape_Type
::
none
&&
old
.
shape_type
==
PT
::
Shape_Type
::
none
)
{
Halfedge_Mesh
old_mesh
;
obj
.
copy_mesh
(
old_mesh
);
action
([
id
,
this
,
no
=
obj
.
opt
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
no
;
obj
.
set_mesh_dirty
();
},
[
id
,
this
,
oo
=
old
,
om
=
std
::
move
(
old_mesh
)]()
mutable
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
oo
;
obj
.
set_mesh
(
om
);
});
action
(
[
id
,
this
,
no
=
obj
.
opt
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
no
;
obj
.
set_mesh_dirty
();
},
[
id
,
this
,
oo
=
old
,
om
=
std
::
move
(
old_mesh
)]()
mutable
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
oo
;
obj
.
set_mesh
(
om
);
});
return
;
}
if
(
obj
.
opt
.
shape_type
==
PT
::
Shape_Type
::
none
&&
old
.
shape_type
!=
PT
::
Shape_Type
::
none
)
{
if
(
obj
.
opt
.
shape_type
==
PT
::
Shape_Type
::
none
&&
old
.
shape_type
!=
PT
::
Shape_Type
::
none
)
{
action
([
id
,
this
,
no
=
obj
.
opt
,
ot
=
old
.
shape_type
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
no
;
obj
.
try_make_editable
(
ot
);
},
[
id
,
this
,
oo
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
oo
;
obj
.
set_mesh_dirty
();
});
action
(
[
id
,
this
,
no
=
obj
.
opt
,
ot
=
old
.
shape_type
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
no
;
obj
.
try_make_editable
(
ot
);
},
[
id
,
this
,
oo
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
oo
;
obj
.
set_mesh_dirty
();
});
return
;
}
action
([
id
,
this
,
no
=
obj
.
opt
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
no
;
obj
.
set_mesh_dirty
();
},
[
id
,
this
,
oo
=
old
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
oo
;
obj
.
set_mesh_dirty
();
});
action
(
[
id
,
this
,
no
=
obj
.
opt
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
no
;
obj
.
set_mesh_dirty
();
},
[
id
,
this
,
oo
=
old
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
opt
=
oo
;
obj
.
set_mesh_dirty
();
});
}
void
Undo
::
update_pose
(
Scene_ID
id
,
Pose
old
)
{
Scene_Item
&
item
=
*
scene
.
get
(
id
);
action
([
id
,
this
,
np
=
item
.
pose
()](){
Scene_Item
&
item
=
*
scene
.
get
(
id
);
item
.
pose
()
=
np
;
},
[
id
,
this
,
op
=
old
](){
Scene_Item
&
item
=
*
scene
.
get
(
id
);
item
.
pose
()
=
op
;
});
Scene_Item
&
item
=
*
scene
.
get
(
id
);
action
(
[
id
,
this
,
np
=
item
.
pose
()]()
{
Scene_Item
&
item
=
*
scene
.
get
(
id
);
item
.
pose
()
=
np
;
},
[
id
,
this
,
op
=
old
]()
{
Scene_Item
&
item
=
*
scene
.
get
(
id
);
item
.
pose
()
=
op
;
});
}
void
Undo
::
update_camera
(
Gui
::
Widget_Camera
&
widget
,
Camera
old
)
{
void
Undo
::
update_camera
(
Gui
::
Widget_Camera
&
widget
,
Camera
old
)
{
Camera
newc
=
widget
.
get
();
action
([
&
widget
,
nc
=
newc
](){
widget
.
load
(
nc
.
center
(),
nc
.
pos
(),
nc
.
get_ar
(),
nc
.
get_fov
());
},
[
&
widget
,
oc
=
old
](){
widget
.
load
(
oc
.
center
(),
oc
.
pos
(),
oc
.
get_ar
(),
oc
.
get_fov
());
});
action
(
[
&
widget
,
nc
=
newc
]()
{
widget
.
load
(
nc
.
center
(),
nc
.
pos
(),
nc
.
get_ar
(),
nc
.
get_fov
());
},
[
&
widget
,
oc
=
old
]()
{
widget
.
load
(
oc
.
center
(),
oc
.
pos
(),
oc
.
get_ar
(),
oc
.
get_fov
());
});
}
void
Undo
::
anim_pose_bones
(
Scene_ID
id
,
float
t
)
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
bool
had
=
obj
.
anim
.
splines
.
has
(
t
)
||
obj
.
armature
.
has_keyframes
();
Pose
old_pose
=
obj
.
anim
.
at
(
t
);
Pose
new_pose
=
obj
.
pose
;
auto
old_joints
=
obj
.
armature
.
at
(
t
);
...
...
@@ -278,45 +293,51 @@ void Undo::anim_pose_bones(Scene_ID id, float t) {
obj
.
anim
.
set
(
t
,
new_pose
);
obj
.
armature
.
set
(
t
);
action
([
=
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
anim
.
set
(
t
,
new_pose
);
obj
.
armature
.
set
(
t
,
new_joints
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
](){
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
if
(
had
)
{
obj
.
anim
.
set
(
t
,
old_pose
);
obj
.
armature
.
set
(
t
,
old_joints
);
}
else
{
obj
.
anim
.
splines
.
erase
(
t
);
obj
.
armature
.
erase
(
t
);
}
});
action
(
[
=
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
anim
.
set
(
t
,
new_pose
);
obj
.
armature
.
set
(
t
,
new_joints
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
]()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
if
(
had
)
{
obj
.
anim
.
set
(
t
,
old_pose
);
obj
.
armature
.
set
(
t
,
old_joints
);
}
else
{
obj
.
anim
.
splines
.
erase
(
t
);
obj
.
armature
.
erase
(
t
);
}
});
}
void
Undo
::
anim_pose
(
Scene_ID
id
,
float
t
)
{
Scene_Item
&
item
=
*
scene
.
get
(
id
);
Scene_Item
&
item
=
*
scene
.
get
(
id
);
bool
had
=
item
.
animation
().
splines
.
has
(
t
);
Pose
old
=
item
.
animation
().
at
(
t
);
Pose
p
=
item
.
pose
();
item
.
animation
().
set
(
t
,
p
);
action
([
=
](){
Scene_Item
&
item
=
*
scene
.
get
(
id
);
item
.
animation
().
set
(
t
,
p
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
](){
Scene_Item
&
item
=
*
scene
.
get
(
id
);
if
(
had
)
item
.
animation
().
set
(
t
,
old
);
else
item
.
animation
().
splines
.
erase
(
t
);
});
action
(
[
=
]()
{
Scene_Item
&
item
=
*
scene
.
get
(
id
);
item
.
animation
().
set
(
t
,
p
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
]()
{
Scene_Item
&
item
=
*
scene
.
get
(
id
);
if
(
had
)
item
.
animation
().
set
(
t
,
old
);
else
item
.
animation
().
splines
.
erase
(
t
);
});
}
void
Undo
::
anim_camera
(
Gui
::
Anim_Camera
&
anim
,
float
t
,
const
Camera
&
cam
)
{
void
Undo
::
anim_camera
(
Gui
::
Anim_Camera
&
anim
,
float
t
,
const
Camera
&
cam
)
{
bool
had
=
anim
.
splines
.
has
(
t
);
Camera
oldc
=
anim
.
at
(
t
);
...
...
@@ -324,22 +345,26 @@ void Undo::anim_camera(Gui::Anim_Camera& anim, float t, const Camera& cam) {
anim
.
set
(
t
,
newc
);
action
([
=
,
&
anim
](){
anim
.
set
(
t
,
newc
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
,
&
anim
](){
if
(
had
)
anim
.
set
(
t
,
oldc
);
else
anim
.
splines
.
erase
(
t
);
});
action
(
[
=
,
&
anim
]()
{
anim
.
set
(
t
,
newc
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
,
&
anim
]()
{
if
(
had
)
anim
.
set
(
t
,
oldc
);
else
anim
.
splines
.
erase
(
t
);
});
}
void
Undo
::
anim_light
(
Scene_ID
id
,
float
t
)
{
Scene_Light
&
item
=
scene
.
get_light
(
id
);
Scene_Light
&
item
=
scene
.
get_light
(
id
);
bool
had_l
=
item
.
lanim
.
splines
.
has
(
t
);
bool
had_p
=
item
.
anim
.
splines
.
has
(
t
);
Pose
old_pose
=
item
.
anim
.
at
(
t
);
Pose
new_pose
=
item
.
pose
;
...
...
@@ -350,35 +375,43 @@ void Undo::anim_light(Scene_ID id, float t) {
item
.
anim
.
set
(
t
,
new_pose
);
item
.
lanim
.
set
(
t
,
new_l
);
action
([
=
](){
Scene_Light
&
item
=
scene
.
get_light
(
id
);
item
.
lanim
.
set
(
t
,
new_l
);
item
.
anim
.
set
(
t
,
new_pose
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
](){
Scene_Light
&
item
=
scene
.
get_light
(
id
);
if
(
had_l
)
item
.
lanim
.
set
(
t
,
old_l
);
else
item
.
lanim
.
splines
.
erase
(
t
);
if
(
had_p
)
item
.
anim
.
set
(
t
,
old_pose
);
else
item
.
anim
.
splines
.
erase
(
t
);
item
.
dirty
();
});
action
(
[
=
]()
{
Scene_Light
&
item
=
scene
.
get_light
(
id
);
item
.
lanim
.
set
(
t
,
new_l
);
item
.
anim
.
set
(
t
,
new_pose
);
gui
.
get_animate
().
refresh
(
scene
);
},
[
=
]()
{
Scene_Light
&
item
=
scene
.
get_light
(
id
);
if
(
had_l
)
item
.
lanim
.
set
(
t
,
old_l
);
else
item
.
lanim
.
splines
.
erase
(
t
);
if
(
had_p
)
item
.
anim
.
set
(
t
,
old_pose
);
else
item
.
anim
.
splines
.
erase
(
t
);
item
.
dirty
();
});
}
void
Undo
::
action
(
std
::
unique_ptr
<
Action_Base
>&&
action
)
{
void
Undo
::
action
(
std
::
unique_ptr
<
Action_Base
>
&&
action
)
{
redos
=
{};
undos
.
push
(
std
::
move
(
action
));
}
void
Undo
::
undo
()
{
if
(
undos
.
empty
())
return
;
if
(
undos
.
empty
())
return
;
undos
.
top
()
->
undo
();
redos
.
push
(
std
::
move
(
undos
.
top
()));
undos
.
pop
();
}
void
Undo
::
redo
()
{
if
(
redos
.
empty
())
return
;
if
(
redos
.
empty
())
return
;
redos
.
top
()
->
redo
();
undos
.
push
(
std
::
move
(
redos
.
top
()));
redos
.
pop
();
...
...
src/scene/undo.h
View file @
c2535f0f
...
...
@@ -4,76 +4,80 @@
#include <memory>
#include <stack>
#include "scene.h"
#include "../gui/widgets.h"
#include "scene.h"
namespace
Gui
{
class
Manager
;
class
Anim_Camera
;
class
Rig
;
}
namespace
Gui
{
class
Manager
;
class
Anim_Camera
;
class
Rig
;
}
// namespace Gui
class
Action_Base
{
virtual
void
undo
()
=
0
;
virtual
void
redo
()
=
0
;
friend
class
Undo
;
public:
virtual
~
Action_Base
()
=
default
;
};
template
<
typename
T
>
class
MeshOp
:
public
Action_Base
{
template
<
typename
T
>
class
MeshOp
:
public
Action_Base
{
void
undo
()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
obj
.
set_mesh
(
mesh
);
}
void
redo
()
{
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
Scene_Object
&
obj
=
scene
.
get_obj
(
id
);
auto
sel
=
obj
.
set_mesh
(
mesh
,
eid
);
op
(
obj
.
get_mesh
(),
sel
);
}
Scene
&
scene
;
Scene
&
scene
;
Scene_ID
id
;
unsigned
int
eid
;
T
op
;
Halfedge_Mesh
mesh
;
public:
MeshOp
(
Scene
&
s
,
Scene_ID
i
,
unsigned
int
e
,
Halfedge_Mesh
&&
m
,
T
&&
t
)
:
scene
(
s
),
id
(
i
),
eid
(
e
),
op
(
t
),
mesh
(
std
::
move
(
m
))
{}
MeshOp
(
Scene
&
s
,
Scene_ID
i
,
unsigned
int
e
,
Halfedge_Mesh
&&
m
,
T
&&
t
)
:
scene
(
s
),
id
(
i
),
eid
(
e
),
op
(
t
),
mesh
(
std
::
move
(
m
))
{}
~
MeshOp
()
=
default
;
};
class
Undo
{
public:
Undo
(
Scene
&
scene
,
Gui
::
Manager
&
man
);
Undo
(
Scene
&
scene
,
Gui
::
Manager
&
man
);
// These could just take Scene_Object&& but this was easier
Scene_Object
&
add_obj
(
GL
::
Mesh
&&
mesh
,
std
::
string
name
);
Scene_Object
&
add_obj
(
Halfedge_Mesh
&&
mesh
,
std
::
string
name
);
void
add_light
(
Scene_Light
&&
mesh
);
Scene_Object
&
add_obj
(
GL
::
Mesh
&&
mesh
,
std
::
string
name
);
Scene_Object
&
add_obj
(
Halfedge_Mesh
&&
mesh
,
std
::
string
name
);
void
add_light
(
Scene_Light
&&
mesh
);
void
del_obj
(
Scene_ID
id
);
void
update_pose
(
Scene_ID
id
,
Pose
old
);
void
del_bone
(
Scene_ID
id
,
Joint
*
j
);
void
add_bone
(
Scene_ID
id
,
Joint
*
j
);
void
move_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
);
void
pose_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
);
void
del_bone
(
Scene_ID
id
,
Joint
*
j
);
void
add_bone
(
Scene_ID
id
,
Joint
*
j
);
void
move_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
);
void
pose_bone
(
Scene_ID
id
,
Joint
*
j
,
Vec3
old
);
void
move_root
(
Scene_ID
id
,
Vec3
old
);
void
update_light
(
Scene_ID
id
,
Scene_Light
::
Options
old
);
void
update_object
(
Scene_ID
id
,
Scene_Object
::
Options
old
);
void
update_material
(
Scene_ID
id
,
Material
::
Options
old
);
void
update_camera
(
Gui
::
Widget_Camera
&
widget
,
Camera
old
);
void
update_camera
(
Gui
::
Widget_Camera
&
widget
,
Camera
old
);
template
<
typename
T
>
void
update_mesh
(
Scene_ID
id
,
Halfedge_Mesh
&&
old
,
unsigned
int
e_id
,
T
&&
op
)
{
template
<
typename
T
>
void
update_mesh
(
Scene_ID
id
,
Halfedge_Mesh
&&
old
,
unsigned
int
e_id
,
T
&&
op
)
{
std
::
stack
<
std
::
unique_ptr
<
Action_Base
>>
empty
;
redos
.
swap
(
empty
);
undos
.
push
(
std
::
make_unique
<
MeshOp
<
T
>>
(
scene
,
id
,
e_id
,
std
::
move
(
old
),
std
::
move
(
op
)));
}
void
update_mesh_full
(
Scene_ID
id
,
Halfedge_Mesh
&&
old_mesh
);
void
update_mesh_full
(
Scene_ID
id
,
Halfedge_Mesh
&&
old_mesh
);
void
anim_pose
(
Scene_ID
id
,
float
t
);
void
anim_pose_bones
(
Scene_ID
id
,
float
t
);
void
anim_camera
(
Gui
::
Anim_Camera
&
anim
,
float
t
,
const
Camera
&
cam
);
void
anim_camera
(
Gui
::
Anim_Camera
&
anim
,
float
t
,
const
Camera
&
cam
);
void
anim_light
(
Scene_ID
id
,
float
t
);
void
undo
();
...
...
@@ -81,13 +85,12 @@ public:
void
reset
();
private:
Scene
&
scene
;
Gui
::
Manager
&
gui
;
Scene
&
scene
;
Gui
::
Manager
&
gui
;
template
<
typename
R
,
typename
U
>
void
action
(
R
&&
redo
,
U
&&
undo
);
void
action
(
std
::
unique_ptr
<
Action_Base
>
&&
action
);
template
<
typename
R
,
typename
U
>
void
action
(
R
&&
redo
,
U
&&
undo
);
void
action
(
std
::
unique_ptr
<
Action_Base
>&&
action
);
std
::
stack
<
std
::
unique_ptr
<
Action_Base
>>
undos
;
std
::
stack
<
std
::
unique_ptr
<
Action_Base
>>
redos
;
};
src/student/bbox.cpp
View file @
c2535f0f
#include "debug.h"
#include "../lib/mathlib.h"
#include "debug.h"
bool
BBox
::
hit
(
const
Ray
&
ray
,
Vec2
&
times
)
const
{
bool
BBox
::
hit
(
const
Ray
&
ray
,
Vec2
&
times
)
const
{
// TODO (PathTracer):
// Implement ray - bounding box intersection test
// If the ray intersected the bounding box within the range given by
...
...
src/student/bsdf.cpp
View file @
c2535f0f
#include "debug.h"
#include "../rays/bsdf.h"
#include "../util/rand.h"
#include "debug.h"
namespace
PT
{
...
...
@@ -12,14 +12,14 @@ Vec3 reflect(Vec3 dir) {
return
Vec3
();
}
Vec3
refract
(
Vec3
out_dir
,
float
index_of_refraction
,
bool
&
was_internal
)
{
Vec3
refract
(
Vec3
out_dir
,
float
index_of_refraction
,
bool
&
was_internal
)
{
// TODO (PathTracer): Task 6
// Use Snell's Law to refract out_dir through the surface
// Return the refracted direction. Set was_internal to false if
// Return the refracted direction. Set was_internal to false if
// refraction does not occur due to total internal reflection,
// and true otherwise.
// and true otherwise.
// When dot(out_dir,normal=(0,1,0)) is positive, then out_dir corresponds to a
// ray exiting the surface into vaccum (ior = 1). However, note that
// you should actually treat this case as _entering_ the surface, because
...
...
@@ -30,14 +30,14 @@ Vec3 refract(Vec3 out_dir, float index_of_refraction, bool& was_internal) {
}
BSDF_Sample
BSDF_Lambertian
::
sample
(
Vec3
out_dir
)
const
{
// TODO (PathTracer): Task 5
// Implement lambertian BSDF. Use of BSDF_Lambertian::sampler may be useful
BSDF_Sample
ret
;
ret
.
attenuation
=
Spectrum
();
// What is the ratio of reflected/incoming light?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
pdf
=
0.0
f
;
// Was was the PDF of the sampled direction?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
pdf
=
0.0
f
;
// Was was the PDF of the sampled direction?
return
ret
;
}
...
...
@@ -46,13 +46,13 @@ Spectrum BSDF_Lambertian::evaluate(Vec3 out_dir, Vec3 in_dir) const {
}
BSDF_Sample
BSDF_Mirror
::
sample
(
Vec3
out_dir
)
const
{
// TODO (PathTracer): Task 6
// Implement mirror BSDF
BSDF_Sample
ret
;
ret
.
attenuation
=
Spectrum
();
// What is the ratio of reflected/incoming light?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
pdf
=
0.0
f
;
// Was was the PDF of the sampled direction? (In this case, the PMF)
return
ret
;
}
...
...
@@ -60,16 +60,16 @@ BSDF_Sample BSDF_Mirror::sample(Vec3 out_dir) const {
Spectrum
BSDF_Mirror
::
evaluate
(
Vec3
out_dir
,
Vec3
in_dir
)
const
{
// Technically, we would return the proper reflectance
// if in_dir was the perfectly reflected out_dir, but given
// that we assume these are single exact directions in a
// continuous space, just assume that we never hit them
// _exactly_ and always return 0.
// that we assume these are single exact directions in a
// continuous space, just assume that we never hit them
// _exactly_ and always return 0.
return
{};
}
BSDF_Sample
BSDF_Glass
::
sample
(
Vec3
out_dir
)
const
{
// TODO (PathTracer): Task 6
// Implement glass BSDF.
// (1) Compute Fresnel coefficient. Tip: use Schlick's approximation.
// (2) Reflect or refract probabilistically based on Fresnel coefficient. Tip: RNG::coin_flip
...
...
@@ -79,14 +79,14 @@ BSDF_Sample BSDF_Glass::sample(Vec3 out_dir) const {
BSDF_Sample
ret
;
ret
.
attenuation
=
Spectrum
();
// What is the ratio of reflected/incoming light?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
pdf
=
0.0
f
;
// Was was the PDF of the sampled direction? (In this case, the PMF)
return
ret
;
}
Spectrum
BSDF_Glass
::
evaluate
(
Vec3
out_dir
,
Vec3
in_dir
)
const
{
// As with BSDF_Mirror, just assume that we never hit the correct
// directions _exactly_ and always return 0.
// directions _exactly_ and always return 0.
return
{};
}
...
...
@@ -104,23 +104,23 @@ Spectrum BSDF_Diffuse::evaluate(Vec3 out_dir, Vec3 in_dir) const {
}
BSDF_Sample
BSDF_Refract
::
sample
(
Vec3
out_dir
)
const
{
// TODO (PathTracer): Task 6
// TODO (PathTracer): Task 6
// Implement pure refraction BSDF.
// Be wary of your eta1/eta2 ratio - are you entering or leaving the surface?
BSDF_Sample
ret
;
ret
.
attenuation
=
Spectrum
();
// What is the ratio of reflected/incoming light?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
direction
=
Vec3
();
// What direction should we sample incoming light from?
ret
.
pdf
=
0.0
f
;
// Was was the PDF of the sampled direction? (In this case, the PMF)
return
ret
;
}
Spectrum
BSDF_Refract
::
evaluate
(
Vec3
out_dir
,
Vec3
in_dir
)
const
{
// As with BSDF_Mirror, just assume that we never hit the correct
// directions _exactly_ and always return 0.
// directions _exactly_ and always return 0.
return
{};
}
}
}
// namespace PT
src/student/bvh.cpp
View file @
c2535f0f
#include "debug.h"
#include "../rays/bvh.h"
#include "debug.h"
#include <stack>
namespace
PT
{
template
<
typename
Primitive
>
void
BVH
<
Primitive
>::
build
(
std
::
vector
<
Primitive
>&&
prims
,
size_t
max_leaf_size
)
{
template
<
typename
Primitive
>
void
BVH
<
Primitive
>::
build
(
std
::
vector
<
Primitive
>
&&
prims
,
size_t
max_leaf_size
)
{
// NOTE (PathTracer):
// This BVH is parameterized on the type of the primitive it contains. This allows
...
...
@@ -37,12 +37,12 @@ void BVH<Primitive>::build(std::vector<Primitive>&& prims, size_t max_leaf_size)
// primitives.
BBox
box
;
for
(
const
Primitive
&
prim
:
primitives
)
box
.
enclose
(
prim
.
bbox
());
for
(
const
Primitive
&
prim
:
primitives
)
box
.
enclose
(
prim
.
bbox
());
new_node
(
box
,
0
,
primitives
.
size
(),
0
,
0
);
}
template
<
typename
Primitive
>
Trace
BVH
<
Primitive
>::
hit
(
const
Ray
&
ray
)
const
{
template
<
typename
Primitive
>
Trace
BVH
<
Primitive
>::
hit
(
const
Ray
&
ray
)
const
{
// TODO (PathTracer): Task 3
// Implement ray - BVH intersection test. A ray intersects
...
...
@@ -53,24 +53,23 @@ Trace BVH<Primitive>::hit(const Ray& ray) const {
// Again, remember you can use hit() on any Primitive value.
Trace
ret
;
for
(
const
Primitive
&
prim
:
primitives
)
{
for
(
const
Primitive
&
prim
:
primitives
)
{
Trace
hit
=
prim
.
hit
(
ray
);
ret
=
Trace
::
min
(
ret
,
hit
);
}
return
ret
;
}
template
<
typename
Primitive
>
BVH
<
Primitive
>::
BVH
(
std
::
vector
<
Primitive
>&&
prims
,
size_t
max_leaf_size
)
{
template
<
typename
Primitive
>
BVH
<
Primitive
>::
BVH
(
std
::
vector
<
Primitive
>
&&
prims
,
size_t
max_leaf_size
)
{
build
(
std
::
move
(
prims
),
max_leaf_size
);
}
template
<
typename
Primitive
>
bool
BVH
<
Primitive
>::
Node
::
is_leaf
()
const
{
template
<
typename
Primitive
>
bool
BVH
<
Primitive
>::
Node
::
is_leaf
()
const
{
return
l
==
0
&&
r
==
0
;
}
template
<
typename
Primitive
>
template
<
typename
Primitive
>
size_t
BVH
<
Primitive
>::
new_node
(
BBox
box
,
size_t
start
,
size_t
size
,
size_t
l
,
size_t
r
)
{
Node
n
;
n
.
bbox
=
box
;
...
...
@@ -82,49 +81,44 @@ size_t BVH<Primitive>::new_node(BBox box, size_t start, size_t size, size_t l, s
return
nodes
.
size
()
-
1
;
}
template
<
typename
Primitive
>
BBox
BVH
<
Primitive
>::
bbox
()
const
{
return
nodes
[
0
].
bbox
;
}
template
<
typename
Primitive
>
BBox
BVH
<
Primitive
>::
bbox
()
const
{
return
nodes
[
0
].
bbox
;
}
template
<
typename
Primitive
>
std
::
vector
<
Primitive
>
BVH
<
Primitive
>::
destructure
()
{
template
<
typename
Primitive
>
std
::
vector
<
Primitive
>
BVH
<
Primitive
>::
destructure
()
{
nodes
.
clear
();
return
std
::
move
(
primitives
);
}
template
<
typename
Primitive
>
void
BVH
<
Primitive
>::
clear
()
{
template
<
typename
Primitive
>
void
BVH
<
Primitive
>::
clear
()
{
nodes
.
clear
();
primitives
.
clear
();
}
template
<
typename
Primitive
>
size_t
BVH
<
Primitive
>::
visualize
(
GL
::
Lines
&
lines
,
GL
::
Lines
&
active
,
size_t
level
,
const
Mat4
&
trans
)
const
{
template
<
typename
Primitive
>
size_t
BVH
<
Primitive
>::
visualize
(
GL
::
Lines
&
lines
,
GL
::
Lines
&
active
,
size_t
level
,
const
Mat4
&
trans
)
const
{
std
::
stack
<
std
::
pair
<
size_t
,
size_t
>>
tstack
;
tstack
.
push
({
0
,
0
});
std
::
stack
<
std
::
pair
<
size_t
,
size_t
>>
tstack
;
tstack
.
push
({
0
,
0
});
size_t
max_level
=
0
;
if
(
nodes
.
empty
())
return
max_level
;
if
(
nodes
.
empty
())
return
max_level
;
while
(
!
tstack
.
empty
())
{
auto
[
idx
,
lvl
]
=
tstack
.
top
();
max_level
=
std
::
max
(
max_level
,
lvl
);
const
Node
&
node
=
nodes
[
idx
];
const
Node
&
node
=
nodes
[
idx
];
tstack
.
pop
();
Vec3
color
=
lvl
==
level
?
Vec3
(
1.0
f
,
0.0
f
,
0.0
f
)
:
Vec3
(
1.0
f
);
GL
::
Lines
&
add
=
lvl
==
level
?
active
:
lines
;
GL
::
Lines
&
add
=
lvl
==
level
?
active
:
lines
;
BBox
box
=
node
.
bbox
;
box
.
transform
(
trans
);
Vec3
min
=
box
.
min
,
max
=
box
.
max
;
auto
edge
=
[
&
](
Vec3
a
,
Vec3
b
)
{
add
.
add
(
a
,
b
,
color
);
};
auto
edge
=
[
&
](
Vec3
a
,
Vec3
b
)
{
add
.
add
(
a
,
b
,
color
);
};
edge
(
min
,
Vec3
{
max
.
x
,
min
.
y
,
min
.
z
});
edge
(
min
,
Vec3
{
min
.
x
,
max
.
y
,
min
.
z
});
...
...
@@ -139,11 +133,13 @@ size_t BVH<Primitive>::visualize(GL::Lines& lines, GL::Lines& active, size_t lev
edge
(
Vec3
{
max
.
x
,
min
.
y
,
min
.
z
},
Vec3
{
max
.
x
,
max
.
y
,
min
.
z
});
edge
(
Vec3
{
max
.
x
,
min
.
y
,
min
.
z
},
Vec3
{
max
.
x
,
min
.
y
,
max
.
z
});
if
(
node
.
l
)
tstack
.
push
({
node
.
l
,
lvl
+
1
});
if
(
node
.
r
)
tstack
.
push
({
node
.
r
,
lvl
+
1
});
if
(
node
.
l
)
tstack
.
push
({
node
.
l
,
lvl
+
1
});
if
(
node
.
r
)
tstack
.
push
({
node
.
r
,
lvl
+
1
});
if
(
!
node
.
l
&&
!
node
.
r
)
{
for
(
size_t
i
=
node
.
start
;
i
<
node
.
start
+
node
.
size
;
i
++
)
{
if
(
!
node
.
l
&&
!
node
.
r
)
{
for
(
size_t
i
=
node
.
start
;
i
<
node
.
start
+
node
.
size
;
i
++
)
{
size_t
c
=
primitives
[
i
].
visualize
(
lines
,
active
,
level
-
lvl
,
trans
);
max_level
=
std
::
max
(
c
,
max_level
);
}
...
...
@@ -152,4 +148,4 @@ size_t BVH<Primitive>::visualize(GL::Lines& lines, GL::Lines& active, size_t lev
return
max_level
;
}
}
}
// namespace PT
src/student/camera.cpp
View file @
c2535f0f
#include "debug.h"
#include "../util/camera.h"
#include "debug.h"
Ray
Camera
::
generate_ray
(
Vec2
screen_coord
)
const
{
...
...
src/student/debug.cpp
View file @
c2535f0f
...
...
@@ -23,17 +23,17 @@ Debug_Data debug_data;
// This runs when the button is clicked
}
Similarly, you can directly connect UI elements to data values by
Similarly, you can directly connect UI elements to data values by
passing in the address of your storage variable:
Checkbox("My Checkbox", &bool_variable);
Then, bool_variable will always reflect the state of the checkbox.
Then, bool_variable will always reflect the state of the checkbox.
These constructs are composable to make pretty advanced UI elements!
The whole Scotty3D UI is implemented in this way.
Some useful functions are documented below, and you can refer to
Some useful functions are documented below, and you can refer to
deps/imgui/imgui.h for many more.
*/
void
student_debug_ui
()
{
...
...
@@ -43,13 +43,13 @@ void student_debug_ui() {
Checkbox
(
"Pathtracer: use normal colors"
,
&
debug_data
.
normal_colors
);
// ImGui examples
if
(
Button
(
"Press Me"
))
{
if
(
Button
(
"Press Me"
))
{
info
(
"Debug button pressed!"
);
}
// We need to store values somewhere, or else they will get reset every time
// we run this function (which is every frame). For convenience, we make them
// static, which gives them the same storage class as global variables.
// static, which gives them the same storage class as global variables.
static
int
int_value
=
0
;
InputInt
(
"Int Input"
,
&
int_value
);
...
...
src/student/debug.h
View file @
c2535f0f
...
...
@@ -4,17 +4,17 @@
/* Debugging Tips:
You may use this file, as well as debug.cpp, to add any debugging features and
UI options that you find useful. To do so, you can add fields to the global
Debug_Data type here and access them in any other student/ files via debug_data.field
You can use your fields to enable/disable features or otherwise change how your
implementation behaves in the other files.
UI options that you find useful. To do so, you can add fields to the global
Debug_Data type here and access them in any other student/ files via debug_data.field
You can use your fields to enable/disable features or otherwise change how your
implementation behaves in the other files.
You can also connect your debug fields to specific UI options by adding
ImGui calls in debug.cpp. This creates a special UI panel that can be enabled
by the Edit -> Edit Debug Data menu item or by pressing Ctrl+D.
by the Edit -> Edit Debug Data menu item or by pressing Ctrl+D.
This panel will contain your specific debug controls.
For example, we have already implemented an option to color pathtracer objects
For example, we have already implemented an option to color pathtracer objects
based on their surface normal. This involved adding the following field to
Debug_Data:
...
...
@@ -25,7 +25,7 @@
This ImGui option to student_debug_ui in debug.cpp:
void student_debug_ui() {
// ...
Checkbox("Use Normal Colors", &debugger.normal_colors);
// ...
...
...
@@ -34,9 +34,10 @@
And we finally used the option in pathtracer.cpp:
Spectrum Pathtracer::trace_ray(const Ray& ray) {
// ...
Spectrum radiance_out = debug_data.normal_colors ? Spectrum(0.5f) : Spectrum::direction(hit.normal);
Spectrum radiance_out = debug_data.normal_colors ? Spectrum(0.5f) :
Spectrum::direction(hit.normal);
// ...
}
*/
...
...
src/student/env_light.cpp
View file @
c2535f0f
#include "debug.h"
#include "../rays/env_light.h"
#include "debug.h"
#include <limits>
...
...
@@ -16,7 +16,7 @@ Light_Sample Env_Map::sample() const {
Samplers
::
Sphere
::
Uniform
uniform
;
ret
.
direction
=
uniform
.
sample
(
ret
.
pdf
);
// Once you've implemented Samplers::Sphere::Image, remove the above and
// Once you've implemented Samplers::Sphere::Image, remove the above and
// uncomment this line to use importance sampling instead.
// ret.direction = sampler.sample(ret.pdf);
...
...
@@ -42,7 +42,8 @@ Light_Sample Env_Hemisphere::sample() const {
}
Spectrum
Env_Hemisphere
::
sample_direction
(
Vec3
dir
)
const
{
if
(
dir
.
y
>
0.0
f
)
return
radiance
;
if
(
dir
.
y
>
0.0
f
)
return
radiance
;
return
{};
}
...
...
@@ -54,8 +55,6 @@ Light_Sample Env_Sphere::sample() const {
return
ret
;
}
Spectrum
Env_Sphere
::
sample_direction
(
Vec3
)
const
{
return
radiance
;
}
Spectrum
Env_Sphere
::
sample_direction
(
Vec3
)
const
{
return
radiance
;
}
}
}
// namespace PT
src/student/meshedit.cpp
View file @
c2535f0f
#include <set>
#include <queue>
#include <set>
#include <unordered_map>
#include "debug.h"
#include "../geometry/halfedge.h"
#include "debug.h"
/* Note on local operation return types:
The local operations all return a std::optional<T> type. This is used so that your
implementation can signify that it does not want to perform the operation for
whatever reason (e.g. you don't want to allow the user to erase the last vertex).
The local operations all return a std::optional<T> type. This is used so that your
implementation can signify that it does not want to perform the operation for
whatever reason (e.g. you don't want to allow the user to erase the last vertex).
An optional can have two values: std::nullopt, or a value of the type it is
parameterized on. In this way, it's similar to a pointer, but has two advantages:
the value it holds need not be allocated elsewhere, and it provides an API that
forces the user to check if it is null before using the value.
An optional can have two values: std::nullopt, or a value of the type it is
parameterized on. In this way, it's similar to a pointer, but has two advantages:
the value it holds need not be allocated elsewhere, and it provides an API that
forces the user to check if it is null before using the value.
In your implementaiton, if you have successfully performed the operation, you can
simply return the required reference:
In your implementaiton, if you have successfully performed the operation, you can
simply return the required reference:
... collapse the edge ...
return collapsed_vertex_ref;
... collapse the edge ...
return collapsed_vertex_ref;
And if you wish to deny the operation, you can return the null optional:
And if you wish to deny the operation, you can return the null optional:
return std::nullopt;
return std::nullopt;
Note that the stubs below all reject their duties by returning the null optional.
Note that the stubs below all reject their duties by returning the null optional.
*/
/*
This method should replace the given vertex and all its neighboring
edges and faces with a single face, returning the new face.
This method should replace the given vertex and all its neighboring
edges and faces with a single face, returning the new face.
*/
std
::
optional
<
Halfedge_Mesh
::
FaceRef
>
Halfedge_Mesh
::
erase_vertex
(
Halfedge_Mesh
::
VertexRef
v
)
{
(
void
)
v
;
return
std
::
nullopt
;
(
void
)
v
;
return
std
::
nullopt
;
}
/*
This method should erase the given edge and return an iterator to the
merged face.
This method should erase the given edge and return an iterator to the
merged face.
*/
std
::
optional
<
Halfedge_Mesh
::
FaceRef
>
Halfedge_Mesh
::
erase_edge
(
Halfedge_Mesh
::
EdgeRef
e
)
{
(
void
)
e
;
return
std
::
nullopt
;
(
void
)
e
;
return
std
::
nullopt
;
}
/*
This method should collapse the given edge and return an iterator to
the new vertex created by the collapse.
This method should collapse the given edge and return an iterator to
the new vertex created by the collapse.
*/
std
::
optional
<
Halfedge_Mesh
::
VertexRef
>
Halfedge_Mesh
::
collapse_edge
(
Halfedge_Mesh
::
EdgeRef
e
)
{
(
void
)
e
;
return
std
::
nullopt
;
(
void
)
e
;
return
std
::
nullopt
;
}
/*
This method should collapse the given face and return an iterator to
the new vertex created by the collapse.
This method should collapse the given face and return an iterator to
the new vertex created by the collapse.
*/
std
::
optional
<
Halfedge_Mesh
::
VertexRef
>
Halfedge_Mesh
::
collapse_face
(
Halfedge_Mesh
::
FaceRef
f
)
{
(
void
)
f
;
return
std
::
nullopt
;
(
void
)
f
;
return
std
::
nullopt
;
}
/*
This method should flip the given edge and return an iterator to the
flipped edge.
This method should flip the given edge and return an iterator to the
flipped edge.
*/
std
::
optional
<
Halfedge_Mesh
::
EdgeRef
>
Halfedge_Mesh
::
flip_edge
(
Halfedge_Mesh
::
EdgeRef
e
)
{
(
void
)
e
;
return
std
::
nullopt
;
(
void
)
e
;
return
std
::
nullopt
;
}
/*
This method should split the given edge and return an iterator to the
newly inserted vertex. The halfedge of this vertex should point along
the edge that was split, rather than the new edges.
This method should split the given edge and return an iterator to the
newly inserted vertex. The halfedge of this vertex should point along
the edge that was split, rather than the new edges.
*/
std
::
optional
<
Halfedge_Mesh
::
VertexRef
>
Halfedge_Mesh
::
split_edge
(
Halfedge_Mesh
::
EdgeRef
e
)
{
(
void
)
e
;
return
std
::
nullopt
;
(
void
)
e
;
return
std
::
nullopt
;
}
/* Note on the beveling process:
Each of the bevel_vertex, bevel_edge, and bevel_face functions do not represent
a full bevel operation. Instead, they should only update the _connectivity_ of
the mesh, _not_ the positions of newly created vertices. In fact, you should set
the positions of new vertices to be exactly the same as wherever they "started from."
When you click on a mesh element while in bevel mode, one of those three functions
is called. But, because you may then adjust the distance/offset of the newly
beveled face, we need another method of updating the positions of the new vertices.
This is where bevel_vertex_positions, bevel_edge_positions, and
bevel_face_positions come in: these functions are called repeatedly as you
move your mouse, the position of which determins the normal and tangent offset
parameters. These functions are also passed an array of the original vertex
positions: for bevel_vertex, it has one element, the original vertex position,
for bevel_edge, two for the two vertices, and for bevel_face, it has the original
position of each vertex in halfedge order. You should use these positions, as well
as the normal and tangent offset fields to assign positions to the new vertices.
Finally, note that the normal and tangent offsets are not relative values - you
should compute a particular new position from them, not a delta to apply.
Each of the bevel_vertex, bevel_edge, and bevel_face functions do not represent
a full bevel operation. Instead, they should only update the _connectivity_ of
the mesh, _not_ the positions of newly created vertices. In fact, you should set
the positions of new vertices to be exactly the same as wherever they "started from."
When you click on a mesh element while in bevel mode, one of those three functions
is called. But, because you may then adjust the distance/offset of the newly
beveled face, we need another method of updating the positions of the new vertices.
This is where bevel_vertex_positions, bevel_edge_positions, and
bevel_face_positions come in: these functions are called repeatedly as you
move your mouse, the position of which determins the normal and tangent offset
parameters. These functions are also passed an array of the original vertex
positions: for bevel_vertex, it has one element, the original vertex position,
for bevel_edge, two for the two vertices, and for bevel_face, it has the original
position of each vertex in halfedge order. You should use these positions, as well
as the normal and tangent offset fields to assign positions to the new vertices.
Finally, note that the normal and tangent offsets are not relative values - you
should compute a particular new position from them, not a delta to apply.
*/
/*
This method should replace the vertex v with a face, corresponding to
a bevel operation. It should return the new face. NOTE: This method is
responsible for updating the *connectivity* of the mesh only---it does not
need to update the vertex positions. These positions will be updated in
Halfedge_Mesh::bevel_vertex_positions (which you also have to
implement!)
This method should replace the vertex v with a face, corresponding to
a bevel operation. It should return the new face. NOTE: This method is
responsible for updating the *connectivity* of the mesh only---it does not
need to update the vertex positions. These positions will be updated in
Halfedge_Mesh::bevel_vertex_positions (which you also have to
implement!)
*/
std
::
optional
<
Halfedge_Mesh
::
FaceRef
>
Halfedge_Mesh
::
bevel_vertex
(
Halfedge_Mesh
::
VertexRef
v
)
{
(
void
)
v
;
return
std
::
nullopt
;
(
void
)
v
;
return
std
::
nullopt
;
}
/*
This method should replace the edge e with a face, corresponding to a
bevel operation. It should return the new face. NOTE: This method is
responsible for updating the *connectivity* of the mesh only---it does not
need to update the vertex positions. These positions will be updated in
Halfedge_Mesh::bevel_edge_positions (which you also have to
implement!)
This method should replace the edge e with a face, corresponding to a
bevel operation. It should return the new face. NOTE: This method is
responsible for updating the *connectivity* of the mesh only---it does not
need to update the vertex positions. These positions will be updated in
Halfedge_Mesh::bevel_edge_positions (which you also have to
implement!)
*/
std
::
optional
<
Halfedge_Mesh
::
FaceRef
>
Halfedge_Mesh
::
bevel_edge
(
Halfedge_Mesh
::
EdgeRef
e
)
{
(
void
)
e
;
return
std
::
nullopt
;
(
void
)
e
;
return
std
::
nullopt
;
}
/*
This method should replace the face f with an additional, inset face
(and ring of faces around it), corresponding to a bevel operation. It
should return the new face. NOTE: This method is responsible for updating
the *connectivity* of the mesh only---it does not need to update the vertex
positions. These positions will be updated in
Halfedge_Mesh::bevel_face_positions (which you also have to
implement!)
This method should replace the face f with an additional, inset face
(and ring of faces around it), corresponding to a bevel operation. It
should return the new face. NOTE: This method is responsible for updating
the *connectivity* of the mesh only---it does not need to update the vertex
positions. These positions will be updated in
Halfedge_Mesh::bevel_face_positions (which you also have to
implement!)
*/
std
::
optional
<
Halfedge_Mesh
::
FaceRef
>
Halfedge_Mesh
::
bevel_face
(
Halfedge_Mesh
::
FaceRef
f
)
{
(
void
)
f
;
return
std
::
nullopt
;
(
void
)
f
;
return
std
::
nullopt
;
}
/*
Compute new vertex positions for the vertices of the beveled vertex.
These vertices can be accessed via new_halfedges[i]->vertex()->pos for
i = 1, ..., new_halfedges.size()-1.
The basic strategy here is to loop over the list of outgoing halfedges,
and use the original vertex position and its associated outgoing edge
to compute a new vertex position along the outgoing edge.
Compute new vertex positions for the vertices of the beveled vertex.
These vertices can be accessed via new_halfedges[i]->vertex()->pos for
i = 1, ..., new_halfedges.size()-1.
The basic strategy here is to loop over the list of outgoing halfedges,
and use the original vertex position and its associated outgoing edge
to compute a new vertex position along the outgoing edge.
*/
void
Halfedge_Mesh
::
bevel_vertex_positions
(
const
std
::
vector
<
Vec3
>
&
start_positions
,
Halfedge_Mesh
::
FaceRef
face
,
float
tangent_offset
)
{
void
Halfedge_Mesh
::
bevel_vertex_positions
(
const
std
::
vector
<
Vec3
>
&
start_positions
,
Halfedge_Mesh
::
FaceRef
face
,
float
tangent_offset
)
{
std
::
vector
<
HalfedgeRef
>
new_halfedges
;
std
::
vector
<
HalfedgeRef
>
new_halfedges
;
auto
h
=
face
->
halfedge
();
do
{
new_halfedges
.
push_back
(
h
);
h
=
h
->
next
();
}
while
(
h
!=
face
->
halfedge
());
}
while
(
h
!=
face
->
halfedge
());
(
void
)
new_halfedges
;
(
void
)
start_positions
;
(
void
)
face
;
(
void
)
tangent_offset
;
(
void
)
new_halfedges
;
(
void
)
start_positions
;
(
void
)
face
;
(
void
)
tangent_offset
;
}
/*
Compute new vertex positions for the vertices of the beveled edge.
These vertices can be accessed via new_halfedges[i]->vertex()->pos for
i = 1, ..., new_halfedges.size()-1.
The basic strategy here is to loop over the list of outgoing halfedges,
and use the preceding and next vertex position from the original mesh
(in the orig array) to compute an offset vertex position.
Note that there is a 1-to-1 correspondence between halfedges in
newHalfedges and vertex positions
in orig. So, you can write loops of the form
for(size_t i = 0; i < new_halfedges.size(); i++)
{
Vector3D pi = start_positions[i]; // get the original vertex
position corresponding to vertex i
}
Compute new vertex positions for the vertices of the beveled edge.
These vertices can be accessed via new_halfedges[i]->vertex()->pos for
i = 1, ..., new_halfedges.size()-1.
The basic strategy here is to loop over the list of outgoing halfedges,
and use the preceding and next vertex position from the original mesh
(in the orig array) to compute an offset vertex position.
Note that there is a 1-to-1 correspondence between halfedges in
newHalfedges and vertex positions
in orig. So, you can write loops of the form
for(size_t i = 0; i < new_halfedges.size(); i++)
{
Vector3D pi = start_positions[i]; // get the original vertex
position corresponding to vertex i
}
*/
void
Halfedge_Mesh
::
bevel_edge_positions
(
const
std
::
vector
<
Vec3
>
&
start_positions
,
Halfedge_Mesh
::
FaceRef
face
,
float
tangent_offset
)
{
void
Halfedge_Mesh
::
bevel_edge_positions
(
const
std
::
vector
<
Vec3
>
&
start_positions
,
Halfedge_Mesh
::
FaceRef
face
,
float
tangent_offset
)
{
std
::
vector
<
HalfedgeRef
>
new_halfedges
;
std
::
vector
<
HalfedgeRef
>
new_halfedges
;
auto
h
=
face
->
halfedge
();
do
{
new_halfedges
.
push_back
(
h
);
h
=
h
->
next
();
}
while
(
h
!=
face
->
halfedge
());
}
while
(
h
!=
face
->
halfedge
());
(
void
)
new_halfedges
;
(
void
)
start_positions
;
(
void
)
face
;
(
void
)
tangent_offset
;
(
void
)
new_halfedges
;
(
void
)
start_positions
;
(
void
)
face
;
(
void
)
tangent_offset
;
}
/*
Compute new vertex positions for the vertices of the beveled face.
These vertices can be accessed via new_halfedges[i]->vertex()->pos for
i = 1, ..., new_halfedges.size()-1.
The basic strategy here is to loop over the list of outgoing halfedges,
and use the preceding and next vertex position from the original mesh
(in the start_positions array) to compute an offset vertex
position.
Note that there is a 1-to-1 correspondence between halfedges in
new_halfedges and vertex positions
in orig. So, you can write loops of the form
for(size_t i = 0; i < new_halfedges.size(); hs++)
{
Vec3 pi = start_positions[i]; // get the original vertex
position corresponding to vertex i
}
Compute new vertex positions for the vertices of the beveled face.
These vertices can be accessed via new_halfedges[i]->vertex()->pos for
i = 1, ..., new_halfedges.size()-1.
The basic strategy here is to loop over the list of outgoing halfedges,
and use the preceding and next vertex position from the original mesh
(in the start_positions array) to compute an offset vertex
position.
Note that there is a 1-to-1 correspondence between halfedges in
new_halfedges and vertex positions
in orig. So, you can write loops of the form
for(size_t i = 0; i < new_halfedges.size(); hs++)
{
Vec3 pi = start_positions[i]; // get the original vertex
position corresponding to vertex i
}
*/
void
Halfedge_Mesh
::
bevel_face_positions
(
const
std
::
vector
<
Vec3
>&
start_positions
,
Halfedge_Mesh
::
FaceRef
face
,
float
tangent_offset
,
float
normal_offset
)
{
void
Halfedge_Mesh
::
bevel_face_positions
(
const
std
::
vector
<
Vec3
>
&
start_positions
,
Halfedge_Mesh
::
FaceRef
face
,
float
tangent_offset
,
float
normal_offset
)
{
if
(
flip_orientation
)
normal_offset
=
-
normal_offset
;
std
::
vector
<
HalfedgeRef
>
new_halfedges
;
if
(
flip_orientation
)
normal_offset
=
-
normal_offset
;
std
::
vector
<
HalfedgeRef
>
new_halfedges
;
auto
h
=
face
->
halfedge
();
do
{
new_halfedges
.
push_back
(
h
);
h
=
h
->
next
();
}
while
(
h
!=
face
->
halfedge
());
}
while
(
h
!=
face
->
halfedge
());
(
void
)
new_halfedges
;
(
void
)
start_positions
;
(
void
)
face
;
(
void
)
tangent_offset
;
(
void
)
normal_offset
;
(
void
)
new_halfedges
;
(
void
)
start_positions
;
(
void
)
face
;
(
void
)
tangent_offset
;
(
void
)
normal_offset
;
}
/*
Splits all non-triangular faces into triangles.
Splits all non-triangular faces into triangles.
*/
void
Halfedge_Mesh
::
triangulate
()
{
// For each face...
// For each face...
}
/* Note on the quad subdivision process:
Unlike the local mesh operations (like bevel or edge flip), we will perform
subdivision by splitting *all* faces into quads "simultaneously." Rather
than operating directly on the halfedge data structure (which as you've
seen is quite difficult to maintain!) we are going to do something a bit nicer:
1. Create a raw list of vertex positions and faces (rather than a full-
blown halfedge mesh).
2. Build a new halfedge mesh from these lists, replacing the old one.
Sometimes rebuilding a data structure from scratch is simpler (and even
more efficient) than incrementally modifying the existing one. These steps are
detailed below.
Step I: Compute the vertex positions for the subdivided mesh.
Here we're going to do something a little bit strange: since we will
have one vertex in the subdivided mesh for each vertex, edge, and face in
the original mesh, we can nicely store the new vertex *positions* as
attributes on vertices, edges, and faces of the original mesh. These positions
can then be conveniently copied into the new, subdivided mesh.
This is what you will implement in linear_subdivide_positions() and
catmullclark_subdivide_positions().
Steps II-IV are provided (see Halfedge_Mesh::subdivide()), but are still detailed
Unlike the local mesh operations (like bevel or edge flip), we will perform
subdivision by splitting *all* faces into quads "simultaneously." Rather
than operating directly on the halfedge data structure (which as you've
seen is quite difficult to maintain!) we are going to do something a bit nicer:
1. Create a raw list of vertex positions and faces (rather than a full-
blown halfedge mesh).
2. Build a new halfedge mesh from these lists, replacing the old one.
Sometimes rebuilding a data structure from scratch is simpler (and even
more efficient) than incrementally modifying the existing one. These steps are
detailed below.
Step I: Compute the vertex positions for the subdivided mesh.
Here we're going to do something a little bit strange: since we will
have one vertex in the subdivided mesh for each vertex, edge, and face in
the original mesh, we can nicely store the new vertex *positions* as
attributes on vertices, edges, and faces of the original mesh. These positions
can then be conveniently copied into the new, subdivided mesh.
This is what you will implement in linear_subdivide_positions() and
catmullclark_subdivide_positions().
Steps II-IV are provided (see Halfedge_Mesh::subdivide()), but are still detailed
here:
Step II: Assign a unique index (starting at 0) to each vertex, edge, and
face in the original mesh. These indices will be the indices of the
vertices in the new (subdivided mesh). They do not have to be assigned
in any particular order, so long as no index is shared by more than one
mesh element, and the total number of indices is equal to V+E+F, i.e.,
the total number of vertices plus edges plus faces in the original mesh.
Basically we just need a one-to-one mapping between original mesh elements
and subdivided mesh vertices.
face in the original mesh. These indices will be the indices of the
vertices in the new (subdivided mesh). They do not have to be assigned
in any particular order, so long as no index is shared by more than one
mesh element, and the total number of indices is equal to V+E+F, i.e.,
the total number of vertices plus edges plus faces in the original mesh.
Basically we just need a one-to-one mapping between original mesh elements
and subdivided mesh vertices.
Step III: Build a list of quads in the new (subdivided) mesh, as tuples of
the element indices defined above. In other words, each new quad should be
of the form (i,j,k,l), where i,j,k and l are four of the indices stored on
our original mesh elements. Note that it is essential to get the orientation
right here: (i,j,k,l) is not the same as (l,k,j,i). Indices of new faces
should circulate in the same direction as old faces (think about the right-hand
rule).
the element indices defined above. In other words, each new quad should be
of the form (i,j,k,l), where i,j,k and l are four of the indices stored on
our original mesh elements. Note that it is essential to get the orientation
right here: (i,j,k,l) is not the same as (l,k,j,i). Indices of new faces
should circulate in the same direction as old faces (think about the right-hand
rule).
Step IV: Pass the list of vertices and quads to a routine that clears
the internal data for this halfedge mesh, and builds new halfedge data from
scratch, using the two lists.
the internal data for this halfedge mesh, and builds new halfedge data from
scratch, using the two lists.
*/
/*
Compute new vertex positions for a mesh that splits each polygon
into quads (by inserting a vertex at the face midpoint and each
of the edge midpoints). The new vertex positions will be stored
in the members Vertex::new_pos, Edge::new_pos, and
Face::new_pos. The values of the positions are based on
simple linear interpolation, e.g., the edge midpoints and face
centroids.
Compute new vertex positions for a mesh that splits each polygon
into quads (by inserting a vertex at the face midpoint and each
of the edge midpoints). The new vertex positions will be stored
in the members Vertex::new_pos, Edge::new_pos, and
Face::new_pos. The values of the positions are based on
simple linear interpolation, e.g., the edge midpoints and face
centroids.
*/
void
Halfedge_Mesh
::
linear_subdivide_positions
()
{
// For each vertex, assign Vertex::new_pos to
// its original position, Vertex::pos.
// For each vertex, assign Vertex::new_pos to
// its original position, Vertex::pos.
// For each edge, assign the midpoint of the two original
// positions to Edge::new_pos.
// For each edge, assign the midpoint of the two original
// positions to Edge::new_pos.
// For each face, assign the centroid (i.e., arithmetic mean)
// of the original vertex positions to Face::new_pos. Note
// that in general, NOT all faces will be triangles!
// For each face, assign the centroid (i.e., arithmetic mean)
// of the original vertex positions to Face::new_pos. Note
// that in general, NOT all faces will be triangles!
}
/*
Compute new vertex positions for a mesh that splits each polygon
into quads (by inserting a vertex at the face midpoint and each
of the edge midpoints). The new vertex positions will be stored
in the members Vertex::new_pos, Edge::new_pos, and
Face::new_pos. The values of the positions are based on
the Catmull-Clark rules for subdivision.
Note: this will only be called on meshes without boundary
Compute new vertex positions for a mesh that splits each polygon
into quads (by inserting a vertex at the face midpoint and each
of the edge midpoints). The new vertex positions will be stored
in the members Vertex::new_pos, Edge::new_pos, and
Face::new_pos. The values of the positions are based on
the Catmull-Clark rules for subdivision.
Note: this will only be called on meshes without boundary
*/
void
Halfedge_Mesh
::
catmullclark_subdivide_positions
()
{
// The implementation for this routine should be
// a lot like Halfedge_Mesh:linear_subdivide_positions:(),
// except that the calculation of the positions themsevles is
// slightly more involved, using the Catmull-Clark subdivision
// rules. (These rules are outlined in the Developer Manual.)
// The implementation for this routine should be
// a lot like Halfedge_Mesh:linear_subdivide_positions:(),
// except that the calculation of the positions themsevles is
// slightly more involved, using the Catmull-Clark subdivision
// rules. (These rules are outlined in the Developer Manual.)
// Faces
// Faces
// Edges
// Edges
// Vertices
// Vertices
}
/*
This routine should increase the number of triangles in the mesh
using Loop subdivision. Note: this is will only be called on triangle meshes.
This routine should increase the number of triangles in the mesh
using Loop subdivision. Note: this is will only be called on triangle meshes.
*/
void
Halfedge_Mesh
::
loop_subdivide
()
{
// Compute new positions for all the vertices in the input mesh, using
// the Loop subdivision rule, and store them in Vertex::new_pos.
// -> At this point, we also want to mark each vertex as being a vertex of the
// original mesh. Use Vertex::is_new for this.
// -> Next, compute the updated vertex positions associated with edges, and
// store it in Edge::new_pos.
// -> Next, we're going to split every edge in the mesh, in any order. For
// future reference, we're also going to store some information about which
// subdivided edges come from splitting an edge in the original mesh, and
// which edges are new, by setting the flat Edge::is_new. Note that in this
// loop, we only want to iterate over edges of the original mesh.
// Otherwise, we'll end up splitting edges that we just split (and the
// loop will never end!)
// -> Now flip any new edge that connects an old and new vertex.
// -> Finally, copy the new vertex positions into final Vertex::pos.
// Each vertex and edge of the original surface can be associated with a
// vertex in the new (subdivided) surface.
// Therefore, our strategy for computing the subdivided vertex locations is to
// *first* compute the new positions
// using the connectivity of the original (coarse) mesh; navigating this mesh
// will be much easier than navigating
// the new subdivided (fine) mesh, which has more elements to traverse. We
// will then assign vertex positions in
// the new mesh based on the values we computed for the original mesh.
// Compute updated positions for all the vertices in the original mesh, using
// the Loop subdivision rule.
// Next, compute the updated vertex positions associated with edges.
// Next, we're going to split every edge in the mesh, in any order. For
// future reference, we're also going to store some information about which
// subdivided edges come from splitting an edge in the original mesh, and
// which edges are new.
// In this loop, we only want to iterate over edges of the original
// mesh---otherwise, we'll end up splitting edges that we just split (and
// the loop will never end!)
// Finally, flip any new edge that connects an old and new vertex.
// Copy the updated vertex positions to the subdivided mesh.
// Compute new positions for all the vertices in the input mesh, using
// the Loop subdivision rule, and store them in Vertex::new_pos.
// -> At this point, we also want to mark each vertex as being a vertex of the
// original mesh. Use Vertex::is_new for this.
// -> Next, compute the updated vertex positions associated with edges, and
// store it in Edge::new_pos.
// -> Next, we're going to split every edge in the mesh, in any order. For
// future reference, we're also going to store some information about which
// subdivided edges come from splitting an edge in the original mesh, and
// which edges are new, by setting the flat Edge::is_new. Note that in this
// loop, we only want to iterate over edges of the original mesh.
// Otherwise, we'll end up splitting edges that we just split (and the
// loop will never end!)
// -> Now flip any new edge that connects an old and new vertex.
// -> Finally, copy the new vertex positions into final Vertex::pos.
// Each vertex and edge of the original surface can be associated with a
// vertex in the new (subdivided) surface.
// Therefore, our strategy for computing the subdivided vertex locations is to
// *first* compute the new positions
// using the connectivity of the original (coarse) mesh; navigating this mesh
// will be much easier than navigating
// the new subdivided (fine) mesh, which has more elements to traverse. We
// will then assign vertex positions in
// the new mesh based on the values we computed for the original mesh.
// Compute updated positions for all the vertices in the original mesh, using
// the Loop subdivision rule.
// Next, compute the updated vertex positions associated with edges.
// Next, we're going to split every edge in the mesh, in any order. For
// future reference, we're also going to store some information about which
// subdivided edges come from splitting an edge in the original mesh, and
// which edges are new.
// In this loop, we only want to iterate over edges of the original
// mesh---otherwise, we'll end up splitting edges that we just split (and
// the loop will never end!)
// Finally, flip any new edge that connects an old and new vertex.
// Copy the updated vertex positions to the subdivided mesh.
}
/*
Isotropic remeshing. Note that this function returns success in a similar
manner to the local operations, except with only a boolean value.
(e.g. you may want to return false if this is not a triangle mesh)
Isotropic remeshing. Note that this function returns success in a similar
manner to the local operations, except with only a boolean value.
(e.g. you may want to return false if this is not a triangle mesh)
*/
bool
Halfedge_Mesh
::
isotropic_remesh
()
{
// Compute the mean edge length.
// Repeat the four main steps for 5 or 6 iterations
// -> Split edges much longer than the target length (being careful about
// how the loop is written!)
// -> Collapse edges much shorter than the target length. Here we need to
// be EXTRA careful about advancing the loop, because many edges may have
// been destroyed by a collapse (which ones?)
// -> Now flip each edge if it improves vertex degree
// -> Finally, apply some tangential smoothing to the vertex positions
return
false
;
// Compute the mean edge length.
// Repeat the four main steps for 5 or 6 iterations
// -> Split edges much longer than the target length (being careful about
// how the loop is written!)
// -> Collapse edges much shorter than the target length. Here we need to
// be EXTRA careful about advancing the loop, because many edges may have
// been destroyed by a collapse (which ones?)
// -> Now flip each edge if it improves vertex degree
// -> Finally, apply some tangential smoothing to the vertex positions
return
false
;
}
/* Helper type for quadric simplification */
struct
Edge_Record
{
Edge_Record
()
{}
Edge_Record
(
std
::
unordered_map
<
Halfedge_Mesh
::
VertexRef
,
Mat4
>&
vertex_quadrics
,
Halfedge_Mesh
::
EdgeRef
e
)
:
edge
(
e
)
{
// Compute the combined quadric from the edge endpoints.
// -> Build the 3x3 linear system whose solution minimizes the quadric error
// associated with these two endpoints.
// -> Use this system to solve for the optimal position, and store it in
// Edge_Record::optimal.
// -> Also store the cost associated with collapsing this edge in
// Edge_Record::cost.
Edge_Record
(
std
::
unordered_map
<
Halfedge_Mesh
::
VertexRef
,
Mat4
>
&
vertex_quadrics
,
Halfedge_Mesh
::
EdgeRef
e
)
:
edge
(
e
)
{
// Compute the combined quadric from the edge endpoints.
// -> Build the 3x3 linear system whose solution minimizes the quadric error
// associated with these two endpoints.
// -> Use this system to solve for the optimal position, and store it in
// Edge_Record::optimal.
// -> Also store the cost associated with collapsing this edge in
// Edge_Record::cost.
}
Halfedge_Mesh
::
EdgeRef
edge
;
Vec3
optimal
;
...
...
@@ -451,7 +454,7 @@ struct Edge_Record {
};
/** Helper type for quadric simplification
*
*
* A PQueue is a minimum-priority queue that
* allows elements to be both inserted and removed from the
* queue. Together, one can easily change the priority of
...
...
@@ -504,48 +507,47 @@ struct Edge_Record {
* queue.remove( item2 );
*
*/
template
<
class
T
>
struct
PQueue
{
void
insert
(
const
T
&
item
)
{
queue
.
insert
(
item
);
}
void
remove
(
const
T
&
item
)
{
template
<
class
T
>
struct
PQueue
{
void
insert
(
const
T
&
item
)
{
queue
.
insert
(
item
);
}
void
remove
(
const
T
&
item
)
{
if
(
queue
.
find
(
item
)
!=
queue
.
end
())
{
queue
.
erase
(
item
);
}
}
const
T
&
top
(
void
)
const
{
return
*
(
queue
.
begin
());
}
const
T
&
top
(
void
)
const
{
return
*
(
queue
.
begin
());
}
void
pop
(
void
)
{
queue
.
erase
(
queue
.
begin
());
}
size_t
size
()
{
return
queue
.
size
();}
size_t
size
()
{
return
queue
.
size
();
}
std
::
set
<
T
>
queue
;
};
/*
Mesh simplification. Note that this function returns success in a similar
manner to the local operations, except with only a boolean value.
(e.g. you may want to return false if you can't simplify the mesh any
further without destroying it.)
/*
Mesh simplification. Note that this function returns success in a similar
manner to the local operations, except with only a boolean value.
(e.g. you may want to return false if you can't simplify the mesh any
further without destroying it.)
*/
bool
Halfedge_Mesh
::
simplify
()
{
std
::
unordered_map
<
VertexRef
,
Mat4
>
vertex_quadrics
;
std
::
unordered_map
<
VertexRef
,
Mat4
>
vertex_quadrics
;
std
::
unordered_map
<
FaceRef
,
Mat4
>
face_quadrics
;
std
::
unordered_map
<
EdgeRef
,
Edge_Record
>
edge_records
;
PQueue
<
Edge_Record
>
edge_queue
;
// Compute initial quadrics for each face by simply writing the plane equation
// for the face in homogeneous coordinates. These quadrics should be stored
// in face_quadrics
// -> Compute an initial quadric for each vertex as the sum of the quadrics
// associated with the incident faces, storing it in vertex_quadrics
// -> Build a priority queue of edges according to their quadric error cost,
// i.e., by building an Edge_Record for each edge and sticking it in the
// queue. You may want to use the above PQueue<Edge_Record> for this.
// -> Until we reach the target edge budget, collapse the best edge. Remember
// to remove from the queue any edge that touches the collapsing edge
// BEFORE it gets collapsed, and add back into the queue any edge touching
// the collapsed vertex AFTER it's been collapsed. Also remember to assign
// a quadric to the collapsed vertex, and to pop the collapsed edge off the
// top of the queue.
return
false
;
std
::
unordered_map
<
EdgeRef
,
Edge_Record
>
edge_records
;
PQueue
<
Edge_Record
>
edge_queue
;
// Compute initial quadrics for each face by simply writing the plane equation
// for the face in homogeneous coordinates. These quadrics should be stored
// in face_quadrics
// -> Compute an initial quadric for each vertex as the sum of the quadrics
// associated with the incident faces, storing it in vertex_quadrics
// -> Build a priority queue of edges according to their quadric error cost,
// i.e., by building an Edge_Record for each edge and sticking it in the
// queue. You may want to use the above PQueue<Edge_Record> for this.
// -> Until we reach the target edge budget, collapse the best edge. Remember
// to remove from the queue any edge that touches the collapsing edge
// BEFORE it gets collapsed, and add back into the queue any edge touching
// the collapsed vertex AFTER it's been collapsed. Also remember to assign
// a quadric to the collapsed vertex, and to pop the collapsed edge off the
// top of the queue.
return
false
;
}
src/student/pathtracer.cpp
View file @
c2535f0f
#include "debug.h"
#include "../rays/pathtracer.h"
#include "../rays/samplers.h"
#include "../util/rand.h"
#include "debug.h"
namespace
PT
{
...
...
@@ -12,13 +12,13 @@ Spectrum Pathtracer::trace_pixel(size_t x, size_t y) {
Vec2
wh
((
float
)
out_w
,
(
float
)
out_h
);
// TODO (PathTracer): Task 1
// Generate a sample within the pixel with coordinates xy and return the
// incoming light using trace_ray.
// Tip: Samplers::Rect::Uniform
// Tip: you may want to use log_ray for debugging
// This currently generates a ray at the bottom left of the pixel every time.
Ray
out
=
camera
.
generate_ray
(
xy
/
wh
);
...
...
@@ -26,12 +26,12 @@ Spectrum Pathtracer::trace_pixel(size_t x, size_t y) {
return
trace_ray
(
out
);
}
Spectrum
Pathtracer
::
trace_ray
(
const
Ray
&
ray
)
{
Spectrum
Pathtracer
::
trace_ray
(
const
Ray
&
ray
)
{
// Trace ray into scene. If nothing is hit, sample the environment
Trace
hit
=
scene
.
hit
(
ray
);
if
(
!
hit
.
hit
)
{
if
(
env_light
.
has_value
())
{
if
(
!
hit
.
hit
)
{
if
(
env_light
.
has_value
())
{
return
env_light
.
value
().
sample_direction
(
ray
.
dir
);
}
return
{};
...
...
@@ -43,7 +43,7 @@ Spectrum Pathtracer::trace_ray(const Ray& ray) {
Mat4
object_to_world
=
Mat4
::
rotate_to
(
hit
.
normal
);
Mat4
world_to_object
=
object_to_world
.
T
();
Vec3
out_dir
=
world_to_object
.
rotate
(
ray
.
point
-
hit
.
position
).
unit
();
const
BSDF
&
bsdf
=
materials
[
hit
.
material
];
const
BSDF
&
bsdf
=
materials
[
hit
.
material
];
// Now we can compute the rendering equation at this point.
// We split it into two stages: sampling lighting (i.e. directly connecting
...
...
@@ -55,68 +55,71 @@ Spectrum Pathtracer::trace_ray(const Ray& ray) {
// indirect lighting components calculated in the code below. The starter
// code sets radiance_out to (0.5,0.5,0.5) so that you can test your geometry
// queries before you implement path tracing.
Spectrum
radiance_out
=
debug_data
.
normal_colors
?
Spectrum
(
0.5
f
)
:
Spectrum
::
direction
(
hit
.
normal
);
Spectrum
radiance_out
=
debug_data
.
normal_colors
?
Spectrum
(
0.5
f
)
:
Spectrum
::
direction
(
hit
.
normal
);
{
auto
sample_light
=
[
&
](
const
auto
&
light
)
{
auto
sample_light
=
[
&
](
const
auto
&
light
)
{
// If the light is discrete (e.g. a point light), then we only need
// one sample, as all samples will be equivalent
int
samples
=
light
.
is_discrete
()
?
1
:
(
int
)
n_area_samples
;
for
(
int
i
=
0
;
i
<
samples
;
i
++
)
{
for
(
int
i
=
0
;
i
<
samples
;
i
++
)
{
Light_Sample
sample
=
light
.
sample
(
hit
.
position
);
Vec3
in_dir
=
world_to_object
.
rotate
(
sample
.
direction
);
// If the light is below the horizon, ignore it
float
cos_theta
=
in_dir
.
y
;
if
(
cos_theta
<=
0.0
f
)
continue
;
if
(
cos_theta
<=
0.0
f
)
continue
;
// If the BSDF has 0 throughput in this direction, ignore it
// This is another oppritunity to do Russian roulette on low-throughput rays,
// which would allow us to skip the shadow ray cast, increasing efficiency.
Spectrum
absorbsion
=
bsdf
.
evaluate
(
out_dir
,
in_dir
);
if
(
absorbsion
.
luma
()
==
0.0
f
)
continue
;
if
(
absorbsion
.
luma
()
==
0.0
f
)
continue
;
// TODO (PathTracer): Task 4
// Construct a shadow ray and compute whether the intersected surface is
// in shadow. Only accumulate light if not in shadow.
// Tip: when making your ray, you will want to slightly offset it from the
// surface it starts on, lest it intersect at time=0. Similarly, you may want
// Tip: when making your ray, you will want to slightly offset it from the
// surface it starts on, lest it intersect at time=0. Similarly, you may want
// to limit the ray slightly before it would hit the light itself.
// Note: that along with the typical cos_theta, pdf factors, we divide by samples.
// This is because we're doing another monte-carlo estimate of the lighting from area lights.
// Note: that along with the typical cos_theta, pdf factors, we divide by samples.
// This is because we're doing another monte-carlo estimate of the lighting from
// area lights.
radiance_out
+=
(
cos_theta
/
(
samples
*
sample
.
pdf
))
*
sample
.
radiance
*
absorbsion
;
}
};
// If the BSDF is discrete (i.e. uses dirac deltas/if statements), then we are never
// going to hit the exact right direction by sampling lights, so ignore them.
if
(
!
bsdf
.
is_discrete
())
{
for
(
const
auto
&
light
:
lights
)
if
(
!
bsdf
.
is_discrete
())
{
for
(
const
auto
&
light
:
lights
)
sample_light
(
light
);
if
(
env_light
.
has_value
())
if
(
env_light
.
has_value
())
sample_light
(
env_light
.
value
());
}
}
// TODO (PathTracer): Task 5
// Compute an indirect lighting estimate using pathtracing with Monte Carlo.
// (1) Ray objects have a depth field; you should use this to avoid
// traveling down one path forever.
// (2) randomly select a new ray direction (it may be reflection or transmittence
// (2) randomly select a new ray direction (it may be reflection or transmittence
// ray depending on surface type) using bsdf.sample()
// (3) potentially terminate path (using Russian roulette). You can make this
// (3) potentially terminate path (using Russian roulette). You can make this
// a function of the bsdf attenuation or track overall ray throughput
// (4) create new scene-space ray and cast it to get incoming light
// (5) add contribution due to incoming light with proper weighting
return
radiance_out
;
return
radiance_out
;
}
}
}
// namespace PT
Prev
1
2
3
4
5
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment