// ======================================================================== //
// Copyright 2009-2018 Intel Corporation                                    //
//                                                                          //
// Licensed under the Apache License, Version 2.0 (the "License");          //
// you may not use this file except in compliance with the License.         //
// You may obtain a copy of the License at                                  //
//                                                                          //
//     http://www.apache.org/licenses/LICENSE-2.0                           //
//                                                                          //
// Unless required by applicable law or agreed to in writing, software      //
// distributed under the License is distributed on an "AS IS" BASIS,        //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
// See the License for the specific language governing permissions and      //
// limitations under the License.                                           //
// ======================================================================== //

#ifndef __RTC_RAY_ISPH__
#define __RTC_RAY_ISPH__

#include "rtcore_common.isph"

/* Ray structure */
struct RTC_ALIGN(16) RTCRay
{
  float org_x;        // x coordinate of ray origin
  float org_y;        // y coordinate of ray origin
  float org_z;        // z coordinate of ray origin
  float tnear;        // start of ray segment

  float dir_x;        // x coordinate of ray direction
  float dir_y;        // y coordinate of ray direction
  float dir_z;        // z coordinate of ray direction
  float time;         // time of this ray for motion blur

  float tfar;         // end of ray segment (set to hit distance)
  unsigned int mask;  // ray mask
  unsigned int id;    // ray ID
  unsigned int flags; // ray flags
};

/* Hit structure */
struct RTCHit
{
  float Ng_x;          // x coordinate of geometry normal
  float Ng_y;          // y coordinate of geometry normal
  float Ng_z;          // z coordinate of geometry normal
   
  float u;             // barycentric u coordinate of hit
  float v;             // barycentric v coordinate of hit

  unsigned int primID; // primitive ID
  unsigned int geomID; // geometry ID
  unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID
};

/* Combined ray/hit structure */
struct RTCRayHit
{
  RTCRay ray;
  RTCHit hit;
};

struct RTCRayN;
struct RTCHitN;
struct RTCRayHitN;

/* Helper functions to access ray packets of runtime size N */
RTC_FORCEINLINE varying float& RTCRayN_org_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[0*N+i]); }
RTC_FORCEINLINE varying float& RTCRayN_org_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[1*N+i]); }
RTC_FORCEINLINE varying float& RTCRayN_org_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[2*N+i]); }
RTC_FORCEINLINE varying float& RTCRayN_tnear(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[3*N+i]); }

RTC_FORCEINLINE varying float& RTCRayN_dir_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[4*N+i]); }
RTC_FORCEINLINE varying float& RTCRayN_dir_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[5*N+i]); }
RTC_FORCEINLINE varying float& RTCRayN_dir_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[6*N+i]); }
RTC_FORCEINLINE varying float& RTCRayN_time (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[7*N+i]); }

RTC_FORCEINLINE varying float&        RTCRayN_tfar (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform)        &((uniform float*)ray)[8*N+i]); }
RTC_FORCEINLINE varying unsigned int& RTCRayN_mask (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[9*N+i]); }
RTC_FORCEINLINE varying unsigned int& RTCRayN_id   (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[10*N+i]); }
RTC_FORCEINLINE varying unsigned int& RTCRayN_flags(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[11*N+i]); }

/* Helper functions to access hit packets of runtime size N */
RTC_FORCEINLINE varying float& RTCHitN_Ng_x(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[0*N+i]); }
RTC_FORCEINLINE varying float& RTCHitN_Ng_y(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[1*N+i]); }
RTC_FORCEINLINE varying float& RTCHitN_Ng_z(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[2*N+i]); }
RTC_FORCEINLINE varying float& RTCHitN_u   (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[3*N+i]); }
RTC_FORCEINLINE varying float& RTCHitN_v   (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[4*N+i]); }
RTC_FORCEINLINE varying unsigned int& RTCHitN_primID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform  )hit)[5*N+i]); }
RTC_FORCEINLINE varying unsigned int& RTCHitN_geomID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform  )hit)[6*N+i]); }
RTC_FORCEINLINE varying unsigned int& RTCHitN_instID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i, uniform unsigned int l) { return *((varying unsigned int* uniform) &((unsigned int* uniform)hit)[7*N+i+l*N]); }

/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */
RTC_FORCEINLINE RTCRayN* uniform RTCRayHitN_RayN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCRayN* uniform)&((uniform float* uniform)rayhit)[0*N]; }
RTC_FORCEINLINE RTCHitN* uniform RTCRayHitN_HitN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCHitN* uniform)&((uniform float* uniform)rayhit)[12*N]; }

/* Ray structure for a packet/stream of N rays in pointer SOA layout */
struct RTCRayNp
{
  uniform float* uniform org_x;
  uniform float* uniform org_y;
  uniform float* uniform org_z;
  uniform float* uniform tnear;

  uniform float* uniform dir_x;
  uniform float* uniform dir_y;
  uniform float* uniform dir_z;
  uniform float* uniform time;

