Embree
/*
 *  SPDX-FileCopyrightText: Copyright 2022-2023 Julian Amann <dev@vertexwahn.de>
 *  SPDX-License-Identifier: Apache-2.0
 */

#include "boost/predef.h"

#include "embree4/rtcore.h"

#if BOOST_ARCH_X86_64
#include <pmmintrin.h>
#include <xmmintrin.h> // SIMD Extensions intrinsics
#endif

#include "gmock/gmock.h"

// Many nice tests:
// https://git.hfs.rub.de/garten/blazert/-/tree/2b46b4874e572fefbf0878d61b971caa4190169b/test/test_embree_primitives

// see: https://gist.github.com/jcowles/af59657e884ecd839f37809f7fdf95e4
TEST(Embree, Embree_initialization) {
#if BOOST_ARCH_X86_64
    // Setup SIMD Extensions intrinsics
    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
    _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
#endif

    RTCDevice device = rtcNewDevice("verbose=1");
    EXPECT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    // create scene
    RTCScene scene = rtcNewScene(device);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);
    ASSERT_TRUE(scene);

    // application vertex and index layout
    struct Vertex   { float x, y, z; };
    struct Triangle { int v0, v1, v2; };

    Vertex v[3];
    v[0].x = -10; v[0].y = -10; v[0].z = 10;
    v[1].x =  10; v[1].y = -10; v[1].z = 10;
    v[2].x =   0; v[2].y =  10; v[2].z = 10;

    Triangle t[1];
    t[0].v0 = 0;
    t[0].v1 = 1;
    t[0].v2 = 2;

    Vertex *vertex_ptr = v;
    Triangle *index_ptr = t;

    // create triangle mesh
    RTCGeometry mesh = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);
    ASSERT_TRUE(mesh);

    // share data buffers
    size_t vertex_count = 3;
    size_t index_count = 3;
    rtcSetSharedGeometryBuffer(mesh,
                               RTC_BUFFER_TYPE_VERTEX,
                               0,
                               RTC_FORMAT_FLOAT3,
                               vertex_ptr,
                               0,
                               sizeof(Vertex),
                               vertex_count);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    rtcSetSharedGeometryBuffer(mesh,
                               RTC_BUFFER_TYPE_INDEX,
                               0,
                               RTC_FORMAT_UINT3,
                               index_ptr,
                               0,
                               3*sizeof(unsigned int),
                               index_count/3);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    rtcCommitGeometry(mesh);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    unsigned int geom_id = 42;
    rtcAttachGeometryByID(scene, mesh, geom_id);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    rtcReleaseGeometry(mesh);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    rtcCommitScene(scene);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    RTCRayHit rayhit;
    rayhit.ray.org_x  = 0.f;
    rayhit.ray.org_y = 0.f;
    rayhit.ray.org_z =  0.f;
    rayhit.ray.dir_x  = 0.f;
    rayhit.ray.dir_y = 0.f;
    rayhit.ray.dir_z = 1.f;
    rayhit.ray.tnear  = 0.f;
    rayhit.ray.tfar   = std::numeric_limits<float>::infinity();
    rayhit.ray.time = 0.f;
    rayhit.ray.mask = -1;
    rayhit.ray.flags = 0;
    rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
    rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;

    RTCIntersectArguments args;
    rtcInitIntersectArguments(&args);
    args.feature_mask = (RTCFeatureFlags) (RTC_FEATURE_FLAG_TRIANGLE);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    rtcIntersect1(scene, &rayhit, &args);
    ASSERT_THAT(rtcGetDeviceError(device), RTC_ERROR_NONE);

    EXPECT_THAT(rayhit.hit.primID, 0);
    EXPECT_THAT(rayhit.hit.geomID, 42);
    EXPECT_THAT(rayhit.ray.tfar, testing::FloatNear(10.f, 0.001f));

    // release objects
    rtcReleaseScene(scene);
    rtcReleaseDevice(device);
}