From 6819bf22139499298b24bde9cae60136d33e1b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Tue, 10 Mar 2026 00:15:28 -0400 Subject: [PATCH 1/8] halp: switch from setting location to setting semantic attributes --- include/avnd/binding/ossia/geometry.hpp | 204 +++++++++++++++++++++--- include/halp/geometry.hpp | 146 ++++++++++++++++- 2 files changed, 319 insertions(+), 31 deletions(-) diff --git a/include/avnd/binding/ossia/geometry.hpp b/include/avnd/binding/ossia/geometry.hpp index 624a71ca..f562b98a 100644 --- a/include/avnd/binding/ossia/geometry.hpp +++ b/include/avnd/binding/ossia/geometry.hpp @@ -20,35 +20,161 @@ struct is_shared_ptr> : std::true_type { }; +template +constexpr ossia::attribute_semantic semantic_for_attribute(const Attr& attr) +{ + // clang-format off + static constexpr auto m = AVND_ENUM_OR_TAG_MATCHER(semantic, + // Core geometry + (ossia::attribute_semantic::position, position, positions, pos, P), + (ossia::attribute_semantic::normal, normal, normals, norm, N), + (ossia::attribute_semantic::tangent, tangent, tangents, tang, T), + (ossia::attribute_semantic::bitangent, bitangent, bitangents, binormal, binormals, B), + + // Basic materials + (ossia::attribute_semantic::texcoord0, texcoord, tex_coord, texcoords, texcoord0, tex_coord0, uv, uv0), + (ossia::attribute_semantic::texcoord1, texcoord1, tex_coord1, uv1, map1), + (ossia::attribute_semantic::texcoord2, texcoord2, tex_coord2, uv2, map2), + (ossia::attribute_semantic::texcoord3, texcoord3, tex_coord3, uv3, map3), + (ossia::attribute_semantic::texcoord4, texcoord4, tex_coord4, uv4, map4), + (ossia::attribute_semantic::texcoord5, texcoord5, tex_coord5, uv5, map5), + (ossia::attribute_semantic::texcoord6, texcoord6, tex_coord6, uv6, map6), + (ossia::attribute_semantic::texcoord7, texcoord7, tex_coord7, uv7, map7), + + (ossia::attribute_semantic::color0, color, colors, color0, col, Cd), + (ossia::attribute_semantic::color1, color1, colors1, col1), + (ossia::attribute_semantic::color2, color2, colors2, col2), + (ossia::attribute_semantic::color3, color3, colors3, col3), + + // Skinning / skeletal animation + (ossia::attribute_semantic::joints0, joints, joints0, bone_indices, blendindices, blendindices0), + (ossia::attribute_semantic::joints1, joints1, bone_indices1, blendindices1), + (ossia::attribute_semantic::weights0, weights, weights0, bone_weights, blendweights, blendweights0), + (ossia::attribute_semantic::weights1, weights1, bone_weights1, blendweights1), + + // Morph targets / blend shapes + (ossia::attribute_semantic::morph_position, morph_position, blendshape_position, morph_pos), + (ossia::attribute_semantic::morph_normal, morph_normal, blendshape_normal, morph_norm), + (ossia::attribute_semantic::morph_tangent, morph_tangent, blendshape_tangent, morph_tang), + (ossia::attribute_semantic::morph_texcoord, morph_texcoord, morph_uv, blendshape_uv), + (ossia::attribute_semantic::morph_color, morph_color, blendshape_color), + + // Transform / instancing + (ossia::attribute_semantic::rotation, rotation, rot, quaternion, orient, orientation), + (ossia::attribute_semantic::rotation_extra, rotation_extra, rot_extra), + (ossia::attribute_semantic::scale, scale, scales), + (ossia::attribute_semantic::uniform_scale, uniform_scale, pscale), + (ossia::attribute_semantic::up, up, up_vector, upvector), + (ossia::attribute_semantic::pivot, pivot, origin), + (ossia::attribute_semantic::transform_matrix, transform_matrix, transform, matrix, model_matrix), + (ossia::attribute_semantic::translation, translation, trans, offset), + + // Particle dynamics + (ossia::attribute_semantic::velocity, velocity, vel, v), + (ossia::attribute_semantic::acceleration, acceleration, accel), + (ossia::attribute_semantic::force, force, forces, f), + (ossia::attribute_semantic::mass, mass, m), + (ossia::attribute_semantic::age, age), + (ossia::attribute_semantic::lifetime, lifetime, life), + (ossia::attribute_semantic::birth_time, birth_time, creation_time), + (ossia::attribute_semantic::particle_id, particle_id, pid), + (ossia::attribute_semantic::drag, drag, air_resistance), + (ossia::attribute_semantic::angular_velocity, angular_velocity, ang_vel, w), + (ossia::attribute_semantic::previous_position, previous_position, prev_pos, ppos), + (ossia::attribute_semantic::rest_position, rest_position, rest, Pref), + (ossia::attribute_semantic::target_position, target_position, target, goal), + (ossia::attribute_semantic::previous_velocity, previous_velocity, prev_vel, pvel), + (ossia::attribute_semantic::state, state, status), + (ossia::attribute_semantic::collision_count, collision_count, num_collisions), + (ossia::attribute_semantic::collision_normal, collision_normal, hit_normal), + (ossia::attribute_semantic::sleep, sleep, sleeping, dormant), + + // Rendering hints + (ossia::attribute_semantic::sprite_size, sprite_size, size, dimensions), + (ossia::attribute_semantic::sprite_rotation, sprite_rotation, sprite_rot), + (ossia::attribute_semantic::sprite_facing, sprite_facing, facing), + (ossia::attribute_semantic::sprite_index, sprite_index, frame, frame_index), + (ossia::attribute_semantic::width, width, curve_thickness, radius), + (ossia::attribute_semantic::opacity, opacity, alpha), + (ossia::attribute_semantic::emissive, emissive, emission, incandescence), + (ossia::attribute_semantic::emissive_strength, emissive_strength, emission_strength, emission_multiplier), + + // Material / PBR + (ossia::attribute_semantic::roughness, roughness, rough), + (ossia::attribute_semantic::metallic, metallic, metalness, metal), + (ossia::attribute_semantic::ambient_occlusion, ambient_occlusion, ao, occlusion), + (ossia::attribute_semantic::specular, specular, spec), + (ossia::attribute_semantic::subsurface, subsurface, sss, subsurface_scattering), + (ossia::attribute_semantic::clearcoat, clearcoat, clear_coat), + (ossia::attribute_semantic::clearcoat_roughness, clearcoat_roughness, clear_coat_roughness), + (ossia::attribute_semantic::anisotropy, anisotropy, aniso), + (ossia::attribute_semantic::anisotropy_direction, anisotropy_direction, aniso_dir, anisotropy_rotation), + (ossia::attribute_semantic::ior, ior, index_of_refraction), + (ossia::attribute_semantic::transmission, transmission, transm), + (ossia::attribute_semantic::thickness, thickness, material_thickness, volume_thickness), + (ossia::attribute_semantic::material_id, material_id, mat_id, material_index), + + // Gaussian splatting + (ossia::attribute_semantic::sh_dc, sh_dc, f_dc, dc), + (ossia::attribute_semantic::sh_coeffs, sh_coeffs, f_rest, sh, spherical_harmonics), + (ossia::attribute_semantic::covariance_3d, covariance_3d, covariance, cov3d), + (ossia::attribute_semantic::sh_degree, sh_degree, degree), + + // Volumetric / field data + (ossia::attribute_semantic::density, density, dens), + (ossia::attribute_semantic::temperature, temperature, temp), + (ossia::attribute_semantic::fuel, fuel), + (ossia::attribute_semantic::pressure, pressure), + (ossia::attribute_semantic::divergence, divergence, div), + (ossia::attribute_semantic::sdf_distance, sdf_distance, sdf, distance), + (ossia::attribute_semantic::voxel_color, voxel_color, vcolor), + + // Topology / connectivity + (ossia::attribute_semantic::name, name, string_name), + (ossia::attribute_semantic::piece_id, piece_id, piece), + (ossia::attribute_semantic::line_id, line_id, curve_id), + (ossia::attribute_semantic::prim_id, prim_id, primitive_id), + (ossia::attribute_semantic::point_id, point_id, ptnum, id), + (ossia::attribute_semantic::group_mask, group_mask, group), + (ossia::attribute_semantic::instance_id, instance_id, instance), + + // UI + (ossia::attribute_semantic::selection, selection, selected, soft_selection), + + // User / general purpose + (ossia::attribute_semantic::fx0, fx0), + (ossia::attribute_semantic::fx1, fx1), + (ossia::attribute_semantic::fx2, fx2), + (ossia::attribute_semantic::fx3, fx3), + (ossia::attribute_semantic::fx4, fx4), + (ossia::attribute_semantic::fx5, fx5), + (ossia::attribute_semantic::fx6, fx6), + (ossia::attribute_semantic::fx7, fx7) + ); + // clang-format on + return m(attr, ossia::attribute_semantic::custom); +} + template constexpr int standard_location_for_attribute(int k) { - if constexpr(requires { Attr::position; } || requires { Attr::positions; }) - return 0; - else if constexpr(requires { Attr::texcoord; } || requires { - Attr::tex_coord; - } || requires { Attr::texcoords; } || requires { - Attr::tex_coords; - } || requires { Attr::uv; } || requires { Attr::uvs; }) - return 1; - else if constexpr(requires { Attr::color; } || requires { Attr::colors; }) - return 2; - else if constexpr(requires { Attr::normal; } || requires { Attr::normals; }) - return 3; - else if constexpr(requires { Attr::tangent; } || requires { Attr::tangents; }) - return 4; - else - return k; + // Location is assigned linearly by the caller (k = index in the attribute list). + // Semantic matching is done by the ossia::attribute_semantic enum, not by location. + return k; } template constexpr auto get_topology(const T& t) -> decltype(ossia::geometry::topology) { + // clang-format off static_constexpr auto m = AVND_ENUM_OR_TAG_MATCHER( - topology, (ossia::geometry::triangles, triangle, triangles), + topology, + (ossia::geometry::triangles, triangle, triangles), (ossia::geometry::triangle_strip, triangle_strip, triangles_strip), (ossia::geometry::triangle_fan, triangle_fan, triangles_fan), - (ossia::geometry::lines, line, lines), (ossia::geometry::points, point, points)); + (ossia::geometry::lines, line, lines), + (ossia::geometry::points, point, points)); + // clang-format on return m(t, ossia::geometry::triangles); } @@ -298,12 +424,41 @@ void load_geometry(T& ctrl, ossia::geometry& geom) avnd::for_each_field_ref(avnd::get_attributes(ctrl), [&](A& attr) { ossia::geometry::attribute a; + a.semantic = semantic_for_attribute(attr); + // FIXME support string semantic attributes for the custom case + // FIXME very brittle if constexpr(requires { a.location = static_cast(attr.location); }) a.location = static_cast(attr.location); else a.location = standard_location_for_attribute(k++); + // Infer semantic from halp::attribute_location for backward compatibility + // halp::attribute_location values: position=0, tex_coord=1, color=2, normal=3, tangent=4 + if(a.semantic == ossia::attribute_semantic::custom) + { + switch(a.location) + { + case 0: // halp::attribute_location::position + a.semantic = ossia::attribute_semantic::position; + break; + case 1: // halp::attribute_location::tex_coord + a.semantic = ossia::attribute_semantic::texcoord0; + break; + case 2: // halp::attribute_location::color + a.semantic = ossia::attribute_semantic::color0; + break; + case 3: // halp::attribute_location::normal + a.semantic = ossia::attribute_semantic::normal; + break; + case 4: // halp::attribute_location::tangent + a.semantic = ossia::attribute_semantic::tangent; + break; + default: + break; + } + } + if constexpr(requires { attr.binding; }) a.binding = attr.binding; @@ -756,14 +911,15 @@ void mesh_from_ossia( { using value_type = typename decltype(dst.attributes)::value_type; using binding_type = decltype(value_type::binding); - using location_type = decltype(value_type::location); + using semantic_type = decltype(value_type::semantic); using format_type = decltype(value_type::format); using offset_type = decltype(value_type::byte_offset); - dst.attributes.push_back( - {.binding = static_cast(in.binding), - .location = static_cast(in.location), - .format = static_cast(in.format), - .byte_offset = static_cast(in.byte_offset)}); + auto& a = dst.attributes.emplace_back( + value_type{ + .binding = static_cast(in.binding), + .semantic = static_cast(in.semantic), + .format = static_cast(in.format), + .byte_offset = static_cast(in.byte_offset)}); } } diff --git a/include/halp/geometry.hpp b/include/halp/geometry.hpp index 4ae8554d..46a31d57 100644 --- a/include/halp/geometry.hpp +++ b/include/halp/geometry.hpp @@ -12,13 +12,145 @@ enum class binding_classification : uint8_t per_vertex, per_instance }; -enum class attribute_location : uint32_t +enum class attribute_semantic : uint32_t { - position = 0, - tex_coord = 1, - color = 2, - normal = 3, - tangent = 4 + // Core geometry + position = 0, // vec3. Object-space position. + normal = 1, // vec3. Surface normal. + tangent = 2, // vec4. xyz=tangent, w=handedness (±1). [glTF TANGENT] + bitangent = 3, // vec3. cross(N, T.xyz) * T.w. + + // Basic materials + texcoord0 = 100, // vec2/3. Primary UV. [glTF TEXCOORD_0] + texcoord1 = texcoord0 + 1, // vec2. Secondary UV (lightmaps). [glTF TEXCOORD_1] + texcoord2 = texcoord0 + 2, // vec2. Secondary UV. [glTF TEXCOORD_2] + texcoord3 = texcoord0 + 3, // vec2. Secondary UV. [glTF TEXCOORD_3] + texcoord4 = texcoord0 + 4, // vec2. Secondary UV. [glTF TEXCOORD_4] + texcoord5 = texcoord0 + 5, // vec2. Secondary UV. + texcoord6 = texcoord0 + 6, // vec2. Secondary UV. + texcoord7 = texcoord0 + 7, // vec2. Secondary UV. + + color0 = 200, // vec4. Vertex color RGBA. [glTF COLOR_0] + color1 = color0 + 1, // vec4. Secondary vertex color. [glTF COLOR_1] + color2 = color0 + 2, // vec4. Secondary vertex color. + color3 = color0 + 3, // vec4. Secondary vertex color. + + // Skinning / skeletal animation + joints0 = 300, // uvec4. Bone indices, set 0. [glTF JOINTS_0] + joints1 = joints0 + 1, // uvec4. Bone indices, set 1. [glTF JOINTS_1] + + weights0 = 400, // vec4. Bone weights, set 0. [glTF WEIGHTS_0] + weights1 = weights0 + 1, // vec4. Bone weights, set 1. [glTF WEIGHTS_1] + + // Morph targets / blend shapes + morph_position = 500, // vec3. Position delta for morph target. + morph_normal = morph_position + 1, // vec3. Normal delta for morph target. + morph_tangent + = morph_position + 2, // vec3. Tangent delta (no w). [glTF morph TANGENT] + morph_texcoord = morph_position + 3, // vec2. UV delta for morph target. + morph_color = morph_position + 4, // vec3/4. Color delta for morph target. + + // Transform / instancing + rotation = 600, // vec4. Quaternion (x,y,z,w). + rotation_extra = morph_position + 1, // vec4. Post-orient rotation. + scale = morph_position + 2, // vec3. Non-uniform scale. + uniform_scale = morph_position + 3, // float. Uniform scale. + up = morph_position + 4, // vec3. Up vector for LookAt. + pivot = morph_position + 5, // vec3. Local pivot point. + transform_matrix + = morph_position + + 6, // mat4. Full transform, overrides TRS. (note: remember that mat4 takes 4 lanes of attributes) + translation = morph_position + 7, // vec3. Additional translation offset. + + // Particle dynamics + velocity = 1000, // vec3. Velocity in units/sec. + acceleration = velocity + 1, // vec3. Current acceleration. + force = velocity + 2, // vec3. Accumulated force this frame. + mass = velocity + 3, // float. + age = velocity + 4, // float. Time since birth, seconds. + lifetime = velocity + 5, // float. Max age before death. + birth_time = velocity + 6, // float. Absolute time of birth. + particle_id = velocity + 7, // int. Stable unique ID. + drag = velocity + 8, // float. Per-particle drag coefficient. + angular_velocity = velocity + 9, // vec3. Rotation speed rad/sec. + previous_position = velocity + 10, // vec3. For Verlet / motion blur. + rest_position = velocity + 11, // vec3. Undeformed position. + target_position = velocity + 12, // vec3. Goal position for constraints. + previous_velocity = velocity + 13, // vec3. Velocity at previous frame. + state = velocity + 14, // int. alive/dying/dead/collided enum. + collision_count = velocity + 15, // int. Number of collisions. + collision_normal = velocity + 16, // vec3. Normal at last collision. + sleep = velocity + 17, // int. Dormant flag (skip simulation). + + // Rendering hints + sprite_size = 1100, // vec2. Billboard width/height. + sprite_rotation = sprite_size + 1, // float. Billboard screen-space rotation. + sprite_facing = sprite_size + 2, // vec3. Custom billboard facing direction. + sprite_index = sprite_size + 3, // int/float. Sub-image index for sprite sheets. + width = sprite_size + 4, // float. Curve/ribbon thickness. + opacity = sprite_size + 5, // float. Separate from color alpha. + emissive = sprite_size + 6, // vec3. Self-illumination color. + emissive_strength = sprite_size + 7, // float. Emissive intensity multiplier. + + // Material / PBR + roughness = 1200, // float. PBR roughness [0-1]. + metallic = roughness + 1, // float. PBR metalness [0-1]. + ambient_occlusion = roughness + 2, // float. Baked AO [0-1]. + specular = roughness + 3, // float. Specular factor. + subsurface = roughness + 4, // float. SSS intensity. + clearcoat = roughness + 5, // float. Clearcoat factor. + clearcoat_roughness = roughness + 6, // float. Clearcoat roughness. + anisotropy = roughness + 7, // float. Anisotropic reflection. + anisotropy_direction = roughness + 8, // vec3. Anisotropy tangent direction. + ior = roughness + 9, // float. Index of refraction. + transmission = roughness + 10, // float. Transmission factor (glass-like). + thickness = roughness + 11, // float. Volume thickness for transmission. + material_id = roughness + 22, // int. Index into material array. + + // Gaussian splatting + sh_dc = 1300, // vec3. SH degree-0 (DC) color. + sh_coeffs = sh_dc + 1, // float[N]. SH coefficients for higher degrees. + covariance_3d = sh_dc + 2, // vec6 or mat3. 3D covariance (6 unique floats). + sh_degree = sh_dc + 3, // int. Active SH degree for this splat (0-3). + + // Volumetric / field data + density = 1400, // float. Scalar density. + temperature = density + 1, // float. + fuel = density + 2, // float. + pressure = density + 3, // float. + divergence = density + 4, // float. + sdf_distance = density + 5, // float. Signed distance field value. + voxel_color = density + 6, // vec4. Per-voxel RGBA. + + // Topology / connectivity + name = 1600, // string. Piece/group identifier. + piece_id = name + 1, // int. Numeric piece/group index. + line_id = name + 2, // int. Which line strip this point belongs to. + prim_id = name + 3, // int. Source primitive index. + point_id = name + 4, // int. Stable point ID (distinct from array index). + group_mask = name + 5, // int. Bitfield for group membership. + instance_id = name + 6, // int. Which instance this element belongs to. + + // UI + selection = 1700, // float. Soft selection weight [0-1]. + + // User / general purpose + fx0 = 2000, // float. General-purpose effect control. + fx1 = fx0 + 1, // float. General-purpose effect control. + fx2 = fx0 + 2, // float. General-purpose effect control. + fx3 = fx0 + 3, // float. General-purpose effect control. + fx4 = fx0 + 4, // float. General-purpose effect control. + fx5 = fx0 + 5, // float. General-purpose effect control. + fx6 = fx0 + 6, // float. General-purpose effect control. + fx7 = fx0 + 7, // float. General-purpose effect control. + + // Custom (string name lookup) + custom = 0xFFFF + // position = 0, + // tex_coord = 1, + // color = 2, + // normal = 3, + // tangent = 4 }; enum class attribute_format : uint8_t { @@ -101,7 +233,7 @@ struct geometry_binding struct geometry_attribute { int binding = 0; - halp::attribute_location location{}; + halp::attribute_semantic semantic{}; halp::attribute_format format{}; int32_t byte_offset{}; }; From af42161d58321d9b242f92318e3d3227583acc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Wed, 8 Apr 2026 01:06:44 +0200 Subject: [PATCH 2/8] geometry: support for aux buffers --- include/avnd/binding/ossia/geometry.hpp | 46 ++++++++++--------------- include/halp/geometry.hpp | 12 +++++++ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/include/avnd/binding/ossia/geometry.hpp b/include/avnd/binding/ossia/geometry.hpp index f562b98a..02980c48 100644 --- a/include/avnd/binding/ossia/geometry.hpp +++ b/include/avnd/binding/ossia/geometry.hpp @@ -418,6 +418,20 @@ void load_geometry(T& ctrl, ossia::geometry& geom) }); } + // Auxiliary buffers (palettes, lookup tables, etc.) + if constexpr(requires { ctrl.auxiliary; }) + { + geom.auxiliary.clear(); + for(const auto& aux : ctrl.auxiliary) + { + geom.auxiliary.push_back({ + .name = aux.name, + .buffer = aux.buffer, + .byte_offset = aux.byte_offset, + .byte_size = aux.byte_size}); + } + } + if(geom.attributes.empty()) { std::size_t k = 0; @@ -425,7 +439,11 @@ void load_geometry(T& ctrl, ossia::geometry& geom) ossia::geometry::attribute a; a.semantic = semantic_for_attribute(attr); - // FIXME support string semantic attributes for the custom case + + // Propagate custom name for string-based matching + if constexpr(requires { attr.name; }) + if(!attr.name.empty()) + a.name = attr.name; // FIXME very brittle if constexpr(requires { a.location = static_cast(attr.location); }) @@ -433,32 +451,6 @@ void load_geometry(T& ctrl, ossia::geometry& geom) else a.location = standard_location_for_attribute(k++); - // Infer semantic from halp::attribute_location for backward compatibility - // halp::attribute_location values: position=0, tex_coord=1, color=2, normal=3, tangent=4 - if(a.semantic == ossia::attribute_semantic::custom) - { - switch(a.location) - { - case 0: // halp::attribute_location::position - a.semantic = ossia::attribute_semantic::position; - break; - case 1: // halp::attribute_location::tex_coord - a.semantic = ossia::attribute_semantic::texcoord0; - break; - case 2: // halp::attribute_location::color - a.semantic = ossia::attribute_semantic::color0; - break; - case 3: // halp::attribute_location::normal - a.semantic = ossia::attribute_semantic::normal; - break; - case 4: // halp::attribute_location::tangent - a.semantic = ossia::attribute_semantic::tangent; - break; - default: - break; - } - } - if constexpr(requires { attr.binding; }) a.binding = attr.binding; diff --git a/include/halp/geometry.hpp b/include/halp/geometry.hpp index 46a31d57..88ce61a4 100644 --- a/include/halp/geometry.hpp +++ b/include/halp/geometry.hpp @@ -236,6 +236,7 @@ struct geometry_attribute halp::attribute_semantic semantic{}; halp::attribute_format format{}; int32_t byte_offset{}; + std::string name; // For custom semantics; empty = use semantic name }; struct geometry_input @@ -244,6 +245,16 @@ struct geometry_input int64_t byte_offset{}; }; +// Named reference to a buffer that is not a vertex attribute. +// Used for lookup tables, palettes, indirect args, etc. +struct geometry_auxiliary_buffer +{ + std::string name; // Shader-visible name for matching + int buffer{-1}; // Index into the buffers array + int64_t byte_offset{}; + int64_t byte_size{}; +}; + template struct rect2d { @@ -1025,6 +1036,7 @@ struct dynamic_geometry std::vector bindings; std::vector attributes; std::vector input; + std::vector auxiliary; int vertices = 0; int indices = 0; From 3cf6879f8723caa771ef87fe244d28e627fb3cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sat, 11 Apr 2026 22:03:06 +0200 Subject: [PATCH 3/8] led view: RGB01 mode --- examples/Advanced/UI/LEDView.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/examples/Advanced/UI/LEDView.hpp b/examples/Advanced/UI/LEDView.hpp index 05b0fc70..0eff097a 100644 --- a/examples/Advanced/UI/LEDView.hpp +++ b/examples/Advanced/UI/LEDView.hpp @@ -61,6 +61,7 @@ enum LEDInputMode RGBW, Lightness01, Lightness8bit, + RGB01 }; struct LEDView { @@ -173,6 +174,17 @@ struct LEDView } break; } + case LEDInputMode::RGB01: { + auto& vec = *list; + for(int i = 0, N = vec.size() - 2; i < N; i += 3) + { + const auto r = std::clamp(ossia::convert(vec[i]), 0.f, 1.f); + const auto g = std::clamp(ossia::convert(vec[i + 1]), 0.f, 1.f); + const auto b = std::clamp(ossia::convert(vec[i + 2]), 0.f, 1.f); + m_pixels.push_back(QColor::fromRgbF(r, g, b)); + } + break; + } case LEDInputMode::RGBW: { auto& vec = *list; for(int i = 0, N = vec.size() - 3; i < N; i += 4) @@ -236,6 +248,8 @@ struct LEDView m_display_mode = LEDInputMode::RGB; else if(m_mode == "RGBW") m_display_mode = LEDInputMode::RGBW; + else if(m_mode == "RGB01") + m_display_mode = LEDInputMode::RGB01; else if(m_mode == "Lightness01") m_display_mode = LEDInputMode::Lightness01; else if(m_mode == "Lightness8bit") From 8154e42f73743bd8d98fe4decfa09932e4515bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sat, 11 Apr 2026 22:06:22 +0200 Subject: [PATCH 4/8] gpu: render target out --- include/avnd/binding/ossia/port_setup.hpp | 5 +++++ include/avnd/concepts/gfx.hpp | 4 ++++ include/avnd/introspection/port.hpp | 2 ++ include/halp/texture.hpp | 24 ++++++++++++++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/avnd/binding/ossia/port_setup.hpp b/include/avnd/binding/ossia/port_setup.hpp index f7aef79e..eb37924d 100644 --- a/include/avnd/binding/ossia/port_setup.hpp +++ b/include/avnd/binding/ossia/port_setup.hpp @@ -158,6 +158,11 @@ struct get_ossia_outlet_type { using type = ossia::texture_outlet; }; +template +struct get_ossia_outlet_type +{ + using type = ossia::texture_outlet; +}; template struct get_ossia_outlet_type { diff --git a/include/avnd/concepts/gfx.hpp b/include/avnd/concepts/gfx.hpp index da812914..d07cda23 100644 --- a/include/avnd/concepts/gfx.hpp +++ b/include/avnd/concepts/gfx.hpp @@ -116,6 +116,10 @@ concept image_port = requires(T t) { T::image(); }; template concept attachment_port = requires { T::attachment(); }; +template +concept gpu_render_target_output_port + = requires { T::render_target_output(); }; + template concept texture_port = cpu_texture_port || gpu_texture_port || sampler_port || attachment_port || image_port; diff --git a/include/avnd/introspection/port.hpp b/include/avnd/introspection/port.hpp index b50e349f..398b7440 100644 --- a/include/avnd/introspection/port.hpp +++ b/include/avnd/introspection/port.hpp @@ -99,6 +99,7 @@ AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(tensor_port) AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(texture_port) AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(cpu_texture_port) AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(gpu_texture_port) +AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(gpu_render_target_output_port) AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(matrix_port) AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(sampler_port) AVND_PORT_INTROSPECTION_FOR_DYNAMIC_CONCEPT(image_port) @@ -237,6 +238,7 @@ template using buffer_output_introspection = buffer_port_output_intr template using texture_output_introspection = texture_port_output_introspection; template using cpu_texture_output_introspection = cpu_texture_port_output_introspection; template using gpu_texture_output_introspection = gpu_texture_port_output_introspection; +template using gpu_render_target_output_output_introspection = gpu_render_target_output_port_output_introspection; template using matrix_output_introspection = matrix_port_output_introspection; template using gpu_matrix_output_introspection = matrix_port_output_introspection; template using sampler_output_introspection = sampler_port_output_introspection; diff --git a/include/halp/texture.hpp b/include/halp/texture.hpp index 39ac2497..82a3dce1 100644 --- a/include/halp/texture.hpp +++ b/include/halp/texture.hpp @@ -201,8 +201,23 @@ using rgb_texture_output = texture_output; struct gpu_texture { - enum format { RGBA8, RGBA16F, RFGBA32F, R8, R16, R16F, R32F } format{RGBA8}; + enum format_t + { + RGBA8, + RGBA16F, + RGBA32F, + R8, + R16, + R16F, + R32F + } format{RGBA8}; void* handle{}; + + // Used when in render target output ports, to + // know the size of the destination render target. + // TODO maybe split it ? + int width{}; + int height{}; }; template @@ -226,4 +241,11 @@ struct gpu_texture_output halp::gpu_texture texture{}; }; + +template +struct gpu_render_target_output +{ + static clang_buggy_consteval auto name() { return std::string_view{lit.value}; } + static constexpr void render_target_output() { } +}; } From 0f25873d03babe633d001dfc13f22d4005718518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sun, 10 May 2026 23:27:23 -0400 Subject: [PATCH 5/8] lightness computer: fix crash due to misalignment of float vs float4 on windows --- examples/Advanced/Image/LightnessComputer.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/Advanced/Image/LightnessComputer.hpp b/examples/Advanced/Image/LightnessComputer.hpp index fdd70d10..03fee810 100644 --- a/examples/Advanced/Image/LightnessComputer.hpp +++ b/examples/Advanced/Image/LightnessComputer.hpp @@ -21,10 +21,9 @@ struct LightnessComputerTextureDisplay void paint(auto ctx) { - if(m_w <= 0 || m_h <= 0) + if(m_w <= 0 || m_h <= 0 || !m_bytes.get()) return; - // Render the texture ctx.draw_bytes(0, 0, width(), height(), m_bytes.get(), m_w, m_h); ctx.update(); @@ -780,7 +779,11 @@ struct LightnessComputer { if(m_last_ui_buffer_size != in_tex.bytesize()) { - ui_buffer = std::make_shared(in_tex.width * in_tex.height * 4); + const std::size_t n = std::size_t(in_tex.width) * in_tex.height * 4; + ui_buffer = std::shared_ptr{ + static_cast( + ::operator new[](n * sizeof(float), std::align_val_t{16})), + [](float* p) { ::operator delete[](p, std::align_val_t{16}); }}; m_last_ui_buffer_size = in_tex.bytesize(); } From dd8dc6b722f4cea9cdd9968da4e7c565909f8313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sun, 10 May 2026 23:29:28 -0400 Subject: [PATCH 6/8] geometry: add aux tex / buffer --- include/avnd/binding/ossia/geometry.hpp | 57 +++++++++++++++++++++---- include/halp/geometry.hpp | 12 ++++++ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/include/avnd/binding/ossia/geometry.hpp b/include/avnd/binding/ossia/geometry.hpp index 02980c48..c62c9fe1 100644 --- a/include/avnd/binding/ossia/geometry.hpp +++ b/include/avnd/binding/ossia/geometry.hpp @@ -32,14 +32,14 @@ constexpr ossia::attribute_semantic semantic_for_attribute(const Attr& attr) (ossia::attribute_semantic::bitangent, bitangent, bitangents, binormal, binormals, B), // Basic materials - (ossia::attribute_semantic::texcoord0, texcoord, tex_coord, texcoords, texcoord0, tex_coord0, uv, uv0), - (ossia::attribute_semantic::texcoord1, texcoord1, tex_coord1, uv1, map1), - (ossia::attribute_semantic::texcoord2, texcoord2, tex_coord2, uv2, map2), - (ossia::attribute_semantic::texcoord3, texcoord3, tex_coord3, uv3, map3), - (ossia::attribute_semantic::texcoord4, texcoord4, tex_coord4, uv4, map4), - (ossia::attribute_semantic::texcoord5, texcoord5, tex_coord5, uv5, map5), - (ossia::attribute_semantic::texcoord6, texcoord6, tex_coord6, uv6, map6), - (ossia::attribute_semantic::texcoord7, texcoord7, tex_coord7, uv7, map7), + (ossia::attribute_semantic::texcoord0, texcoord, tex_coord, texcoords, texcoord0, texcoord_0, tex_coord0, uv, uv0, uv_0), + (ossia::attribute_semantic::texcoord1, texcoord1, texcoord_1, tex_coord1, uv1, uv_1, map1), + (ossia::attribute_semantic::texcoord2, texcoord2, texcoord_2, tex_coord2, uv2, uv_2, map2), + (ossia::attribute_semantic::texcoord3, texcoord3, texcoord_3, tex_coord3, uv3, uv_3, map3), + (ossia::attribute_semantic::texcoord4, texcoord4, texcoord_4, tex_coord4, uv4, uv_4, map4), + (ossia::attribute_semantic::texcoord5, texcoord5, texcoord_5, tex_coord5, uv5, uv_5, map5), + (ossia::attribute_semantic::texcoord6, texcoord6, texcoord_6, tex_coord6, uv6, uv_6, map6), + (ossia::attribute_semantic::texcoord7, texcoord7, texcoord_7, tex_coord7, uv7, uv_7, map7), (ossia::attribute_semantic::color0, color, colors, color0, col, Cd), (ossia::attribute_semantic::color1, color1, colors1, col1), @@ -913,6 +913,47 @@ void mesh_from_ossia( .format = static_cast(in.format), .byte_offset = static_cast(in.byte_offset)}); } + + if constexpr(requires { dst.auxiliary; }) + { + dst.auxiliary.clear(); + for(const ossia::geometry::auxiliary_buffer& in : src.auxiliary) + { + using value_type = typename decltype(dst.auxiliary)::value_type; + dst.auxiliary.push_back( + value_type{ + .name = in.name, + .buffer = in.buffer, + .byte_offset = in.byte_offset, + .byte_size = in.byte_size}); + } + } + + if constexpr(requires { dst.auxiliary_textures; }) + { + dst.auxiliary_textures.clear(); + for(const ossia::geometry::auxiliary_texture& in : src.auxiliary_textures) + { + using value_type = + typename decltype(dst.auxiliary_textures)::value_type; + + if constexpr(requires(value_type v) { v.sampler_handle; }) + { + dst.auxiliary_textures.push_back( + value_type{ + .name = in.name, + .handle = in.native_handle, + .sampler_handle = in.sampler_handle}); + } + else + { + dst.auxiliary_textures.push_back( + value_type{ + .name = in.name, + .handle = in.native_handle}); + } + } + } } template diff --git a/include/halp/geometry.hpp b/include/halp/geometry.hpp index 88ce61a4..503afed3 100644 --- a/include/halp/geometry.hpp +++ b/include/halp/geometry.hpp @@ -255,6 +255,16 @@ struct geometry_auxiliary_buffer int64_t byte_size{}; }; +// Textures that ride along with a geometry (useful for cubemaps, LUTs, etc.) +struct geometry_auxiliary_texture +{ + std::string name; + void* handle{}; + + // Optional backend-owned sampler associated with this texture. + void* sampler_handle{}; +}; + template struct rect2d { @@ -1060,6 +1070,8 @@ struct dynamic_gpu_geometry std::vector bindings; std::vector attributes; std::vector input; + std::vector auxiliary; + std::vector auxiliary_textures; int vertices = 0; int indices = 0; From 2a51aeff4965c9930cb312f0436f1b2437f88791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sun, 10 May 2026 23:30:01 -0400 Subject: [PATCH 7/8] widgets: xyzw spinbox --- include/avnd/wrappers/widgets.hpp | 20 +++++++++++++++++++- include/halp/controls.hpp | 3 +++ include/halp/controls.sliders.hpp | 29 +++++++++++++++++++++++++++++ include/halp/file_port.hpp | 2 ++ include/halp/texture.hpp | 3 +++ include/halp/value_types.hpp | 15 +++++++++++++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/include/avnd/wrappers/widgets.hpp b/include/avnd/wrappers/widgets.hpp index f088d06a..e9389def 100644 --- a/include/avnd/wrappers/widgets.hpp +++ b/include/avnd/wrappers/widgets.hpp @@ -33,6 +33,8 @@ enum class widget_type xy_spinbox, xyz, xyz_spinbox, + xyzw, + xyzw_spinbox, color, time_chooser, folder, @@ -89,6 +91,10 @@ struct widget_reflection return "xyz"; case widget_type::xyz_spinbox: return "xyz_spinbox"; + case widget_type::xyz: + return "xyzw"; + case widget_type::xyz_spinbox: + return "xyzw_spinbox"; case widget_type::color: return "color"; case widget_type::time_chooser: @@ -316,6 +322,10 @@ consteval auto get_widget() return widget_reflection{widget_type::range_spinbox}; } } + else if constexpr(requires { T::widget::xyzw_spinbox; }) + { + return widget_reflection{widget_type::xyzw_spinbox}; + } else if constexpr(requires { T::widget::xyz_spinbox; }) { return widget_reflection{widget_type::xyz_spinbox}; @@ -326,7 +336,11 @@ consteval auto get_widget() } else if constexpr(requires { T::widget::spinbox; }) { - if constexpr(requires { T::widget::xyz; }) + if constexpr(requires { T::widget::xyzw; }) + { + return widget_reflection{widget_type::xyzw_spinbox}; + } + else if constexpr(requires { T::widget::xyz; }) { return widget_reflection{widget_type::xyz_spinbox}; } @@ -372,6 +386,10 @@ consteval auto get_widget() { return widget_reflection{widget_type::combobox}; } + else if constexpr(requires { T::widget::xyzw; }) + { + return widget_reflection{widget_type::xyzw}; + } else if constexpr(requires { T::widget::xyz; }) { return widget_reflection{widget_type::xyz}; diff --git a/include/halp/controls.hpp b/include/halp/controls.hpp index 851bff54..8e9bde72 100644 --- a/include/halp/controls.hpp +++ b/include/halp/controls.hpp @@ -249,6 +249,9 @@ using xy_spinboxes_i32 = halp::xy_spinboxes_t; template > using xyz_spinboxes_f32 = halp::xyz_spinboxes_t; +template > +using xyzw_spinboxes_f32 = halp::xyzw_spinboxes_t; + template > using hbargraph_f32 = halp::hbargraph_t; template > diff --git a/include/halp/controls.sliders.hpp b/include/halp/controls.sliders.hpp index 74509aae..524ed744 100644 --- a/include/halp/controls.sliders.hpp +++ b/include/halp/controls.sliders.hpp @@ -204,6 +204,35 @@ struct xyz_spinboxes_t } }; +template +struct xyzw_spinboxes_t +{ + using value_type = xyzw_type; + enum widget + { + xyzw, + spinbox + }; + static clang_buggy_consteval auto range() noexcept + { + return range_t{.min = T(setup.min), .max = T(setup.max), .init = T(setup.init)}; + } + static clang_buggy_consteval auto name() noexcept + { + return std::string_view{lit.value}; + } + + value_type value = {T(setup.init), T(setup.init), T(setup.init)}; + + operator value_type&() noexcept { return value; } + operator const value_type&() const noexcept { return value; } + auto& operator=(value_type t) noexcept + { + value = t; + return *this; + } +}; + /// 1D range /// struct range_slider_range diff --git a/include/halp/file_port.hpp b/include/halp/file_port.hpp index b7254860..466b7ca6 100644 --- a/include/halp/file_port.hpp +++ b/include/halp/file_port.hpp @@ -8,8 +8,10 @@ #include #include +#include #include #include + HALP_MODULE_EXPORT namespace halp { diff --git a/include/halp/texture.hpp b/include/halp/texture.hpp index 82a3dce1..0b76bd9e 100644 --- a/include/halp/texture.hpp +++ b/include/halp/texture.hpp @@ -4,10 +4,13 @@ #include #include +#include #include #include #include #include + +#include #include HALP_MODULE_EXPORT diff --git a/include/halp/value_types.hpp b/include/halp/value_types.hpp index 2f27026f..d671b437 100644 --- a/include/halp/value_types.hpp +++ b/include/halp/value_types.hpp @@ -49,6 +49,21 @@ struct xyz_type } }; +template +struct xyzw_type +{ + T x, y, z, w; + + constexpr xyzw_type& operator=(T single) noexcept + { + x = single; + y = single; + z = single; + w = single; + return *this; + } +}; + struct color_type { float r, g, b, a; From 628fb5af028c947751c3576b30965da50bbee796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sun, 10 May 2026 23:30:37 -0400 Subject: [PATCH 8/8] textures: more texture port kinds (cubemap etc.) --- include/halp/texture.hpp | 129 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/include/halp/texture.hpp b/include/halp/texture.hpp index 0b76bd9e..082f76c7 100644 --- a/include/halp/texture.hpp +++ b/include/halp/texture.hpp @@ -202,25 +202,90 @@ template using rgb_texture_output = texture_output; +enum class texture_kind : std::uint8_t +{ + texture_2d = 0, + texture_array, + cubemap, + texture_3d, +}; + +template +constexpr texture_kind texture_kind_of() noexcept +{ + if constexpr(requires { F::texture_target(); }) + return F::texture_target(); + else + return texture_kind::texture_2d; +} + +// Opt-in for the upstream's depth attachment to surface on this +// input's `texture.depth_handle`. Reads halp_meta(samplable_depth, true). +template +constexpr bool samplable_depth_of() noexcept +{ + if constexpr(requires { F::samplable_depth(); }) + return F::samplable_depth(); + else + return false; +} + struct gpu_texture { enum format_t { RGBA8, - RGBA16F, - RGBA32F, + BGRA8, R8, + RG8, + R16, + RG16, + + RGBA16F, + RGBA32F, R16F, - R32F + R32F, + + RGB10A2, + + R8UI, + R32UI, + RG32UI, + RGBA32UI, + + R8SI, + R32SI, + RG32SI, + RGBA32SI } format{RGBA8}; void* handle{}; + // Optional companion depth attachment. + enum class depth_format_t : std::uint8_t + { + D16, + D24, + D24S8, + D32F + } depth_format{depth_format_t::D32F}; + void* depth_handle{}; + // Used when in render target output ports, to // know the size of the destination render target. // TODO maybe split it ? int width{}; int height{}; + + // Producers that allocate non-2D textures (cube, array, 3D) should set this so + // consumers which don't care about the specific port type can still route correctly. + texture_kind kind{texture_kind::texture_2d}; + + // For arrays / 3D / cube the depth-or-layer count. Cube == 6 + int layers_or_depth{1}; + + // Optional backend-owned sampler handle to pair with this texture. + void* sampler_handle{}; }; template @@ -245,6 +310,64 @@ struct gpu_texture_output halp::gpu_texture texture{}; }; +template +struct gpu_cubemap_input : gpu_texture_input +{ + halp_meta(texture_target, halp::texture_kind::cubemap) +}; + +template +struct gpu_sampleable_depth_input : gpu_texture_input +{ + halp_meta(samplable_depth, true) +}; + +template +struct gpu_cubemap_output : gpu_texture_output +{ + halp_meta(texture_target, halp::texture_kind::cubemap) + + gpu_cubemap_output() noexcept + { + this->texture.kind = halp::texture_kind::cubemap; + this->texture.layers_or_depth = 6; + } +}; + +template +struct gpu_texture_array_input : gpu_texture_input +{ + halp_meta(texture_target, halp::texture_kind::texture_array) +}; + +template +struct gpu_texture_array_output : gpu_texture_output +{ + halp_meta(texture_target, halp::texture_kind::texture_array) + + gpu_texture_array_output() noexcept + { + this->texture.kind = halp::texture_kind::texture_array; + } +}; + +template +struct gpu_texture_3d_input : gpu_texture_input +{ + halp_meta(texture_target, halp::texture_kind::texture_3d) +}; + +template +struct gpu_texture_3d_output : gpu_texture_output +{ + halp_meta(texture_target, halp::texture_kind::texture_3d) + + gpu_texture_3d_output() noexcept + { + this->texture.kind = halp::texture_kind::texture_3d; + } +}; + template struct gpu_render_target_output {