OSL::Matrix44 ShadingPoint::OSLObjectTransformInfo::get_inverse_transform(float t) const { const Transformd assembly_xform = m_assembly_instance_transform->evaluate(t); const Transformd::MatrixType m( m_object_instance_transform->get_parent_to_local() * assembly_xform.get_parent_to_local()); return Matrix4f(transpose(m)); }
double get_autofocus_focal_distance(const Intersector& intersector) const { // The autofocus considers the scene at the middle of the shutter interval. const float time = get_shutter_middle_time(); const Transformd transform = m_transform_sequence.evaluate(time); // Create a ray that goes through the center of the lens. ShadingRay ray; ray.m_org = transform.get_local_to_parent().extract_translation(); ray.m_dir = normalize(transform.vector_to_parent(-ndc_to_camera(m_autofocus_target))); ray.m_tmin = 0.0; ray.m_tmax = numeric_limits<double>::max(); ray.m_time = ShadingRay::Time::create_with_normalized_time( 0.5f, get_shutter_open_time(), get_shutter_close_time()); ray.m_flags = VisibilityFlags::ProbeRay; ray.m_depth = 0; // Trace the ray. ShadingPoint shading_point; intersector.trace(ray, shading_point); if (shading_point.hit()) { // Hit: compute the focal distance. const Vector3d v = shading_point.get_point() - ray.m_org; const double af_focal_distance = -transform.vector_to_local(v).z; RENDERER_LOG_INFO( "camera \"%s\": autofocus sets focal distance to %f (using camera position at time=%.1f).", get_path().c_str(), af_focal_distance, ray.m_time.m_absolute); return af_focal_distance; } else { // Miss: focus at infinity. RENDERER_LOG_INFO( "camera \"%s\": autofocus sets focal distance to infinity (using camera position at time=%.1f).", get_path().c_str(), ray.m_time.m_absolute); return 1.0e38; } }
double get_autofocus_focal_distance(const Intersector& intersector) const { // The autofocus considers the scene at the middle of the shutter interval. const double time = get_shutter_middle_time(); const Transformd transform = m_transform_sequence.evaluate(time); // Compute the camera space coordinates of the focus point. const Vector3d film_point = ndc_to_camera(m_autofocus_target); // Create a ray in world space. ShadingRay ray; ray.m_org = transform.point_to_parent(Vector3d(0.0)); ray.m_dir = transform.point_to_parent(film_point) - ray.m_org; ray.m_tmin = 0.0; ray.m_tmax = numeric_limits<double>::max(); ray.m_time = time; ray.m_type = ShadingRay::ProbeRay; ray.m_depth = 0; // Trace the ray. ShadingPoint shading_point; intersector.trace(ray, shading_point); if (shading_point.hit()) { // Hit: compute the focal distance. const Vector3d v = shading_point.get_point() - ray.m_org; const double af_focal_distance = -transform.vector_to_local(v).z; RENDERER_LOG_INFO( "camera \"%s\": autofocus sets focal distance to %f (using camera position at time=%.1f).", get_name(), af_focal_distance, ray.m_time); return af_focal_distance; } else { // Miss: focus at infinity. RENDERER_LOG_INFO( "camera \"%s\": autofocus sets focal distance to infinity (using camera position at time=%.1f).", get_name(), ray.m_time); return 1.0e38; } }
void compute_radiance( InputEvaluator& input_evaluator, const Transformd& light_transform, const Vector3d& axis, const Vector3d& outgoing, Spectrum& radiance) const { const Vector3d up = light_transform.vector_to_parent(m_up); const Vector3d v = -axis; const Vector3d u = normalize(cross(up, v)); const Vector3d n = cross(v, u); const double cos_theta = dot(outgoing, axis); assert(cos_theta > m_cos_outer_half_angle); const Vector3d d = outgoing / cos_theta - axis; const float x = static_cast<float>(dot(d, u) * m_rcp_screen_half_size); const float y = static_cast<float>(dot(d, n) * m_rcp_screen_half_size); const Vector2f uv(0.5f * (x + 1.0f), 0.5f * (y + 1.0f)); const InputValues* values = input_evaluator.evaluate<InputValues>(m_inputs, uv); radiance = values->m_intensity; radiance *= static_cast<float>(values->m_intensity_multiplier); if (cos_theta < m_cos_inner_half_angle) { radiance *= static_cast<float>( smoothstep(m_cos_outer_half_angle, m_cos_inner_half_angle, cos_theta)); } }
void sample_disk( const Transformd& light_transform, const Vector2d& s, const Vector3d& disk_center, const double disk_radius, Vector3d& position, Vector3d& outgoing, Spectrum& value, double& probability) const { outgoing = -normalize(light_transform.get_parent_z()); const Basis3d basis(outgoing); const Vector2d p = sample_disk_uniform(s); position = disk_center - m_safe_scene_diameter * basis.get_normal() + disk_radius * p[0] * basis.get_tangent_u() + disk_radius * p[1] * basis.get_tangent_v(); probability = 1.0 / (Pi * disk_radius * disk_radius); compute_sun_radiance( outgoing, m_values.m_turbidity, m_values.m_radiance_multiplier, value); value *= SunSolidAngle; }
void add_cube( Assembly& assembly, const size_t ix, const size_t iy, const double fx, const double fz, const Color3b& color, const Transformd& transform) override { // Push vertices. const size_t base_vertex_index = m_mesh->get_vertex_count(); for (size_t i = 0; i < m_cube->get_vertex_count(); ++i) { m_mesh->push_vertex( transform.point_to_parent(m_cube->get_vertex(i))); } // Push normals. const size_t base_vertex_normal_index = m_mesh->get_vertex_normal_count(); for (size_t i = 0; i < m_cube->get_vertex_normal_count(); ++i) { m_mesh->push_vertex_normal( normalize( transform.normal_to_parent(m_cube->get_vertex_normal(i)))); } // Push texture coordinates. const size_t tex_coords_index = m_mesh->push_tex_coords( GVector2(static_cast<float>(fx), static_cast<float>(1.0 - fz))); // Push triangles. for (size_t i = 0; i < m_cube->get_triangle_count(); ++i) { Triangle triangle = m_cube->get_triangle(i); triangle.m_v0 += static_cast<uint32>(base_vertex_index); triangle.m_v1 += static_cast<uint32>(base_vertex_index); triangle.m_v2 += static_cast<uint32>(base_vertex_index); triangle.m_n0 += static_cast<uint32>(base_vertex_normal_index); triangle.m_n1 += static_cast<uint32>(base_vertex_normal_index); triangle.m_n2 += static_cast<uint32>(base_vertex_normal_index); triangle.m_a0 = triangle.m_a1 = triangle.m_a2 = static_cast<uint32>(tex_coords_index); m_mesh->push_triangle(triangle); } }
bool TransformSequence::swaps_handedness(const Transformd& xform) const { if (!m_can_swap_handedness) return false; if (m_all_swap_handedness) return true; return xform.swaps_handedness(); }
Vector3d compute_ray_direction( const Vector2d& film_point, // NDC const Vector3d& lens_point, // world space const Transformd& transform) const { // Compute film point in camera space. const Vector3d film_point_cs = ndc_to_camera(film_point); // Compute focal point in world space. const Vector3d focal_point = transform.point_to_parent(-m_focal_ratio * film_point_cs); // Return ray direction in world space. return normalize(focal_point - lens_point); }
void LightSampler::collect_emitting_triangles( const Assembly& assembly, const AssemblyInstance& assembly_instance) { // Loop over the object instances of the assembly. const size_t object_instance_count = assembly.object_instances().size(); for (size_t object_instance_index = 0; object_instance_index < object_instance_count; ++object_instance_index) { // Retrieve the object instance. const ObjectInstance* object_instance = assembly.object_instances().get_by_index(object_instance_index); // Retrieve the materials of the object instance. const MaterialArray& front_materials = object_instance->get_front_materials(); const MaterialArray& back_materials = object_instance->get_back_materials(); // Skip object instances without light-emitting materials. if (!has_emitting_materials(front_materials) && !has_emitting_materials(back_materials)) continue; // Compute the object space to world space transformation. // todo: add support for moving light-emitters. const Transformd& object_instance_transform = object_instance->get_transform(); const Transformd assembly_instance_transform = assembly_instance.transform_sequence().empty() ? Transformd::identity() : assembly_instance.transform_sequence().earliest_transform(); const Transformd global_transform = assembly_instance_transform * object_instance_transform; // Retrieve the object. Object& object = object_instance->get_object(); // Retrieve the region kit of the object. Access<RegionKit> region_kit(&object.get_region_kit()); // Loop over the regions of the object. const size_t region_count = region_kit->size(); for (size_t region_index = 0; region_index < region_count; ++region_index) { // Retrieve the region. const IRegion* region = (*region_kit)[region_index]; // Retrieve the tessellation of the region. Access<StaticTriangleTess> tess(®ion->get_static_triangle_tess()); // Loop over the triangles of the region. const size_t triangle_count = tess->m_primitives.size(); for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index) { // Fetch the triangle. const Triangle& triangle = tess->m_primitives[triangle_index]; // Skip triangles without a material. if (triangle.m_pa == Triangle::None) continue; // Fetch the materials assigned to this triangle. const size_t pa_index = static_cast<size_t>(triangle.m_pa); const Material* front_material = pa_index < front_materials.size() ? front_materials[pa_index] : 0; const Material* back_material = pa_index < back_materials.size() ? back_materials[pa_index] : 0; // Skip triangles that don't emit light. if ((front_material == 0 || front_material->get_uncached_edf() == 0) && (back_material == 0 || back_material->get_uncached_edf() == 0)) continue; // Retrieve object instance space vertices of the triangle. const GVector3& v0_os = tess->m_vertices[triangle.m_v0]; const GVector3& v1_os = tess->m_vertices[triangle.m_v1]; const GVector3& v2_os = tess->m_vertices[triangle.m_v2]; // Transform triangle vertices to assembly space. const GVector3 v0_as = object_instance_transform.point_to_parent(v0_os); const GVector3 v1_as = object_instance_transform.point_to_parent(v1_os); const GVector3 v2_as = object_instance_transform.point_to_parent(v2_os); // Compute the support plane of the hit triangle in assembly space. const GTriangleType triangle_geometry(v0_as, v1_as, v2_as); TriangleSupportPlaneType triangle_support_plane; triangle_support_plane.initialize(TriangleType(triangle_geometry)); // Transform triangle vertices to world space. const Vector3d v0(assembly_instance_transform.point_to_parent(v0_as)); const Vector3d v1(assembly_instance_transform.point_to_parent(v1_as)); const Vector3d v2(assembly_instance_transform.point_to_parent(v2_as)); // Compute the geometric normal to the triangle and the area of the triangle. Vector3d geometric_normal = cross(v1 - v0, v2 - v0); const double geometric_normal_norm = norm(geometric_normal); if (geometric_normal_norm == 0.0) continue; const double rcp_geometric_normal_norm = 1.0 / geometric_normal_norm; const double rcp_area = 2.0 * rcp_geometric_normal_norm; const double area = 0.5 * geometric_normal_norm; geometric_normal *= rcp_geometric_normal_norm; assert(is_normalized(geometric_normal)); // Retrieve object instance space vertex normals. const GVector3& n0_os = tess->m_vertex_normals[triangle.m_n0]; const GVector3& n1_os = tess->m_vertex_normals[triangle.m_n1]; const GVector3& n2_os = tess->m_vertex_normals[triangle.m_n2]; // Transform vertex normals to world space. const Vector3d n0(normalize(global_transform.normal_to_parent(n0_os))); const Vector3d n1(normalize(global_transform.normal_to_parent(n1_os))); const Vector3d n2(normalize(global_transform.normal_to_parent(n2_os))); for (size_t side = 0; side < 2; ++side) { const Material* material = side == 0 ? front_material : back_material; const Vector3d side_geometric_normal = side == 0 ? geometric_normal : -geometric_normal; const Vector3d side_n0 = side == 0 ? n0 : -n0; const Vector3d side_n1 = side == 0 ? n1 : -n1; const Vector3d side_n2 = side == 0 ? n2 : -n2; // Skip sides without a material. if (material == 0) continue; const EDF* edf = material->get_uncached_edf(); // Skip sides without a light-emitting material. if (edf == 0) continue; // Create a light-emitting triangle. EmittingTriangle emitting_triangle; emitting_triangle.m_assembly_instance = &assembly_instance; emitting_triangle.m_object_instance_index = object_instance_index; emitting_triangle.m_region_index = region_index; emitting_triangle.m_triangle_index = triangle_index; emitting_triangle.m_v0 = v0; emitting_triangle.m_v1 = v1; emitting_triangle.m_v2 = v2; emitting_triangle.m_n0 = side_n0; emitting_triangle.m_n1 = side_n1; emitting_triangle.m_n2 = side_n2; emitting_triangle.m_geometric_normal = side_geometric_normal; emitting_triangle.m_triangle_support_plane = triangle_support_plane; emitting_triangle.m_rcp_area = rcp_area; emitting_triangle.m_edf = edf; // Store the light-emitting triangle. const size_t emitting_triangle_index = m_emitting_triangles.size(); m_emitting_triangles.push_back(emitting_triangle); // Insert the light-emitting triangle into the CDFs. m_emitter_cdf.insert(emitting_triangle_index + m_lights.size(), area); m_emitting_triangle_cdf.insert(emitting_triangle_index, area); // Keep track of the total area of the light-emitting triangles. m_total_emissive_area += area; } } } } }
AABB3d TransformSequence::compute_motion_segment_bbox( const AABB3d& bbox, const Transformd& from, const Transformd& to) const { // // Reference: // // http://gruenschloss.org/motion-blur/motion-blur.pdf page 11. // // Parameters. const double MinLength = Pi / 2.0; const double RootEps = 1.0e-6; const double GrowEps = 1.0e-4; const size_t MaxIterations = 100; // Start with the bounding box at 'from'. const AABB3d from_bbox = from.to_parent(bbox); AABB3d motion_bbox = from_bbox; // Setup an interpolator between 'from' and 'to'. TransformInterpolatord interpolator; if (!interpolator.set_transforms(from, to)) return motion_bbox; // Compute the scalings at 'from' and 'to'. const Vector3d s0 = interpolator.get_s0(); const Vector3d s1 = interpolator.get_s1(); // Compute the relative rotation between 'from' and 'to'. const Quaterniond q = interpolator.get_q1() * conjugate(interpolator.get_q0()); // Transform the relative rotation to the axis-angle representation. Vector3d axis; double angle; q.extract_axis_angle(axis, angle); if (axis.z < 0.0) angle = -angle; // The following code only makes sense if there is a rotation component. if (angle == 0.0) return motion_bbox; // Compute the rotation required to align the rotation axis with the Z axis. const Vector3d Z(0.0, 0.0, 1.0); const Vector3d perp = cross(Z, axis); const double perp_norm = norm(perp); Transformd axis_to_z; if (perp_norm == 0.0) axis_to_z = Transformd::identity(); else { const Vector3d v = perp / perp_norm; const double sin_a = clamp(perp_norm, -1.0, 1.0); const double cos_a = sqrt(1.0 - sin_a * sin_a); axis_to_z.set_local_to_parent(Matrix4d::make_rotation(v, cos_a, +sin_a)); axis_to_z.set_parent_to_local(Matrix4d::make_rotation(v, cos_a, -sin_a)); } // Build the linear scaling functions Sx(theta), Sy(theta) and Sz(theta). const LinearFunction sx(1.0, s1.x / s0.x, angle); const LinearFunction sy(1.0, s1.y / s0.y, angle); const LinearFunction sz(1.0, s1.z / s0.z, angle); // Consider each corner of the bounding box. Notice an important trick here: // we take advantage of the way AABB::compute_corner() works to only iterate // over the four corners at Z=min instead of over all eight corners since we // anyway transform the rotation to be aligned with the Z axis. for (size_t c = 0; c < 4; ++c) { // Compute the position of this corner at 'from'. const Vector3d corner = axis_to_z.point_to_local(from_bbox.compute_corner(c)); const Vector2d corner2d(corner.x, corner.y); // Build the trajectory functions x(theta) and y(theta). const TrajectoryX tx(sx, sy, corner2d); const TrajectoryY ty(sx, sy, corner2d); // Find all the rotation angles at which this corner is an extremum and update the motion bounding box. RootHandler root_handler(tx, ty, sz, axis_to_z, corner, motion_bbox); find_multiple_roots_newton( Bind<TrajectoryX>(tx, &TrajectoryX::d), Bind<TrajectoryX>(tx, &TrajectoryX::dd), 0.0, angle, MinLength, RootEps, MaxIterations, root_handler); find_multiple_roots_newton( Bind<TrajectoryY>(ty, &TrajectoryY::d), Bind<TrajectoryY>(ty, &TrajectoryY::dd), 0.0, angle, MinLength, RootEps, MaxIterations, root_handler); } motion_bbox.robust_grow(GrowEps); return motion_bbox; }
void LightPathsWidget::set_transform(const Transformd& transform) { m_camera_matrix = transform.get_parent_to_local(); }
void AOVoxelTree::build( const Scene& scene, BuilderType& builder) { // The voxel tree is built using the scene geometry at the middle of the shutter interval. const double time = scene.get_camera()->get_shutter_middle_time(); // Loop over the assembly instances of the scene. for (const_each<AssemblyInstanceContainer> i = scene.assembly_instances(); i; ++i) { // Retrieve the assembly instance. const AssemblyInstance& assembly_instance = *i; // Retrieve the assembly. const Assembly& assembly = assembly_instance.get_assembly(); // Loop over the object instances of the assembly. for (const_each<ObjectInstanceContainer> j = assembly.object_instances(); j; ++j) { // Retrieve the object instance. const ObjectInstance& object_instance = *j; // Compute the object space to world space transformation. const Transformd transform = assembly_instance.transform_sequence().evaluate(time) * object_instance.get_transform(); // Retrieve the object. Object& object = object_instance.get_object(); // Retrieve the region kit of the object. Access<RegionKit> region_kit(&object.get_region_kit()); // Loop over the regions of the object. const size_t region_count = region_kit->size(); for (size_t region_index = 0; region_index < region_count; ++region_index) { // Retrieve the region. const IRegion* region = (*region_kit)[region_index]; // Retrieve the tessellation of the region. Access<StaticTriangleTess> tess(®ion->get_static_triangle_tess()); // Push all triangles of the region into the tree. const size_t triangle_count = tess->m_primitives.size(); for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index) { // Fetch the triangle. const Triangle& triangle = tess->m_primitives[triangle_index]; // Retrieve object instance space vertices of the triangle. const GVector3& v0_os = tess->m_vertices[triangle.m_v0]; const GVector3& v1_os = tess->m_vertices[triangle.m_v1]; const GVector3& v2_os = tess->m_vertices[triangle.m_v2]; // Transform triangle vertices to world space. const GVector3 v0(transform.point_to_parent(v0_os)); const GVector3 v1(transform.point_to_parent(v1_os)); const GVector3 v2(transform.point_to_parent(v2_os)); // Push the triangle into the tree. TriangleIntersector intersector(v0, v1, v2); builder.push(intersector); } } } } }
void LightSampler::collect_emitting_triangles( const Assembly& assembly, const AssemblyInstance& assembly_instance, const TransformSequence& transform_sequence) { // Loop over the object instances of the assembly. const size_t object_instance_count = assembly.object_instances().size(); for (size_t object_instance_index = 0; object_instance_index < object_instance_count; ++object_instance_index) { // Retrieve the object instance. const ObjectInstance* object_instance = assembly.object_instances().get_by_index(object_instance_index); // Retrieve the materials of the object instance. const MaterialArray& front_materials = object_instance->get_front_materials(); const MaterialArray& back_materials = object_instance->get_back_materials(); // Skip object instances without light-emitting materials. if (!has_emitting_materials(front_materials) && !has_emitting_materials(back_materials)) continue; double object_area = 0.0; // Compute the object space to world space transformation. // todo: add support for moving light-emitters. const Transformd& object_instance_transform = object_instance->get_transform(); const Transformd& assembly_instance_transform = transform_sequence.get_earliest_transform(); const Transformd global_transform = assembly_instance_transform * object_instance_transform; // Retrieve the object. Object& object = object_instance->get_object(); // Retrieve the region kit of the object. Access<RegionKit> region_kit(&object.get_region_kit()); // Loop over the regions of the object. const size_t region_count = region_kit->size(); for (size_t region_index = 0; region_index < region_count; ++region_index) { // Retrieve the region. const IRegion* region = (*region_kit)[region_index]; // Retrieve the tessellation of the region. Access<StaticTriangleTess> tess(®ion->get_static_triangle_tess()); // Loop over the triangles of the region. const size_t triangle_count = tess->m_primitives.size(); for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index) { // Fetch the triangle. const Triangle& triangle = tess->m_primitives[triangle_index]; // Skip triangles without a material. if (triangle.m_pa == Triangle::None) continue; // Fetch the materials assigned to this triangle. const size_t pa_index = static_cast<size_t>(triangle.m_pa); const Material* front_material = pa_index < front_materials.size() ? front_materials[pa_index] : 0; const Material* back_material = pa_index < back_materials.size() ? back_materials[pa_index] : 0; // Skip triangles that don't emit light. if ((front_material == 0 || front_material->has_emission() == false) && (back_material == 0 || back_material->has_emission() == false)) continue; // Retrieve object instance space vertices of the triangle. const GVector3& v0_os = tess->m_vertices[triangle.m_v0]; const GVector3& v1_os = tess->m_vertices[triangle.m_v1]; const GVector3& v2_os = tess->m_vertices[triangle.m_v2]; // Transform triangle vertices to assembly space. const GVector3 v0_as = object_instance_transform.point_to_parent(v0_os); const GVector3 v1_as = object_instance_transform.point_to_parent(v1_os); const GVector3 v2_as = object_instance_transform.point_to_parent(v2_os); // Compute the support plane of the hit triangle in assembly space. const GTriangleType triangle_geometry(v0_as, v1_as, v2_as); TriangleSupportPlaneType triangle_support_plane; triangle_support_plane.initialize(TriangleType(triangle_geometry)); // Transform triangle vertices to world space. const Vector3d v0(assembly_instance_transform.point_to_parent(v0_as)); const Vector3d v1(assembly_instance_transform.point_to_parent(v1_as)); const Vector3d v2(assembly_instance_transform.point_to_parent(v2_as)); // Compute the geometric normal to the triangle and the area of the triangle. Vector3d geometric_normal = cross(v1 - v0, v2 - v0); const double geometric_normal_norm = norm(geometric_normal); if (geometric_normal_norm == 0.0) continue; const double rcp_geometric_normal_norm = 1.0 / geometric_normal_norm; const double rcp_area = 2.0 * rcp_geometric_normal_norm; const double area = 0.5 * geometric_normal_norm; geometric_normal *= rcp_geometric_normal_norm; assert(is_normalized(geometric_normal)); // Retrieve object instance space vertex normals. Vector3d n0_os, n1_os, n2_os; if (triangle.m_n0 != Triangle::None && triangle.m_n1 != Triangle::None && triangle.m_n2 != Triangle::None) { n0_os = Vector3d(tess->m_vertex_normals[triangle.m_n0]); n1_os = Vector3d(tess->m_vertex_normals[triangle.m_n1]); n2_os = Vector3d(tess->m_vertex_normals[triangle.m_n2]); } else n0_os = n1_os = n2_os = geometric_normal; // Transform vertex normals to world space. const Vector3d n0(normalize(global_transform.normal_to_parent(n0_os))); const Vector3d n1(normalize(global_transform.normal_to_parent(n1_os))); const Vector3d n2(normalize(global_transform.normal_to_parent(n2_os))); for (size_t side = 0; side < 2; ++side) { // Retrieve the material; skip sides without a material or without emission. const Material* material = side == 0 ? front_material : back_material; if (material == 0 || material->has_emission() == false) continue; // Retrieve the EDF and get the importance multiplier. double importance_multiplier = 1.0; if (const EDF* edf = material->get_uncached_edf()) importance_multiplier = edf->get_uncached_importance_multiplier(); // Accumulate the object area for OSL shaders. object_area += area; // Compute the probability density of this triangle. const double triangle_importance = m_params.m_importance_sampling ? area : 1.0; const double triangle_prob = triangle_importance * importance_multiplier; // Create a light-emitting triangle. EmittingTriangle emitting_triangle; emitting_triangle.m_assembly_instance = &assembly_instance; emitting_triangle.m_object_instance_index = object_instance_index; emitting_triangle.m_region_index = region_index; emitting_triangle.m_triangle_index = triangle_index; emitting_triangle.m_v0 = v0; emitting_triangle.m_v1 = v1; emitting_triangle.m_v2 = v2; emitting_triangle.m_n0 = side == 0 ? n0 : -n0; emitting_triangle.m_n1 = side == 0 ? n1 : -n1; emitting_triangle.m_n2 = side == 0 ? n2 : -n2; emitting_triangle.m_geometric_normal = side == 0 ? geometric_normal : -geometric_normal; emitting_triangle.m_triangle_support_plane = triangle_support_plane; emitting_triangle.m_rcp_area = rcp_area; emitting_triangle.m_triangle_prob = 0.0; // will be initialized once the emitting triangle CDF is built emitting_triangle.m_material = material; // Store the light-emitting triangle. const size_t emitting_triangle_index = m_emitting_triangles.size(); m_emitting_triangles.push_back(emitting_triangle); // Insert the light-emitting triangle into the CDF. m_emitting_triangles_cdf.insert(emitting_triangle_index, triangle_prob); } } } #ifdef APPLESEED_WITH_OSL store_object_area_in_shadergroups( &assembly_instance, object_instance, object_area, front_materials); store_object_area_in_shadergroups( &assembly_instance, object_instance, object_area, back_materials); #endif } }