/* SPDX-License-Identifier: Apache-2.0
 * Licensed under the Apache License, Version 2.0 (the "License"); */

CCL_NAMESPACE_BEGIN

/* Point Primitive
 *
 * Point primitive for rendering point clouds.
 */

#ifdef __POINTCLOUD__

/* Reading attributes on various point elements */

ccl_device float point_attribute_float(KernelGlobals kg,
                                       ccl_private const ShaderData *sd,
                                       const AttributeDescriptor desc,
                                       ccl_private float *dx,
                                       ccl_private float *dy)
{
#  ifdef __RAY_DIFFERENTIALS__
  if (dx)
    *dx = 0.0f;
  if (dy)
    *dy = 0.0f;
#  endif

  if (desc.element == ATTR_ELEMENT_VERTEX) {
    return kernel_data_fetch(attributes_float, desc.offset + sd->prim);
  }
  else {
    return 0.0f;
  }
}

ccl_device float2 point_attribute_float2(KernelGlobals kg,
                                         ccl_private const ShaderData *sd,
                                         const AttributeDescriptor desc,
                                         ccl_private float2 *dx,
                                         ccl_private float2 *dy)
{
#  ifdef __RAY_DIFFERENTIALS__
  if (dx)
    *dx = make_float2(0.0f, 0.0f);
  if (dy)
    *dy = make_float2(0.0f, 0.0f);
#  endif

  if (desc.element == ATTR_ELEMENT_VERTEX) {
    return kernel_data_fetch(attributes_float2, desc.offset + sd->prim);
  }
  else {
    return make_float2(0.0f, 0.0f);
  }
}

ccl_device float3 point_attribute_float3(KernelGlobals kg,
                                         ccl_private const ShaderData *sd,
                                         const AttributeDescriptor desc,
                                         ccl_private float3 *dx,
                                         ccl_private float3 *dy)
{
#  ifdef __RAY_DIFFERENTIALS__
  if (dx)
    *dx = make_float3(0.0f, 0.0f, 0.0f);
  if (dy)
    *dy = make_float3(0.0f, 0.0f, 0.0f);
#  endif

  if (desc.element == ATTR_ELEMENT_VERTEX) {
    return kernel_data_fetch(attributes_float3, desc.offset + sd->prim);
  }
  else {
    return make_float3(0.0f, 0.0f, 0.0f);
  }
}

ccl_device float4 point_attribute_float4(KernelGlobals kg,
                                         ccl_private const ShaderData *sd,
                                         const AttributeDescriptor desc,
                                         ccl_private float4 *dx,
                                         ccl_private float4 *dy)
{
#  ifdef __RAY_DIFFERENTIALS__
  if (dx)
    *dx = zero_float4();
  if (dy)
    *dy = zero_float4();
#  endif

  if (desc.element == ATTR_ELEMENT_VERTEX) {
    return kernel_data_fetch(attributes_float4, desc.offset + sd->prim);
  }
  else {
    return zero_float4();
  }
}

/* Point position */

ccl_device float3 point_position(KernelGlobals kg, ccl_private const ShaderData *sd)
{
  if (sd->type & PRIMITIVE_POINT) {
    /* World space center. */
    float3 P = (sd->type & PRIMITIVE_MOTION) ?
                   float4_to_float3(motion_point(kg, sd->object, sd->prim, sd->time)) :
                   float4_to_float3(kernel_data_fetch(points, sd->prim));

    if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
      object_position_transform(kg, sd, &P);
    }

    return P;
  }

  return zero_float3();
}

/* Point radius */

ccl_device float point_radius(KernelGlobals kg, ccl_private const ShaderData *sd)
{
  if (sd->type & PRIMITIVE_POINT) {
    /* World space radius. */
    const float r = kernel_data_fetch(points, sd->prim).w;

    if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) {
      return r;
    }
    else {
      const float normalized_r = r * (1.0f / M_SQRT3_F);
      float3 dir = make_float3(normalized_r, normalized_r, normalized_r);
      object_dir_transform(kg, sd, &dir);
      return len(dir);
    }
  }

  return 0.0f;
}

/* Point random */

ccl_device float point_random(KernelGlobals kg, ccl_private const ShaderData *sd)
{
  if (sd->type & PRIMITIVE_POINT) {
    const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POINT_RANDOM);
    return (desc.offset != ATTR_STD_NOT_FOUND) ? point_attribute_float(kg, sd, desc, NULL, NULL) :
                                                 0.0f;
  }
  return 0.0f;
}

/* Point location for motion pass, linear interpolation between keys and
 * ignoring radius because we do the same for the motion keys */

ccl_device float3 point_motion_center_location(KernelGlobals kg, ccl_private const ShaderData *sd)
{
  return float4_to_float3(kernel_data_fetch(points, sd->prim));
}

#endif /* __POINTCLOUD__ */

CCL_NAMESPACE_END