  uniform float* uniform tfar;
  uniform unsigned int* uniform mask;
  uniform unsigned int* uniform id;
  uniform unsigned int* uniform flags;
};

/* Hit structure for a packet/stream of N rays in pointer SOA layout */
struct RTCHitNp
{
  uniform float* uniform Ng_x;
  uniform float* uniform Ng_y;
  uniform float* uniform Ng_z;

  uniform float* uniform u;
  uniform float* uniform v;

  uniform unsigned int* uniform primID;
  uniform unsigned int* uniform geomID;
  uniform unsigned int* uniform instID[RTC_MAX_INSTANCE_LEVEL_COUNT];
};

/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */
struct RTCRayHitNp
{
  RTCRayNp ray;
  RTCHitNp hit;
};

RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* uniform rayN, uniform unsigned int N, uniform unsigned int i)
{
  RTCRay ray;
  ray.org_x = RTCRayN_org_x(rayN,N,i);
  ray.org_y = RTCRayN_org_y(rayN,N,i);
  ray.org_z = RTCRayN_org_z(rayN,N,i);
  ray.tnear = RTCRayN_tnear(rayN,N,i);
  ray.dir_x = RTCRayN_dir_x(rayN,N,i);
  ray.dir_y = RTCRayN_dir_y(rayN,N,i);
  ray.dir_z = RTCRayN_dir_z(rayN,N,i);
  ray.time  = RTCRayN_time(rayN,N,i);
  ray.tfar  = RTCRayN_tfar(rayN,N,i);
  ray.mask  = RTCRayN_mask(rayN,N,i);
  ray.id    = RTCRayN_id(rayN,N,i);
  ray.flags = RTCRayN_flags(rayN,N,i);
  return ray;
}

RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* uniform hitN, uniform unsigned int N, uniform unsigned int i)
{
  RTCHit hit;
  hit.Ng_x   = RTCHitN_Ng_x(hitN,N,i);
  hit.Ng_y   = RTCHitN_Ng_y(hitN,N,i);
  hit.Ng_z   = RTCHitN_Ng_z(hitN,N,i);
  hit.u      = RTCHitN_u(hitN,N,i);
  hit.v      = RTCHitN_v(hitN,N,i);
  hit.primID = RTCHitN_primID(hitN,N,i);
  hit.geomID = RTCHitN_geomID(hitN,N,i);
  for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++)
    hit.instID[l] = RTCHitN_instID(hitN,N,i,l);
  return hit;
}

RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* uniform hitN, const varying RTCHit* uniform hit, uniform unsigned int N, uniform unsigned int i)
{
  RTCHitN_Ng_x(hitN,N,i)   = hit->Ng_x;
  RTCHitN_Ng_y(hitN,N,i)   = hit->Ng_y;
  RTCHitN_Ng_z(hitN,N,i)   = hit->Ng_z;
  RTCHitN_u(hitN,N,i)      = hit->u;
  RTCHitN_v(hitN,N,i)      = hit->v;
  RTCHitN_primID(hitN,N,i) = hit->primID;
  RTCHitN_geomID(hitN,N,i) = hit->geomID;
  for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++)
    RTCHitN_instID(hitN,N,i,l) = hit->instID[l];
}

RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* uniform rayhitN, uniform unsigned int N, uniform unsigned int i)
{
  RTCRayHit rh;

  RTCRayN* uniform ray = RTCRayHitN_RayN(rayhitN,N);
  rh.ray.org_x = RTCRayN_org_x(ray,N,i);
  rh.ray.org_y = RTCRayN_org_y(ray,N,i);
  rh.ray.org_z = RTCRayN_org_z(ray,N,i);
  rh.ray.tnear = RTCRayN_tnear(ray,N,i);
  rh.ray.dir_x = RTCRayN_dir_x(ray,N,i);
  rh.ray.dir_y = RTCRayN_dir_y(ray,N,i);
  rh.ray.dir_z = RTCRayN_dir_z(ray,N,i);
  rh.ray.time  = RTCRayN_time(ray,N,i);
  rh.ray.tfar  = RTCRayN_tfar(ray,N,i);
  rh.ray.mask  = RTCRayN_mask(ray,N,i);
  rh.ray.id    = RTCRayN_id(ray,N,i);
  rh.ray.flags = RTCRayN_flags(ray,N,i);

  RTCHitN* uniform hit = RTCRayHitN_HitN(rayhitN,N);
  rh.hit.Ng_x   = RTCHitN_Ng_x(hit,N,i);
  rh.hit.Ng_y   = RTCHitN_Ng_y(hit,N,i);
  rh.hit.Ng_z   = RTCHitN_Ng_z(hit,N,i);
  rh.hit.u      = RTCHitN_u(hit,N,i);
  rh.hit.v      = RTCHitN_v(hit,N,i);
  rh.hit.primID = RTCHitN_primID(hit,N,i);
  rh.hit.geomID = RTCHitN_geomID(hit,N,i);
  for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++)
    rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l);

  return rh;
}

#endif
