cgv
Loading...
Searching...
No Matches
volume_renderer.cxx
1#include "volume_renderer.h"
2#include <random>
3#include <cgv/math/ftransform.h>
4#include <cgv_gl/gl/gl.h>
5#include <cgv_gl/gl/gl_tools.h>
6
7namespace cgv {
8 namespace render {
9 volume_renderer& ref_volume_renderer(context& ctx, int ref_count_change)
10 {
11 static int ref_count = 0;
12 static volume_renderer r;
13 r.manage_singleton(ctx, "volume_renderer", ref_count, ref_count_change);
14 return r;
15 }
16
18 {
21
22 unsigned size = 64;
23 std::vector<uint8_t> noise_data(size*size);
24
25 std::default_random_engine rng(42);
26 std::uniform_int_distribution<unsigned> dist(0u, 255u);
27
28 for(size_t i = 0; i < noise_data.size(); ++i)
29 noise_data[i] = static_cast<uint8_t>(dist(rng));
30
32 noise_texture.create(ctx, dv, 0);
33 }
34
36 {
37 // validate set attributes
38 const volume_render_style& vrs = get_style<volume_render_style>();
39 bool res = renderer::validate_attributes(ctx);
40 res = res && (volume_texture != nullptr);
41 return res;
42 }
44 {
45 const volume_render_style& vrs = get_style<volume_render_style>();
46
47 options.define_macro_if_not_default("NUM_STEPS", vrs.integration_quality, VolumeIntegrationQuality::k128);
49 options.define_macro_if_true(vrs.enable_noise_offset, "ENABLE_NOISE_OFFSET");
50 options.define_macro_if_not_default("SHOW_VOLUME", vrs.show_volume, true);
51 options.define_macro_if_true(vrs.enable_lighting, "ENABLE_LIGHTING");
52 options.define_macro_if_true(vrs.use_gradient_texture, "USE_GRADIENT_TEXTURE");
53 options.define_macro_if_true(vrs.enable_gradient_modulation, "ENABLE_GRADIENT_MODULATION");
54 options.define_macro_if_true(vrs.enable_depth_test, "ENABLE_DEPTH_TEST");
55
56 options.define_macro_if_not_default("ISOSURFACE_MODE", vrs.isosurface_mode, VolumeIsosurfaceMode::kNone);
57 options.define_macro_if_not_default("ISOSURFACE_COLOR_MODE", vrs.isosurface_color_from_transfer_function, false);
58
59 options.define_macro_if_not_default("SLICE_MODE", vrs.slice_mode, VolumeSliceMode::kNone);
60
61 options.define_macro_if_not_default("COMPOSITING_MODE", vrs.compositing_mode, VolumeCompositingMode::kBlend);
63 options.define_macro_if_not_default("TRANSFER_FUNCTION_SAMPLER_DIMENSIONS", transfer_function_texture->get_nr_dimensions(), 1u);
64 }
66 {
67 bool res = renderer::init(ctx);
68 res = res && position_aam.init(ctx);
69 enable_attribute_array_manager(ctx, position_aam);
70
71 // use a single optimized triangle strip to define a cube
72 std::vector<vec3> positions = {
73 { -0.5f, +0.5f, -0.5f },
74 { +0.5f, +0.5f, -0.5f },
75 { -0.5f, -0.5f, -0.5f },
76 { +0.5f, -0.5f, -0.5f },
77 { +0.5f, -0.5f, +0.5f },
78 { +0.5f, +0.5f, -0.5f },
79 { +0.5f, +0.5f, +0.5f },
80 { -0.5f, +0.5f, -0.5f },
81 { -0.5f, +0.5f, +0.5f },
82 { -0.5f, -0.5f, -0.5f },
83 { -0.5f, -0.5f, +0.5f },
84 { +0.5f, -0.5f, +0.5f },
85 { -0.5f, +0.5f, +0.5f },
86 { +0.5f, +0.5f, +0.5 }
87 };
88 set_position_array(ctx, positions);
89
92
93 return res;
94 }
95 void volume_renderer::clear (const context& ctx) {
96 disable_attribute_array_manager(ctx, position_aam);
97 position_aam.destruct(ctx);
99 renderer::clear(ctx); // perform upstream actions
100 }
101
103 if(!tex || tex->get_nr_dimensions() != 3)
104 return false;
105 volume_texture = tex;
106 return true;
107 }
108
110 if(!tex || tex->get_nr_dimensions() > 2)
111 return false;
113 return true;
114 }
115
117 if(!tex || tex->get_nr_dimensions() != 3)
118 return false;
119 gradient_texture = tex;
120 return true;
121 }
122
124 if(!tex || tex->get_nr_dimensions() != 2)
125 return false;
126 depth_texture = tex;
127 return true;
128 }
129
130 void volume_renderer::set_bounding_box(const box3& bbox)
131 {
132 bounding_box = bbox;
133 }
134
135 void volume_renderer::transform_to_bounding_box(bool flag)
136 {
138 }
139
140 void volume_renderer::set_noise_offset(const vec2& offset)
141 {
142 noise_offset = offset;
143 }
144
146 const volume_render_style& vrs = get_style<volume_render_style>();
147 if(!renderer::enable(ctx))
148 return false;
149 int vp[4];
150 glGetIntegerv(GL_VIEWPORT, vp);
151 ref_prog().set_uniform(ctx, "viewport_dims", vec2(float(vp[2] - vp[0]), float(vp[3] - vp[1])));
152 ref_prog().set_uniform(ctx, "noise_offset", noise_offset);
153
154 ref_prog().set_uniform(ctx, "picking_opacity_threshold", vrs.picking_opacity_threshold);
155 ref_prog().set_uniform(ctx, "scale_adjustment_factor", vrs.scale_adjustment_factor);
156
157 ref_prog().set_uniform(ctx, "light_local_to_eye", vrs.light_local_to_eye);
158 ref_prog().set_uniform(ctx, "light_direction", normalize(vrs.light_direction));
159 float specular_exponent = cgv::math::lerp(128.0f, 1.0f, cgv::math::clamp(vrs.roughness, 0.0f, 1.0f));
160 ref_prog().set_uniform(ctx, "ambient_strength", vrs.ambient_strength);
161 ref_prog().set_uniform(ctx, "diffuse_strength", vrs.diffuse_strength);
162 ref_prog().set_uniform(ctx, "specular_strength", vrs.specular_strength);
163 ref_prog().set_uniform(ctx, "specular_exponent", specular_exponent);
164 ref_prog().set_uniform(ctx, "specular_color_mix", vrs.specular_color_mix);
165
166 ref_prog().set_uniform(ctx, "gradient_lambda", std::max(vrs.gradient_lambda, 0.001f));
167
168 ref_prog().set_uniform(ctx, "isovalue", vrs.isovalue);
169 ref_prog().set_uniform(ctx, "isosurface_color", vrs.isosurface_color);
170
171 ref_prog().set_uniform(ctx, "slice_axis", vrs.slice_axis);
172 ref_prog().set_uniform(ctx, "slice_coordinate", vrs.slice_coordinate);
173 ref_prog().set_uniform(ctx, "slice_opacity", vrs.slice_opacity);
174
175 ref_prog().set_uniform(ctx, "clip_box_min", vrs.clip_box.get_min_pnt());
176
177 ref_prog().set_uniform(ctx, "clip_box_max", vrs.clip_box.get_max_pnt());
178
180 ctx.set_depth_func(CF_ALWAYS);
181 ctx.push_blend_state();
182 ctx.enable_blending();
184 ctx.push_cull_state();
185 ctx.set_cull_state(CM_FRONTFACE);
186
189 noise_texture.enable(ctx, 2);
192 return true;
193 }
210 void volume_renderer::draw(context& ctx, size_t start, size_t count, bool use_strips, bool use_adjacency, uint32_t strip_restart_index)
211 {
213
215 const volume_render_style& vrs = get_style<volume_render_style>();
216 cgv::box3 clipped_box(
219 );
220 ctx.mul_modelview_matrix(cgv::math::translate4(clipped_box.get_center()));
221 ctx.mul_modelview_matrix(cgv::math::scale4(clipped_box.get_extent()));
222 }
223
224 glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)14);
225
227 }
228 }
229}
230
231#include <cgv/gui/provider.h>
232
233namespace cgv {
234 namespace gui {
235
238 bool create(provider* p, const std::string& label,
239 void* value_ptr, const std::string& value_type,
240 const std::string& gui_type, const std::string& options, bool*) {
242 return false;
243 cgv::render::volume_render_style* vrs_ptr = reinterpret_cast<cgv::render::volume_render_style*>(value_ptr);
244 cgv::base::base* b = dynamic_cast<cgv::base::base*>(p);
245
246 p->add_member_control(b, "Quality", vrs_ptr->integration_quality, "dropdown", "w=114;enums='8=8,16=16,32=32,64=64,128=128,256=256,512=512,1024=1024,2048=2048,4096=4096'", " ");
247 p->add_member_control(b, "Use Noise", vrs_ptr->enable_noise_offset, "check", "w=74");
248 p->add_member_control(b, "Interpolation", vrs_ptr->interpolation_mode, "dropdown", "enums=Nearest,Smoothed,Linear,Cubic");
249
250 p->add_member_control(b, "Depth Test", vrs_ptr->enable_depth_test, "check");
251 p->add_member_control(b, "Opacity Threshold", vrs_ptr->picking_opacity_threshold, "value_slider", "min=0.0;max=1.0;step=0.001;tooltip='Opacity threshold used for focus picking'");
252
253 p->add_member_control(b, "Show Volume", vrs_ptr->show_volume, "toggle");
254 p->add_member_control(b, "Compositing Mode", vrs_ptr->compositing_mode, "dropdown", "enums='Maximum Intensity Projection, Average, Blend'");
255
256 p->add_member_control(b, "Scale Adjustment", vrs_ptr->scale_adjustment_factor, "value_slider", "min=0.0;step=0.001;max=1000.0;log=true;ticks=true");
257
258 if(p->begin_tree_node("Lighting", vrs_ptr->enable_lighting, false)) {
259 p->align("/a");
260 p->add_member_control(b, "Enable", vrs_ptr->enable_lighting, "check", "w=88", " ");
261 p->add_member_control(b, "Local to Eye", vrs_ptr->light_local_to_eye, "check", "w=100");
262 p->add_member_control(b, "Normals from Gradient Texture", vrs_ptr->use_gradient_texture, "check");
263 p->add_member_control(b, "Direction", vrs_ptr->light_direction[0], "value", "w=58;min=-1;max=1", " ");
264 p->add_member_control(b, "", vrs_ptr->light_direction[1], "value", "w=58;min=-1;max=1", " ");
265 p->add_member_control(b, "", vrs_ptr->light_direction[2], "value", "w=58;min=-1;max=1");
266 p->add_member_control(b, "", vrs_ptr->light_direction[0], "slider", "w=58;min=-1;max=1;step=0.0001;ticks=true", " ");
267 p->add_member_control(b, "", vrs_ptr->light_direction[1], "slider", "w=58;min=-1;max=1;step=0.0001;ticks=true", " ");
268 p->add_member_control(b, "", vrs_ptr->light_direction[2], "slider", "w=58;min=-1;max=1;step=0.0001;ticks=true");
269
270 p->add_decorator("Light Parameters", "heading", "level=3");
271 p->add_member_control(b, "Ambient", vrs_ptr->ambient_strength, "value_slider", "min=0;max=1;step=0.001;ticks=true");
272 p->add_member_control(b, "Diffuse", vrs_ptr->diffuse_strength, "value_slider", "min=0;max=1;step=0.001;ticks=true");
273 p->add_member_control(b, "Specular", vrs_ptr->specular_strength, "value_slider", "min=0;max=1;step=0.001;ticks=true");
274 p->add_member_control(b, "Roughness", vrs_ptr->roughness, "value_slider", "min=0;max=1;step=0.001;ticks=true");
275 p->add_member_control(b, "Specular Color", vrs_ptr->specular_color_mix, "value_slider", "min=0;max=1;step=0.001;ticks=true");
276 p->align("/b");
277 p->end_tree_node(vrs_ptr->enable_lighting);
278 }
279
280 if(p->begin_tree_node("Gradient Modulation", vrs_ptr->enable_gradient_modulation, false)) {
281 p->align("\a");
282 p->add_member_control(b, "Enable", vrs_ptr->enable_gradient_modulation, "check");
283 p->add_member_control(b, "Lambda", vrs_ptr->gradient_lambda, "value_slider", "min=0.001;max=10;step=0.001;ticks=true;log=true");
284 p->align("\b");
286 }
287 if (p->begin_tree_node("Orthogonal Slicing", vrs_ptr->slice_mode, false)) {
288 p->align("\a");
289 p->add_member_control(b, "Mode", vrs_ptr->slice_mode, "dropdown", "enums='Disabled,Opaque,Transparent'");
290 p->add_member_control(b, "Axis", vrs_ptr->slice_axis, "value_slider", "min=0;max=2;ticks=true");
291 p->add_member_control(b, "Coordinate", vrs_ptr->slice_coordinate, "value_slider", "min=0;max=1;ticks=true");
292 p->add_member_control(b, "Opacity", vrs_ptr->slice_opacity, "value_slider", "min=0;max=1;ticks=true");
293 p->align("\b");
294 p->end_tree_node(vrs_ptr->slice_mode);
295 }
296
297 if(p->begin_tree_node("Isosurface (Blend only)", vrs_ptr->isosurface_mode, false)) {
298 p->align("\a");
299 p->add_member_control(b, "", vrs_ptr->isosurface_mode, "dropdown", "enums='Disabled,Isovalue,Alpha Threshold'");
300
301 p->add_member_control(b, "Value", vrs_ptr->isovalue, "value_slider", "min=0.0;max=1.0;step=0.001;ticks=true");
302 p->add_member_control(b, "Color", vrs_ptr->isosurface_color, "", "w=42", " ");
303 p->add_member_control(b, "From Transfer Function", vrs_ptr->isosurface_color_from_transfer_function, "check", "w=146");
304 p->align("\b");
305 p->end_tree_node(vrs_ptr->isosurface_mode);
306 }
307
308 if(p->begin_tree_node("Clip Box", vrs_ptr->clip_box, false)) {
309 p->align("\a");
310 p->add_gui("Clipping Box", vrs_ptr->clip_box, "",
311 "options='w=100;min=0;max=1;step=0.01;ticks=true;align=\"BL\"';align_col=' '");
312 p->align("\b");
313 p->end_tree_node(vrs_ptr->clip_box);
314 }
315
316 return true;
317 }
318 };
319
320#include "gl/lib_begin.h"
321
322 CGV_API cgv::gui::gui_creator_registration<volume_render_style_gui_creator> volume_rs_gc_reg("volume_render_style_gui_creator");
323
324 }
325}
base class for all classes that can be registered with support for dynamic properties (see also secti...
Definition base.h:75
A data_format describes a multidimensional data block of data entries.
Definition data_format.h:17
unsigned get_nr_dimensions() const
return the number of dimensions of the data set
the data view gives access to a data array of one, two, three or four dimensions.
Definition data_view.h:153
helper template for registration of gui creators
Definition gui_creator.h:32
derive from this class to provide a gui to the current viewer
Definition provider.h:64
bool add_gui(const std::string &label, T &value, const std::string &gui_type="", const std::string &options="")
Add a composed gui of the given gui_type for the given value.
Definition provider.h:247
cgv::base::base_ptr add_decorator(const std::string &label, const std::string &decorator_type, const std::string &options="", const std::string &align="\n")
add a newly created decorator to the group
Definition provider.cxx:168
void align(const std::string &_align)
send pure alignment information
Definition provider.cxx:36
bool begin_tree_node(const std::string &label, const T &value, bool initial_visibility=false, const std::string &options="", gui_group_ptr ggp=gui_group_ptr())
Begin a sub tree of a tree structured gui.
Definition provider.h:212
data::ref_ptr< control< T > > add_member_control(cgv::base::base *base_ptr, const std::string &label, T &value, const std::string &gui_type="", const std::string &options="", const std::string &align="\n")
add control with callback to cgv::base::on_set method on cgv::gui::control::value_change
Definition provider.h:137
void end_tree_node(const T &value)
template specialization that allows to specify value reference plus node_instance by using the result...
Definition provider.h:222
fpnt_type get_center() const
return the center of the box
fvec_type get_extent() const
return a vector with the extents in the different dimensions
const fpnt_type & get_max_pnt() const
return a const reference to corner 7
const fpnt_type & get_min_pnt() const
return a const reference to corner 0
base class for all drawables, which is independent of the used rendering API.
Definition context.h:670
void set_blend_func_back_to_front()
set the default blend function for back to front blending (source = BF_SRC_ALPHA, destination = BF_ON...
Definition context.cxx:1896
virtual void mul_modelview_matrix(const dmat4 &MV)
multiply given matrix from right to current modelview matrix
Definition context.cxx:1952
void push_depth_test_state()
push a copy of the current depth test state onto the stack saved attributes: depth test enablement,...
Definition context.cxx:1813
void push_blend_state()
push a copy of the current blend state onto the stack saved attributes: blend enablement,...
Definition context.cxx:1860
void pop_blend_state()
pop the top of the current culling state from the stack
Definition context.cxx:1864
void pop_depth_test_state()
pop the top of the current depth test state from the stack
Definition context.cxx:1817
void push_cull_state()
push a copy of the current culling state onto the stack saved attributes: cull face enablement,...
Definition context.cxx:1843
virtual void set_cull_state(CullingMode culling_mode)
set the culling state
Definition context.cxx:1856
virtual void set_depth_func(CompareFunction func)
set the depth test function
Definition context.cxx:1830
virtual void enable_blending()
enable blending
Definition context.cxx:1900
void pop_cull_state()
pop the top of the current culling state from the stack
Definition context.cxx:1847
void pop_modelview_matrix()
see push_V for an explanation
Definition context.cxx:1958
void push_modelview_matrix()
push the current viewing matrix onto a matrix stack for viewing matrices.
Definition context.cxx:1946
virtual bool is_created() const
return whether component has been created
Definition context.cxx:2226
void manage_singleton(context &ctx, const std::string &renderer_name, int &ref_count, int ref_count_change)
used by derived classes to manage singletons
Definition renderer.cxx:13
virtual bool enable(context &ctx)
enables renderer
Definition renderer.cxx:206
virtual bool init(context &ctx)
call init() once before using renderer
Definition renderer.cxx:175
shader_program & ref_prog()
derived renderer classes have access to shader program
Definition renderer.h:129
virtual void enable_attribute_array_manager(const context &ctx, attribute_array_manager &aam)
call this before setting attribute arrays to manage attribute array in given manager
Definition renderer.cxx:56
virtual void clear(const context &ctx)
the clear function destructs the shader program
Definition renderer.cxx:351
virtual void disable_attribute_array_manager(const context &ctx, attribute_array_manager &aam)
call this after last render/draw call to ensure that no other users of renderer change attribute arra...
Definition renderer.cxx:65
void set_position_array(const context &ctx, const std::vector< T > &positions)
templated method to set the position attribute from a vector of positions of type T
Definition renderer.h:231
virtual bool validate_attributes(const context &ctx) const
call to validate, whether essential position attribute is defined
Definition renderer.cxx:132
virtual bool disable(context &ctx)
disable renderer
Definition renderer.cxx:232
Stores preprocessor options used for conditionally compiling shader programs.
Definition shader_code.h:73
void define_macro_if_true(bool predicate, const std::string &identifier)
Conditionally define identifier as a macro with replacement text 1.
void define_macro_if_not_default(const std::string &identifier, const T &value, const T &default_value)
Conditionally define identifier as a macro with value as replacement text.
bool set_uniform(const context &ctx, const std::string &name, const T &value, bool generate_error=false)
Set the value of a uniform by name, where the type can be any of int, unsigned, float,...
the texture class encapsulates all functionality independent of the rendering api.
Definition texture.h:15
bool create(const context &ctx, TextureType _tt=TT_UNDEF, unsigned width=-1, unsigned height=-1, unsigned depth=-1)
create the texture of dimension and resolution specified in the data format base class.
Definition texture.cxx:214
bool disable(const context &ctx)
disable texture and restore state from before last enable call
Definition texture.cxx:752
bool enable(const context &ctx, int tex_unit=-1)
enable this texture in the given texture unit, -1 corresponds to the current unit.
Definition texture.cxx:741
bool destruct(const context &ctx)
destruct the texture and free texture memory and handle
Definition texture.cxx:726
renderer that supports point splatting
texture * gradient_texture
the 3D texture containing vector gradients used for lighting normal calculation
bool validate_attributes(const context &ctx) const override
call to validate, whether essential position attribute is defined
bool set_gradient_texture(texture *tex)
sets the gradient texture used for lighting
void clear(const context &ctx) override
clean up
bool init(context &ctx) override
construct shader programs and return whether this was successful, call inside of init method of drawa...
texture * depth_texture
a 2D texture from a frame buffer depth buffer used to combine volume rendering with opaque geometry
texture * transfer_function_texture
the 2D transfer function texture used for classification of the volume values
void init_noise_texture(context &ctx)
initializes the noise texture with random values
bool enable(context &ctx) override
enables renderer
void draw(context &ctx, size_t start, size_t count, bool use_strips=false, bool use_adjacency=false, uint32_t strip_restart_index=-1) override
Draw a range of vertices or indexed elements.
bool apply_bounding_box_transformation
whether to translate and scale the volume to the given bounding box during rendering
bool set_transfer_function_texture(texture *tex)
sets the transfer function used for classification; must be 1D or 2D (as loaded from an image)
vec2 noise_offset
offset applied to the noise texture (can be used in conjunction with temporal anti aliasing)
bool set_volume_texture(texture *tex)
sets the 3D volume texture containing scalar values (density or other measured quantities)
bool set_depth_texture(texture *tex)
sets the depth texture needed for rendering with additional opaque geometry
texture * volume_texture
the 3D texture used for rendering
texture noise_texture
a 2D texture containing random noise used to offset ray start positions in order to reduce ring artif...
box3 bounding_box
the bounding box of the volume in scene units
void update_shader_program_options(shader_compile_options &options) const override
update shader program compile options based on render style
bool disable(context &ctx) override
disable renderer
@ CF_R
undefinded format with no component
@ kLinear
modification of the built-in trilinear interpolation to prevent triangular artifacts (results look bl...
volume_renderer & ref_volume_renderer(context &ctx, int ref_count_change)
reference to a singleton volume renderer that is shared among drawables
@ TI_UINT8
signed integer stored in 64 bits
Definition type_id.h:23
this header is dependency free
Definition print.h:11
cgv::math::fvec< float, 2 > vec2
declare type of 2d single precision floating point vectors
Definition fvec.h:692
interface for gui creators
Definition gui_creator.h:14
bool create(provider *p, const std::string &label, void *value_ptr, const std::string &value_type, const std::string &gui_type, const std::string &options, bool *)
attempt to create a gui and return whether this was successful
bool light_local_to_eye
whether the light is local to the eye position (moves with the eye) or is static to the scene
bool enable_noise_offset
whether to use the noise texture to offset ray start positions in order to reduce sampling artifacts
bool enable_lighting
whether to enable lighting
bool show_volume
whether to show direct volume
float specular_strength
material specular component strength
float ambient_strength
light ambient component strength
VolumeIsosurfaceMode isosurface_mode
mode of a single supported isosurface
rgb isosurface_color
the default constant isosurface color
bool enable_depth_test
whether to enable depth testing by reading depth from a texture to allow geometry intersecting the vo...
float isovalue
the value used to check for an isosurface
float gradient_lambda
influence scale for gradient-based opacity modulation
VolumeSliceMode slice_mode
mode of slice rendering
bool isosurface_color_from_transfer_function
whether to color the isosurface based on the transfer function
float scale_adjustment_factor
the coefficient used to adjust sample opacity based on volume scaling (useful range between 50 and 50...
VolumeInterpolationMode interpolation_mode
the interpolation method used (supplied volume texture should be set to GL_LINEAR)
float slice_opacity
in case of transparent mode, slice opacity
VolumeIntegrationQuality integration_quality
quality measure for the number of steps used during ray marching
float specular_color_mix
material specular color mix factor (0 = color from transfer function, 1 = pure white)
vec3 light_direction
the direction of the directional light
float roughness
material roughness (inversely proportional to specular shininess)
bool use_gradient_texture
whether to use a supplied gradient texture or compute gradients on the fly via central differences (d...
VolumeCompositingMode compositing_mode
the compositing mode used
int slice_axis
coordinate axis orthogonal to which slice is rendered
float diffuse_strength
material diffuse component strength
bool enable_gradient_modulation
whether to enable modulating the volume opacity by the gradient magnitude
box3 clip_box
a bounding box used to define a subspace of the volume to be visualized
float slice_coordinate
coordinate value along axis defining slice in range [0,1]
traits class with a static function get_name() of type const char* that returns the type name of the ...
Definition type_name.h:54