Release NaraMotion v0.26.0: Structure of Arrays (SoA), Path Arena Allocator, Uniform Grid Indexing, Dual-Buffer Rendering & Centroid Spring Pop-in
b516e00 verified Download c_src/naramotion_perspective.cpp from ArifiIslam/NaraMotion: direct link, hf CLI and curl.
- Browser
- Download file 14 kB
-
https://huggingface.co/ArifiIslam/NaraMotion/resolve/main/c_src/naramotion_perspective.cpp
- Command line
-
hf download hf://ArifiIslam/NaraMotion/c_src/naramotion_perspective.cpp
-
curl -L -o naramotion_perspective.cpp https://huggingface.co/ArifiIslam/NaraMotion/resolve/main/c_src/naramotion_perspective.cpp
14 kB
| // Blend2D C Types | |
| typedef uint32_t BLResult; | |
| struct BLPoint { | |
| double x; | |
| double y; | |
| }; | |
| struct BLPathCore { | |
| void* impl; | |
| }; | |
| extern "C" { | |
| BLResult bl_path_init(BLPathCore* self); | |
| BLResult bl_path_reset(BLPathCore* self); | |
| BLResult bl_path_clear(BLPathCore* self); | |
| BLResult bl_path_destroy(BLPathCore* self); | |
| size_t bl_path_get_size(const BLPathCore* self); | |
| const BLPoint* bl_path_get_vertex_data(const BLPathCore* self); | |
| const uint8_t* bl_path_get_command_data(const BLPathCore* self); | |
| BLResult bl_path_move_to(BLPathCore* self, double x0, double y0); | |
| BLResult bl_path_line_to(BLPathCore* self, double x1, double y1); | |
| BLResult bl_path_close(BLPathCore* self); | |
| } | |
| // 3D Camera Parameters struct | |
| struct BLCameraParams { | |
| double viewport_width; | |
| double viewport_height; | |
| double fov_degrees; | |
| double pitch_degrees; // Tilt angle (0 = top-down, 80 = grazing horizon) | |
| double yaw_degrees; // Horizontal rotation | |
| double roll_degrees; // Side-to-side banking | |
| double distance; // Camera distance / altitude | |
| double target_x; // Focus center X | |
| double target_y; // Focus center Y | |
| double near_clip; // Minimum depth W before clipping | |
| }; | |
| extern "C" { | |
| // ============================================================================ | |
| // 1. Perspective Matrix & 3D Projector Core | |
| // ============================================================================ | |
| static inline bool _project_single_point( | |
| double x, double y, | |
| const BLCameraParams* cam, | |
| double* out_sx, double* out_sy, | |
| double* out_depth_scale | |
| ) { | |
| double dx = x - cam->target_x; | |
| double dy = y - cam->target_y; | |
| double yaw_rad = cam->yaw_degrees * DEG_TO_RAD; | |
| double pitch_rad = cam->pitch_degrees * DEG_TO_RAD; | |
| double roll_rad = cam->roll_degrees * DEG_TO_RAD; | |
| double cos_yaw = std::cos(yaw_rad), sin_yaw = std::sin(yaw_rad); | |
| double cos_pitch = std::cos(pitch_rad), sin_pitch = std::sin(pitch_rad); | |
| double cos_roll = std::cos(roll_rad), sin_roll = std::sin(roll_rad); | |
| // 1. Yaw rotation (horizontal rotation around Z axis) | |
| double x1 = dx * cos_yaw - dy * sin_yaw; | |
| double y1 = dx * sin_yaw + dy * cos_yaw; | |
| // 2. Pitch rotation (tilt towards horizon) | |
| // Z depth along optical axis: Z = y1 * sin(pitch) + distance | |
| double z_cam = y1 * sin_pitch + cam->distance; | |
| double y_cam = y1 * cos_pitch; | |
| double x_cam = x1; | |
| // Near-plane clipping: if point is behind camera or too close to lens | |
| double near_plane = (cam->near_clip > 1.0) ? cam->near_clip : 10.0; | |
| if (z_cam <= near_plane) { | |
| if (out_sx) *out_sx = 0.0; | |
| if (out_sy) *out_sy = 0.0; | |
| if (out_depth_scale) *out_depth_scale = 0.0; | |
| return false; | |
| } | |
| // 3. Roll rotation (camera banking) | |
| double x_final = x_cam * cos_roll - y_cam * sin_roll; | |
| double y_final = x_cam * sin_roll + y_cam * cos_roll; | |
| // 4. Perspective Projection | |
| double fov_rad = cam->fov_degrees * DEG_TO_RAD; | |
| double f = 1.0 / std::tan(fov_rad * 0.5); | |
| double scale = (f * cam->viewport_height * 0.5) / z_cam; | |
| double sx = cam->viewport_width * 0.5 + x_final * scale; | |
| double sy = cam->viewport_height * 0.5 - y_final * scale; | |
| double depth_scale = cam->distance / z_cam; | |
| if (out_sx) *out_sx = sx; | |
| if (out_sy) *out_sy = sy; | |
| if (out_depth_scale) *out_depth_scale = depth_scale; | |
| return true; | |
| } | |
| BL_PERSPECTIVE_EXPORT int bl_perspective_project_point( | |
| double x, double y, | |
| const BLCameraParams* cam, | |
| double* out_sx, double* out_sy, | |
| double* out_depth_scale | |
| ) { | |
| if (!cam) return 0; | |
| return _project_single_point(x, y, cam, out_sx, out_sy, out_depth_scale) ? 1 : 0; | |
| } | |
| BL_PERSPECTIVE_EXPORT size_t bl_perspective_transform_points( | |
| const double* in_pts_xy, | |
| double* out_pts_xy, | |
| double* out_depth_scales, | |
| uint8_t* out_valid_mask, | |
| size_t count, | |
| const BLCameraParams* cam | |
| ) { | |
| if (!in_pts_xy || !out_pts_xy || !cam || count == 0) return 0; | |
| size_t valid_count = 0; | |
| for (size_t i = 0; i < count; i++) { | |
| double px = in_pts_xy[i * 2 + 0]; | |
| double py = in_pts_xy[i * 2 + 1]; | |
| double sx = 0.0, sy = 0.0, ds = 0.0; | |
| bool ok = _project_single_point(px, py, cam, &sx, &sy, &ds); | |
| out_pts_xy[i * 2 + 0] = sx; | |
| out_pts_xy[i * 2 + 1] = sy; | |
| if (out_depth_scales) out_depth_scales[i] = ds; | |
| if (out_valid_mask) out_valid_mask[i] = ok ? 1 : 0; | |
| if (ok) valid_count++; | |
| } | |
| return valid_count; | |
| } | |
| BL_PERSPECTIVE_EXPORT size_t bl_perspective_transform_lines( | |
| const double* in_lines_x1y1x2y2, | |
| double* out_lines_x1y1x2y2, | |
| double* out_depth_scales, | |
| uint8_t* out_valid_mask, | |
| size_t line_count, | |
| const BLCameraParams* cam | |
| ) { | |
| if (!in_lines_x1y1x2y2 || !out_lines_x1y1x2y2 || !cam || line_count == 0) return 0; | |
| size_t valid_count = 0; | |
| for (size_t i = 0; i < line_count; i++) { | |
| double x1 = in_lines_x1y1x2y2[i * 4 + 0]; | |
| double y1 = in_lines_x1y1x2y2[i * 4 + 1]; | |
| double x2 = in_lines_x1y1x2y2[i * 4 + 2]; | |
| double y2 = in_lines_x1y1x2y2[i * 4 + 3]; | |
| double sx1 = 0.0, sy1 = 0.0, ds1 = 0.0; | |
| double sx2 = 0.0, sy2 = 0.0, ds2 = 0.0; | |
| bool ok1 = _project_single_point(x1, y1, cam, &sx1, &sy1, &ds1); | |
| bool ok2 = _project_single_point(x2, y2, cam, &sx2, &sy2, &ds2); | |
| out_lines_x1y1x2y2[i * 4 + 0] = sx1; | |
| out_lines_x1y1x2y2[i * 4 + 1] = sy1; | |
| out_lines_x1y1x2y2[i * 4 + 2] = sx2; | |
| out_lines_x1y1x2y2[i * 4 + 3] = sy2; | |
| double avg_ds = (ds1 + ds2) * 0.5; | |
| if (out_depth_scales) out_depth_scales[i] = avg_ds; | |
| bool line_valid = (ok1 && ok2); | |
| if (out_valid_mask) out_valid_mask[i] = line_valid ? 1 : 0; | |
| if (line_valid) valid_count++; | |
| } | |
| return valid_count; | |
| } | |
| BL_PERSPECTIVE_EXPORT int bl_perspective_transform_path( | |
| const BLPathCore* src, | |
| BLPathCore* dst, | |
| double* out_avg_depth, | |
| const BLCameraParams* cam | |
| ) { | |
| if (!src || !dst || !cam) return -1; | |
| size_t count = bl_path_get_size(src); | |
| if (count == 0) return 0; | |
| const BLPoint* vtx_data = bl_path_get_vertex_data(src); | |
| const uint8_t* cmd_data = bl_path_get_command_data(src); | |
| bl_path_clear(dst); | |
| double total_depth = 0.0; | |
| size_t depth_samples = 0; | |
| bool in_figure = false; | |
| for (size_t i = 0; i < count; i++) { | |
| uint8_t cmd = cmd_data[i]; | |
| double vx = vtx_data[i].x; | |
| double vy = vtx_data[i].y; | |
| if (cmd == 5) { // Close | |
| if (in_figure) { | |
| bl_path_close(dst); | |
| in_figure = false; | |
| } | |
| continue; | |
| } | |
| if (std::isnan(vx) || std::isnan(vy) || std::isinf(vx) || std::isinf(vy)) { | |
| continue; | |
| } | |
| double sx = 0.0, sy = 0.0, ds = 0.0; | |
| bool ok = _project_single_point(vx, vy, cam, &sx, &sy, &ds); | |
| if (!ok) { | |
| in_figure = false; | |
| continue; | |
| } | |
| total_depth += ds; | |
| depth_samples++; | |
| if (cmd == 0 || !in_figure) { // MoveTo | |
| bl_path_move_to(dst, sx, sy); | |
| in_figure = true; | |
| } else if (cmd == 1) { // LineTo | |
| bl_path_line_to(dst, sx, sy); | |
| } else { | |
| bl_path_line_to(dst, sx, sy); | |
| } | |
| } | |
| if (out_avg_depth) { | |
| *out_avg_depth = (depth_samples > 0) ? (total_depth / depth_samples) : 1.0; | |
| } | |
| return 0; | |
| } | |
| // ============================================================================ | |
| // 2. High-Performance 3D Spherical Globe Renderer (Ray-Sphere Texture Mapping) | |
| // ============================================================================ | |
| static inline uint32_t _sample_bilinear_rgba32( | |
| const uint32_t* src, | |
| int width, int height, int stride_bytes, | |
| double u, double v | |
| ) { | |
| u = std::fmod(u, 1.0); | |
| if (u < 0.0) u += 1.0; | |
| v = std::max(0.0, std::min(1.0, v)); | |
| double px = u * (width - 1); | |
| double py = v * (height - 1); | |
| int x0 = (int)px; | |
| int y0 = (int)py; | |
| int x1 = std::min(x0 + 1, width - 1); | |
| int y1 = std::min(y0 + 1, height - 1); | |
| double fx = px - x0; | |
| double fy = py - y0; | |
| int stride_pixels = stride_bytes / 4; | |
| uint32_t c00 = src[y0 * stride_pixels + x0]; | |
| uint32_t c10 = src[y0 * stride_pixels + x1]; | |
| uint32_t c01 = src[y1 * stride_pixels + x0]; | |
| uint32_t c11 = src[y1 * stride_pixels + x1]; | |
| auto unpack_lerp = [&](int shift) -> uint32_t { | |
| double v00 = (c00 >> shift) & 0xFF; | |
| double v10 = (c10 >> shift) & 0xFF; | |
| double v01 = (c01 >> shift) & 0xFF; | |
| double v11 = (c11 >> shift) & 0xFF; | |
| double top = v00 + (v10 - v00) * fx; | |
| double bot = v01 + (v11 - v01) * fx; | |
| double val = top + (bot - top) * fy; | |
| return (uint32_t)std::max(0.0, std::min(255.0, val)); | |
| }; | |
| uint32_t a = unpack_lerp(24); | |
| uint32_t r = unpack_lerp(16); | |
| uint32_t g = unpack_lerp(8); | |
| uint32_t b = unpack_lerp(0); | |
| return (a << 24) | (r << 16) | (g << 8) | b; | |
| } | |
| BL_PERSPECTIVE_EXPORT int bl_perspective_render_globe( | |
| uint32_t* dst_pixels, | |
| int dst_w, int dst_h, int dst_stride_bytes, | |
| const uint32_t* src_texture, | |
| int src_w, int src_h, int src_stride_bytes, | |
| double globe_cx, double globe_cy, double globe_radius, | |
| double rot_lon_deg, double rot_lat_deg, | |
| uint32_t glow_color | |
| ) { | |
| if (!dst_pixels || !src_texture || dst_w <= 0 || dst_h <= 0 || globe_radius <= 0.0) { | |
| return -1; | |
| } | |
| int dst_stride_px = dst_stride_bytes / 4; | |
| double r_sq = globe_radius * globe_radius; | |
| double inv_radius = 1.0 / globe_radius; | |
| double rot_lon_rad = rot_lon_deg * DEG_TO_RAD; | |
| double rot_lat_rad = rot_lat_deg * DEG_TO_RAD; | |
| double c_lat = std::cos(rot_lat_rad), s_lat = std::sin(rot_lat_rad); | |
| int min_y = std::max(0, (int)std::floor(globe_cy - globe_radius * 1.15)); | |
| int max_y = std::min(dst_h - 1, (int)std::ceil(globe_cy + globe_radius * 1.15)); | |
| int min_x = std::max(0, (int)std::floor(globe_cx - globe_radius * 1.15)); | |
| int max_x = std::min(dst_w - 1, (int)std::ceil(globe_cx + globe_radius * 1.15)); | |
| uint32_t glow_a = (glow_color >> 24) & 0xFF; | |
| uint32_t glow_r = (glow_color >> 16) & 0xFF; | |
| uint32_t glow_g = (glow_color >> 8) & 0xFF; | |
| uint32_t glow_b = glow_color & 0xFF; | |
| for (int y = min_y; y <= max_y; y++) { | |
| double dy = (double)y - globe_cy; | |
| double dy_sq = dy * dy; | |
| for (int x = min_x; x <= max_x; x++) { | |
| double dx = (double)x - globe_cx; | |
| double dist_sq = dx * dx + dy_sq; | |
| double dist = std::sqrt(dist_sq); | |
| if (dist_sq <= r_sq) { | |
| // Ray intersects front sphere hemisphere: z = sqrt(R^2 - x^2 - y^2) | |
| double dz = std::sqrt(r_sq - dist_sq); | |
| // Local normal (nx, ny, nz) | |
| double nx = dx * inv_radius; | |
| double ny = dy * inv_radius; | |
| double nz = dz * inv_radius; | |
| // Latitude & Longitude rotation | |
| double ny2 = ny * c_lat - nz * s_lat; | |
| double nz2 = ny * s_lat + nz * c_lat; | |
| double nx2 = nx; | |
| double lat = std::asin(std::max(-1.0, std::min(1.0, -ny2))); | |
| double lon = std::atan2(nx2, nz2) + rot_lon_rad; | |
| // Map to UV coordinates | |
| double u = (lon / (2.0 * PI)) + 0.5; | |
| double v = 0.5 - (lat / PI); | |
| uint32_t tex_col = _sample_bilinear_rgba32(src_texture, src_w, src_h, src_stride_bytes, u, v); | |
| // Shading: limb darkening & atmospheric edge glow | |
| double limb = 1.0 - nz; | |
| double shade = 0.6 + 0.4 * nz; | |
| uint32_t a = (tex_col >> 24) & 0xFF; | |
| uint32_t r = (uint32_t)(((tex_col >> 16) & 0xFF) * shade); | |
| uint32_t g = (uint32_t)(((tex_col >> 8) & 0xFF) * shade); | |
| uint32_t b = (uint32_t)((tex_col & 0xFF) * shade); | |
| // Add subtle atmosphere rim | |
| if (limb > 0.6 && glow_a > 0) { | |
| double rim_factor = (limb - 0.6) / 0.4; | |
| r = (uint32_t)std::min(255.0, r + glow_r * rim_factor * 0.5); | |
| g = (uint32_t)std::min(255.0, g + glow_g * rim_factor * 0.5); | |
| b = (uint32_t)std::min(255.0, b + glow_b * rim_factor * 0.5); | |
| } | |
| dst_pixels[y * dst_stride_px + x] = (a << 24) | (r << 16) | (g << 8) | b; | |
| } else if (dist < globe_radius * 1.15 && glow_a > 0) { | |
| // Atmospheric Outer Glow Halo | |
| double halo_prog = 1.0 - (dist - globe_radius) / (globe_radius * 0.15); | |
| double alpha_factor = std::pow(halo_prog, 2.5); | |
| uint32_t cur_pixel = dst_pixels[y * dst_stride_px + x]; | |
| uint32_t cur_a = (cur_pixel >> 24) & 0xFF; | |
| uint32_t cur_r = (cur_pixel >> 16) & 0xFF; | |
| uint32_t cur_g = (cur_pixel >> 8) & 0xFF; | |
| uint32_t cur_b = cur_pixel & 0xFF; | |
| uint32_t out_r = (uint32_t)std::min(255.0, cur_r + glow_r * alpha_factor); | |
| uint32_t out_g = (uint32_t)std::min(255.0, cur_g + glow_g * alpha_factor); | |
| uint32_t out_b = (uint32_t)std::min(255.0, cur_b + glow_b * alpha_factor); | |
| uint32_t out_a = (uint32_t)std::min(255.0, cur_a + glow_a * alpha_factor); | |
| dst_pixels[y * dst_stride_px + x] = (out_a << 24) | (out_r << 16) | (out_g << 8) | out_b; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| } // extern "C" | |