diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 7bcb2e8..c60e37b 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -26,7 +26,7 @@ FetchContent_MakeAvailable(glfw) FetchContent_Declare( imgui GIT_REPOSITORY "https://github.com/ocornut/imgui" - GIT_TAG "v1.92.4" + GIT_TAG "v1.92.7" GIT_PROGRESS TRUE ) FetchContent_MakeAvailable(imgui) diff --git a/implot3d.cpp b/implot3d.cpp index 97edc9a..754267c 100644 --- a/implot3d.cpp +++ b/implot3d.cpp @@ -48,6 +48,16 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot3d files. You can read releases logs https://github.com/brenocq/implot3d/releases for more details. +- 2026/04/04 (0.4) - PlotMesh signature changed: the ImPlot3DPoint* overload is deprecated and will be removed in v1.0. + A new overload accepting separate coordinate arrays (vtx_xs, vtx_ys, vtx_zs) was added, matching + the pattern of PlotLine/PlotScatter/PlotTriangle and supporting Spec.Offset and Spec.Stride. + ```cpp + // Before + ImPlot3D::PlotMesh("Mesh", vtx, idxs, vtx_count, idx_count); + + // After + ImPlot3D::PlotMesh("Mesh", vtx_xs, vtx_ys, vtx_zs, idxs, vtx_count, idx_count); + ``` - 2026/02/03 (0.4) - ImPlotSpec was made the default and _only_ way of styling plot items. The SetNextXXXStyle functions have been removed. - SetNextLineStyle has been removed, styling should be set via ImPlot3DSpec. ```cpp @@ -631,7 +641,7 @@ float ComputeMaxTickLabelExtent(const ImPlot3DAxis& axis); // Spacing constants for axis tick labels and axis labels static const float AXIS_TICK_INNER_PAD = 5.0f; // gap between axis edge and inner edge of tick labels -static const float AXIS_LABEL_PAD = 10.0f; // gap between tick label outer edge and axis label center +static const float AXIS_LABEL_PAD = 10.0f; // gap between tick label outer edge and axis label center static const float AXIS_RECT_MIN_WIDTH = 40.0f; // minimum hover rect width when labels are absent // Computes the total outward width of the hover rect for an axis @@ -2898,7 +2908,7 @@ struct ImPlot3DStyleVarInfo { static const ImPlot3DStyleVarInfo GPlot3DStyleVarInfo[] = { // Item style {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, LineWeight)}, // ImPlot3DStyleVar_LineWeight - {ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlot3DStyle, Marker)}, // ImPlot3DStyleVar_Marker + {ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlot3DStyle, Marker)}, // ImPlot3DStyleVar_Marker {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, MarkerSize)}, // ImPlot3DStyleVar_MarkerSize {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, FillAlpha)}, // ImPlot3DStyleVar_FillAlpha diff --git a/implot3d.h b/implot3d.h index 128ccbe..fd08225 100644 --- a/implot3d.h +++ b/implot3d.h @@ -103,17 +103,22 @@ typedef ImTextureID ImTextureRef; // Plotting properties. These provide syntactic sugar for creating ImPlot3DSpec from (ImPlot3DProp,value) pairs enum ImPlot3DProp_ { - ImPlot3DProp_LineColor, // Line color; IMPLOT3D_AUTO_COL will use next Colormap color - ImPlot3DProp_LineWeight, // Line weight in pixels - ImPlot3DProp_FillColor, // Fill color (applies to shaded regions); IMPLOT3D_AUTO_COL will use next Colormap color - ImPlot3DProp_FillAlpha, // Alpha multiplier (applies to FillColor and MarkerFillColor) - ImPlot3DProp_Marker, // Marker type - ImPlot3DProp_MarkerSize, // Size of markers (radius) *in pixels* - ImPlot3DProp_MarkerLineColor, // Marker outline color; IMPLOT3D_AUTO_COL will use next LineColor - ImPlot3DProp_MarkerFillColor, // Marker fill color; IMPLOT3D_AUTO_COL will use LineColor - ImPlot3DProp_Offset, // Data index offset - ImPlot3DProp_Stride, // Data stride in bytes; IMPLOT3D_AUTO will result in sizeof(T) where T is the type passed to PlotX - ImPlot3DProp_Flags // Optional item flags; can be composed from common ImPlot3DItemFlags and/or specialized ImPlot3DXFlags + ImPlot3DProp_LineColor, // Line color; IMPLOT3D_AUTO_COL will use next Colormap color + ImPlot3DProp_LineColors, // Array of line colors (ImU32*); if nullptr, use LineColor for all + ImPlot3DProp_LineWeight, // Line weight in pixels + ImPlot3DProp_FillColor, // Fill color (applies to shaded regions); IMPLOT3D_AUTO_COL will use next Colormap color + ImPlot3DProp_FillColors, // Array of fill colors (ImU32*); if nullptr, use FillColor for all + ImPlot3DProp_FillAlpha, // Alpha multiplier (applies to FillColor, FillColors, MarkerFillColor, and MarkerFillColors) + ImPlot3DProp_Marker, // Marker type + ImPlot3DProp_MarkerSize, // Size of markers (radius) *in pixels* + ImPlot3DProp_MarkerSizes, // Array of marker sizes (float*); if nullptr, use MarkerSize for all + ImPlot3DProp_MarkerLineColor, // Marker outline color; IMPLOT3D_AUTO_COL will use next LineColor + ImPlot3DProp_MarkerLineColors, // Array of marker outline colors (ImU32*); if nullptr, use MarkerLineColor for all + ImPlot3DProp_MarkerFillColor, // Marker fill color; IMPLOT3D_AUTO_COL will use LineColor + ImPlot3DProp_MarkerFillColors, // Array of marker fill colors (ImU32*); if nullptr, use MarkerFillColor for all + ImPlot3DProp_Offset, // Data index offset + ImPlot3DProp_Stride, // Data stride in bytes; IMPLOT3D_AUTO will result in sizeof(T) where T is the type passed to PlotX + ImPlot3DProp_Flags // Optional item flags; can be composed from common ImPlot3DItemFlags and/or specialized ImPlot3DXFlags }; // Flags for ImPlot3D::BeginPlot() @@ -378,13 +383,18 @@ enum ImPlot3DColormap_ { // }); struct ImPlot3DSpec { ImVec4 LineColor = IMPLOT3D_AUTO_COL; // Line color; IMPLOT3D_AUTO_COL will use next Colormap color + ImU32* LineColors = nullptr; // Per-index line colors; if nullptr, use LineColor for all float LineWeight = 1.0f; // Line weight in pixels ImVec4 FillColor = IMPLOT3D_AUTO_COL; // Fill color (applies to shaded regions); IMPLOT3D_AUTO_COL will use next Colormap color - float FillAlpha = IMPLOT3D_AUTO; // Alpha multiplier (applies to FillColor and MarkerFillColor) + ImU32* FillColors = nullptr; // Per-index fill colors; if nullptr, use FillColor for all + float FillAlpha = IMPLOT3D_AUTO; // Alpha multiplier (applies to FillColor, FillColors, MarkerFillColor, and MarkerFillColors) ImPlot3DMarker Marker = ImPlot3DMarker_Auto; // Marker type float MarkerSize = IMPLOT3D_AUTO; // Size of markers (radius) *in pixels* + float* MarkerSizes = nullptr; // Per-index marker sizes; if nullptr, use MarkerSize for all ImVec4 MarkerLineColor = IMPLOT3D_AUTO_COL; // Marker outline color; IMPLOT3D_AUTO_COL will use LineColor + ImU32* MarkerLineColors = nullptr; // Per-index marker outline colors; if nullptr, use MarkerLineColor for all ImVec4 MarkerFillColor = IMPLOT3D_AUTO_COL; // Marker fill color; IMPLOT3D_AUTO_COL will use LineColor + ImU32* MarkerFillColors = nullptr; // Per-index marker fill colors; if nullptr, use MarkerFillColor for all int Offset = 0; // Data index offset int Stride = IMPLOT3D_AUTO; // Data stride in bytes; IMPLOT3D_AUTO will result in sizeof(T) where T is the type passed to PlotX ImPlot3DItemFlags Flags = @@ -426,6 +436,27 @@ struct ImPlot3DSpec { IM_ASSERT(0 && "User provided an ImPlot3DProp which cannot be set from scalar value!"); } + // Set a property from an ImU32* array (per-index colors). + void SetProp(ImPlot3DProp prop, ImU32* v) { + switch (prop) { + case ImPlot3DProp_LineColors: LineColors = v; return; + case ImPlot3DProp_FillColors: FillColors = v; return; + case ImPlot3DProp_MarkerLineColors: MarkerLineColors = v; return; + case ImPlot3DProp_MarkerFillColors: MarkerFillColors = v; return; + default: break; + } + IM_ASSERT(0 && "User provided an ImPlot3DProp which cannot be set from ImU32* value!"); + } + + // Set a property from a float* array (per-index sizes). + void SetProp(ImPlot3DProp prop, float* v) { + switch (prop) { + case ImPlot3DProp_MarkerSizes: MarkerSizes = v; return; + default: break; + } + IM_ASSERT(0 && "User provided an ImPlot3DProp which cannot be set from float* value!"); + } + // Set a property from an ImVec4 value. void SetProp(ImPlot3DProp prop, const ImVec4& v) { switch (prop) { @@ -620,9 +651,18 @@ IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min = 0.0, double scale_max = 0.0, const ImPlot3DSpec& spec = ImPlot3DSpec()); -// Plots a 3D mesh given vertex positions and indices. Triangles are defined by the index buffer (every 3 indices form a triangle) -IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, - const ImPlot3DSpec& spec = ImPlot3DSpec()); +// Plots a 3D mesh given vertex positions as separate coordinate arrays and an index buffer. +// Triangles are defined by the index buffer (every 3 indices form a triangle). +// Spec.Offset and Spec.Stride apply to the vertex coordinate arrays only, not to the index buffer. +// Color array semantics: +// - FillColors / LineColors: idx_count entries, indexed by position in the index buffer. +// Each triangle has 3 consecutive color entries (one per corner). +// Setting all 3 to the same value gives flat per-triangle shading. +// Setting different values enables Gouraud shading (GPU-interpolated). +// To shade by vertex, map: FillColors[i] = vtx_colors[idxs[i]]. +// - MarkerFillColors / MarkerLineColors: vtx_count entries, one per unique vertex. +IMPLOT3D_TMP void PlotMesh(const char* label_id, const T* vtx_xs, const T* vtx_ys, const T* vtx_zs, const unsigned int* idxs, int vtx_count, + int idx_count, const ImPlot3DSpec& spec = ImPlot3DSpec()); // Plots a rectangular image in 3D defined by its center and two direction vectors (axes). // #center is the center of the rectangle in plot coordinates. @@ -1011,8 +1051,7 @@ struct ImPlot3DStyle { // Constructor IMPLOT3D_API ImPlot3DStyle(); ImPlot3DStyle(const ImPlot3DStyle& other) = default; - ImPlot3DStyle& operator=(const ImPlot3DStyle& other) = - default; + ImPlot3DStyle& operator=(const ImPlot3DStyle& other) = default; }; //----------------------------------------------------------------------------- @@ -1069,16 +1108,26 @@ extern unsigned int duck_idx[DUCK_IDX_COUNT]; // Duck indices namespace ImPlot3D { // OBSOLETED in v0.4 (from February 2026) -// IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO); // OBSOLETED IN v0.4 // Set ImPlotSpec.LineColor/LineWeight or construct ImPlotSpec with { ImPlotSpec_LineColor, color, ImPlotSpec_LineWeight, weight }. +// IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO); // OBSOLETED IN v0.4 // Set +// ImPlotSpec.LineColor/LineWeight or construct ImPlotSpec with { ImPlotSpec_LineColor, color, ImPlotSpec_LineWeight, weight }. -// IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO);// OBSOLETED IN v0.4 // Set ImPlotSpec.FillColor/FillAlpha or construct ImPlotSpec with { ImPlotSpec_FillColor, color, ImPlotSpec_FillAlpha, alpha }. +// IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO);// OBSOLETED IN v0.4 // Set +// ImPlotSpec.FillColor/FillAlpha or construct ImPlotSpec with { ImPlotSpec_FillColor, color, ImPlotSpec_FillAlpha, alpha }. -// IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); // OBSOLETED IN v0.4 // Set ImPlotSpec.Marker/MarkerSize/MarkerFillColor/LineWeight/MarkerLineColor or construct ImPlotSpec with { ImPlotSpec_Marker, marker, ImPlotSpec_MarkerSize, size, ImPlotSpec_MarkerFillColor, fill_color, ImPlotSpec_LineWeight, weight, ImPlotSpec_MarkerLineColor, outline }. +// IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight +// = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); // OBSOLETED IN v0.4 // Set +// ImPlotSpec.Marker/MarkerSize/MarkerFillColor/LineWeight/MarkerLineColor or construct ImPlotSpec with { ImPlotSpec_Marker, marker, +// ImPlotSpec_MarkerSize, size, ImPlotSpec_MarkerFillColor, fill_color, ImPlotSpec_LineWeight, weight, ImPlotSpec_MarkerLineColor, outline }. // OBSOLETED in v0.3 -> PLANNED REMOVAL in v1.0 IMPLOT3D_DEPRECATED(IMPLOT3D_API ImVec2 GetPlotPos()); // Renamed to GetPlotRectPos() IMPLOT3D_DEPRECATED(IMPLOT3D_API ImVec2 GetPlotSize()); // Renamed to GetPlotRectSize() +// OBSOLETED in v0.4 -> PLANNED REMOVAL in v1.0 +// Use PlotMesh(label_id, vtx_xs, vtx_ys, vtx_zs, idx, vtx_count, idx_count, spec) instead. +IMPLOT3D_DEPRECATED(IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idxs, int vtx_count, int idx_count, + const ImPlot3DSpec& spec = ImPlot3DSpec())); + } // namespace ImPlot3D #endif // #ifndef IMPLOT3D_DISABLE_OBSOLETE_FUNCTIONS diff --git a/implot3d_demo.cpp b/implot3d_demo.cpp index 555ad92..ce7e051 100644 --- a/implot3d_demo.cpp +++ b/implot3d_demo.cpp @@ -31,8 +31,13 @@ // Helper to wire demo markers located in code to an interactive browser (e.g. imgui_explorer) #if IMGUI_VERSION_NUM >= 19263 -namespace ImGui { extern IMGUI_API void DemoMarker(const char* file, int line, const char* section); }; -#define IMGUI_DEMO_MARKER(section) do { ImGui::DemoMarker("implot3d_demo.cpp", __LINE__, section); } while (0) +namespace ImGui { +extern IMGUI_API void DemoMarker(const char* file, int line, const char* section); +}; +#define IMGUI_DEMO_MARKER(section) \ + do { \ + ImGui::DemoMarker("implot3d_demo.cpp", __LINE__, section); \ + } while (0) #else #define IMGUI_DEMO_MARKER(section) #endif @@ -335,6 +340,7 @@ void DemoSurfacePlots() { IMGUI_DEMO_MARKER("Plots/Surface Plots"); constexpr int N = 20; static float xs[N * N], ys[N * N], zs[N * N]; + static ImU32 custom_colors[N * N]; static float t = 0.0f; t += ImGui::GetIO().DeltaTime; @@ -350,6 +356,9 @@ void DemoSurfacePlots() { xs[idx] = min_val + j * step; // X values are constant along rows ys[idx] = min_val + i * step; // Y values are constant along columns zs[idx] = ImSin(2 * t + ImSqrt((xs[idx] * xs[idx] + ys[idx] * ys[idx]))); // z = sin(2t + sqrt(x^2 + y^2)) + // Custom per-point color: R=x, G=y, B=z (each remapped from [-1,1] to [0,1]) + custom_colors[idx] = + IM_COL32((ImU8)((xs[idx] + 1) * 0.5f * 255), (ImU8)((ys[idx] + 1) * 0.5f * 255), (ImU8)((zs[idx] + 1) * 0.5f * 255), 255); } } @@ -375,13 +384,21 @@ void DemoSurfacePlots() { ImGui::SameLine(); ImGui::Combo("##SurfaceColormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps)); } + + // Custom per-point colors + ImGui::RadioButton("Custom Per-Point", &selected_fill, 2); + if (selected_fill == 2) + ImGui::SameLine(), ImGui::TextDisabled("R=x, G=y, B=z"); + ImGui::Unindent(); } - // Choose range + // Choose range (only applies to Colormap mode) static bool custom_range = false; static float range_min = -1.0f; static float range_max = 1.0f; + if (selected_fill != 1) + ImGui::BeginDisabled(); ImGui::Checkbox("Custom range", &custom_range); { ImGui::Indent(); @@ -395,6 +412,8 @@ void DemoSurfacePlots() { ImGui::Unindent(); } + if (selected_fill != 1) + ImGui::EndDisabled(); // Select flags static ImPlot3DSurfaceFlags flags = ImPlot3DSurfaceFlags_NoMarkers; @@ -415,6 +434,8 @@ void DemoSurfacePlots() { spec.LineColor = ImPlot3D::GetColormapColor(1); if (selected_fill == 0) spec.FillColor = solid_color; + else if (selected_fill == 2) + spec.FillColors = custom_colors; // Plot the surface if (custom_range) @@ -457,6 +478,7 @@ void DemoMeshPlots() { ImPlot3DSpec spec; spec.Flags = flags; + spec.Stride = (int)sizeof(ImPlot3DPoint); // Set fill style spec.FillColor = fill_color; // Set line style @@ -469,11 +491,11 @@ void DemoMeshPlots() { // Plot mesh if (mesh_id == 0) - ImPlot3D::PlotMesh("Duck", duck_vtx, duck_idx, DUCK_VTX_COUNT, DUCK_IDX_COUNT, spec); + ImPlot3D::PlotMesh("Duck", &duck_vtx[0].x, &duck_vtx[0].y, &duck_vtx[0].z, duck_idx, DUCK_VTX_COUNT, DUCK_IDX_COUNT, spec); else if (mesh_id == 1) - ImPlot3D::PlotMesh("Sphere", sphere_vtx, sphere_idx, SPHERE_VTX_COUNT, SPHERE_IDX_COUNT, spec); + ImPlot3D::PlotMesh("Sphere", &sphere_vtx[0].x, &sphere_vtx[0].y, &sphere_vtx[0].z, sphere_idx, SPHERE_VTX_COUNT, SPHERE_IDX_COUNT, spec); else if (mesh_id == 2) - ImPlot3D::PlotMesh("Cube", cube_vtx, cube_idx, CUBE_VTX_COUNT, CUBE_IDX_COUNT, spec); + ImPlot3D::PlotMesh("Cube", &cube_vtx[0].x, &cube_vtx[0].y, &cube_vtx[0].z, cube_idx, CUBE_VTX_COUNT, CUBE_IDX_COUNT, spec); ImPlot3D::EndPlot(); } @@ -902,6 +924,215 @@ void DemoNaNValues() { } } +void Demo_PerIndexColors() { + IMGUI_DEMO_MARKER("Plots/Per-Index Colors"); + + // Colorful Lines + static float xs1[1001], ys1[1001], zs1[1001]; + static ImU32 colors1[1001]; + for (int i = 0; i < 1001; ++i) { + xs1[i] = i * 0.001f; + ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); + zs1[i] = 0.5f + 0.5f * cosf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); + // Rainbow colors + float hue = (float)i / 1000.0f; + colors1[i] = ImColor::HSV(hue, 0.8f, 0.9f); + } + static float xs2[20], ys2[20], zs2[20]; + static ImU32 colors2[20]; + for (int i = 0; i < 20; ++i) { + xs2[i] = i * 1 / 19.0f; + ys2[i] = xs2[i] * xs2[i]; + zs2[i] = xs2[i] * ys2[i]; + // Colormap colors (Viridis) + float t = i / 19.0f; + ImVec4 color = ImPlot3D::SampleColormap(t, ImPlot3DColormap_Viridis); + colors2[i] = ImGui::GetColorU32(color); + } + if (ImPlot3D::BeginPlot("Colorful Lines")) { + ImPlot3D::PlotLine("f(x)", xs1, ys1, zs1, 1001, {ImPlot3DProp_LineColors, colors1}); + ImPlot3D::PlotLine("g(x)", xs2, ys2, zs2, 20, + {ImPlot3DProp_Marker, ImPlot3DMarker_Circle, ImPlot3DProp_Flags, (int)ImPlot3DLineFlags_Segments, ImPlot3DProp_LineColors, + colors2, ImPlot3DProp_MarkerFillColors, colors2, ImPlot3DProp_MarkerLineColors, colors2}); + ImPlot3D::EndPlot(); + } + + // Colorful Scatter + srand(0); + static float xs_scatter1[100], ys_scatter1[100], zs_scatter1[100]; + static ImU32 colors_scatter1_fill[100], colors_scatter1_line[100]; + static float sizes_scatter1[100]; + for (int i = 0; i < 100; ++i) { + xs_scatter1[i] = i * 0.01f; + ys_scatter1[i] = xs_scatter1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); + zs_scatter1[i] = xs_scatter1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); + // Rainbow hue colors + float hue = i / 99.0f; + colors_scatter1_fill[i] = ImColor::HSV(hue, 0.8f, 0.9f); + colors_scatter1_line[i] = ImColor::HSV(hue, 0.9f, 0.7f); + // Random sizes between 2 and 6 + sizes_scatter1[i] = 2.0f + 4.0f * ((float)rand() / (float)RAND_MAX); + } + static float xs_scatter2[50], ys_scatter2[50], zs_scatter2[50]; + static ImU32 colors_scatter2[50]; + static float sizes_scatter2[50]; + for (int i = 0; i < 50; ++i) { + xs_scatter2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); + ys_scatter2[i] = 0.50f + 0.2f * ((float)rand() / (float)RAND_MAX); + zs_scatter2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); + // Colormap colors (Viridis) + float t = i / 49.0f; + ImVec4 color = ImPlot3D::SampleColormap(t, ImPlot3DColormap_Viridis); + colors_scatter2[i] = ImGui::GetColorU32(color); + // Random sizes between 2 and 6 + sizes_scatter2[i] = 2.0f + 4.0f * ((float)rand() / (float)RAND_MAX); + } + + if (ImPlot3D::BeginPlot("Colorful Scatter")) { + ImPlot3D::PlotScatter("Data 1", xs_scatter1, ys_scatter1, zs_scatter1, 100, + {ImPlot3DProp_MarkerFillColors, colors_scatter1_fill, ImPlot3DProp_MarkerLineColors, colors_scatter1_line, + ImPlot3DProp_MarkerSizes, sizes_scatter1}); + ImPlot3D::PlotScatter("Data 2", xs_scatter2, ys_scatter2, zs_scatter2, 50, + {ImPlot3DProp_Marker, ImPlot3DMarker_Square, ImPlot3DProp_MarkerFillColors, colors_scatter2, + ImPlot3DProp_MarkerLineColors, colors_scatter2, ImPlot3DProp_MarkerSizes, sizes_scatter2, ImPlot3DProp_FillAlpha, + 0.5f}); + ImPlot3D::EndPlot(); + } + + // Colorful Triangles (pyramid with per-vertex fill colors) + static float xs_tri[18], ys_tri[18], zs_tri[18]; + static ImU32 colors_tri[18]; + { + // Apex + float ax = 0.0f, ay = 0.0f, az = 1.0f; + // Square base corners + float cx[4] = {-0.5f, 0.5f, 0.5f, -0.5f}; + float cy[4] = {-0.5f, -0.5f, 0.5f, 0.5f}; + float cz[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + int v = 0; + auto AddVertex = [&](float x, float y, float z) { + xs_tri[v] = x; + ys_tri[v] = y; + zs_tri[v] = z; + v++; + }; + AddVertex(ax, ay, az); + AddVertex(cx[0], cy[0], cz[0]); + AddVertex(cx[1], cy[1], cz[1]); + AddVertex(ax, ay, az); + AddVertex(cx[1], cy[1], cz[1]); + AddVertex(cx[2], cy[2], cz[2]); + AddVertex(ax, ay, az); + AddVertex(cx[2], cy[2], cz[2]); + AddVertex(cx[3], cy[3], cz[3]); + AddVertex(ax, ay, az); + AddVertex(cx[3], cy[3], cz[3]); + AddVertex(cx[0], cy[0], cz[0]); + AddVertex(cx[0], cy[0], cz[0]); + AddVertex(cx[1], cy[1], cz[1]); + AddVertex(cx[2], cy[2], cz[2]); + AddVertex(cx[0], cy[0], cz[0]); + AddVertex(cx[2], cy[2], cz[2]); + AddVertex(cx[3], cy[3], cz[3]); + // Hot colormap sampled by z: bottom (z=0) is cold, apex (z=1) is hot + for (int i = 0; i < 18; ++i) { + ImVec4 color = ImPlot3D::SampleColormap(0.5f * zs_tri[i], ImPlot3DColormap_Hot); + colors_tri[i] = ImGui::GetColorU32(color); + } + } + if (ImPlot3D::BeginPlot("Colorful Triangles")) { + ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -0.5, 1.5); + ImPlot3D::PlotTriangle("Pyramid", xs_tri, ys_tri, zs_tri, 18, {ImPlot3DProp_FillColors, colors_tri, ImPlot3DProp_FillAlpha, 0.8f}); + ImPlot3D::EndPlot(); + } + + // Colorful Quads (cube [0,1]^3 with per-vertex RGB colors: R=x, G=y, B=z) + static float xs_quad[24], ys_quad[24], zs_quad[24]; + static ImU32 colors_quad[24]; + { + // clang-format off + xs_quad[0]=1; ys_quad[0]=0; zs_quad[0]=0; xs_quad[1]=1; ys_quad[1]=1; zs_quad[1]=0; xs_quad[2]=1; ys_quad[2]=1; zs_quad[2]=1; xs_quad[3]=1; ys_quad[3]=0; zs_quad[3]=1; + xs_quad[4]=0; ys_quad[4]=0; zs_quad[4]=0; xs_quad[5]=0; ys_quad[5]=1; zs_quad[5]=0; xs_quad[6]=0; ys_quad[6]=1; zs_quad[6]=1; xs_quad[7]=0; ys_quad[7]=0; zs_quad[7]=1; + xs_quad[8]=0; ys_quad[8]=1; zs_quad[8]=0; xs_quad[9]=1; ys_quad[9]=1; zs_quad[9]=0; xs_quad[10]=1; ys_quad[10]=1; zs_quad[10]=1; xs_quad[11]=0; ys_quad[11]=1; zs_quad[11]=1; + xs_quad[12]=0; ys_quad[12]=0; zs_quad[12]=0; xs_quad[13]=1; ys_quad[13]=0; zs_quad[13]=0; xs_quad[14]=1; ys_quad[14]=0; zs_quad[14]=1; xs_quad[15]=0; ys_quad[15]=0; zs_quad[15]=1; + xs_quad[16]=0; ys_quad[16]=0; zs_quad[16]=1; xs_quad[17]=1; ys_quad[17]=0; zs_quad[17]=1; xs_quad[18]=1; ys_quad[18]=1; zs_quad[18]=1; xs_quad[19]=0; ys_quad[19]=1; zs_quad[19]=1; + xs_quad[20]=0; ys_quad[20]=0; zs_quad[20]=0; xs_quad[21]=1; ys_quad[21]=0; zs_quad[21]=0; xs_quad[22]=1; ys_quad[22]=1; zs_quad[22]=0; xs_quad[23]=0; ys_quad[23]=1; zs_quad[23]=0; + // clang-format on + // Color = (R=x, G=y, B=z) per vertex + for (int i = 0; i < 24; ++i) + colors_quad[i] = IM_COL32((ImU8)(xs_quad[i] * 255), (ImU8)(ys_quad[i] * 255), (ImU8)(zs_quad[i] * 255), 255); + } + if (ImPlot3D::BeginPlot("Colorful Quads")) { + ImPlot3D::SetupAxesLimits(-0.5, 1.5, -0.5, 1.5, -0.5, 1.5); + ImPlot3D::PlotQuad("Cube", xs_quad, ys_quad, zs_quad, 24, {ImPlot3DProp_FillColors, colors_quad, ImPlot3DProp_FillAlpha, 0.8f}); + ImPlot3D::EndPlot(); + } + + // Gouraud-shaded Duck (yellow duck lit by a point light with ambient) + // Per-vertex normals are computed once, then mapped to index-buffer slots so the GPU + // interpolates colors across each triangle (Gouraud shading). + static ImU32 duck_fill_colors[DUCK_IDX_COUNT]; + static bool duck_colors_built = false; + if (!duck_colors_built) { + // Accumulate area-weighted face normals into each vertex + struct Vec3 { + float x, y, z; + }; + auto sub3 = [](Vec3 a, Vec3 b) -> Vec3 { return {a.x - b.x, a.y - b.y, a.z - b.z}; }; + auto add3 = [](Vec3 a, Vec3 b) -> Vec3 { return {a.x + b.x, a.y + b.y, a.z + b.z}; }; + auto cross3 = [](Vec3 a, Vec3 b) -> Vec3 { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; }; + auto dot3 = [](Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }; + auto norm3 = [&dot3](Vec3 a) -> Vec3 { + float len = sqrtf(dot3(a, a)); + return len > 1e-8f ? Vec3{a.x / len, a.y / len, a.z / len} : Vec3{0, 0, 1}; + }; + + Vec3 vtx_normals[DUCK_VTX_COUNT] = {}; + for (int i = 0; i < DUCK_IDX_COUNT; i += 3) { + unsigned int i0 = duck_idx[i], i1 = duck_idx[i + 1], i2 = duck_idx[i + 2]; + Vec3 p0 = {(float)duck_vtx[i0].x, (float)duck_vtx[i0].y, (float)duck_vtx[i0].z}; + Vec3 p1 = {(float)duck_vtx[i1].x, (float)duck_vtx[i1].y, (float)duck_vtx[i1].z}; + Vec3 p2 = {(float)duck_vtx[i2].x, (float)duck_vtx[i2].y, (float)duck_vtx[i2].z}; + Vec3 fn = cross3(sub3(p1, p0), sub3(p2, p0)); // area-weighted face normal + vtx_normals[i0] = add3(vtx_normals[i0], fn); + vtx_normals[i1] = add3(vtx_normals[i1], fn); + vtx_normals[i2] = add3(vtx_normals[i2], fn); + } + for (int v = 0; v < DUCK_VTX_COUNT; v++) + vtx_normals[v] = norm3(vtx_normals[v]); + + // Lighting: yellow duck, warm point light, soft ambient + Vec3 light_pos = {2.0f, 2.0f, 3.0f}; + Vec3 duck_col = {1.0f, 0.85f, 0.1f}; // yellow + Vec3 light_col = {1.0f, 0.95f, 0.8f}; // warm white + Vec3 ambient = {0.15f, 0.12f, 0.03f}; // dim warm ambient + + ImU32 vtx_colors[DUCK_VTX_COUNT]; + for (int v = 0; v < DUCK_VTX_COUNT; v++) { + Vec3 pos = {(float)duck_vtx[v].x, (float)duck_vtx[v].y, (float)duck_vtx[v].z}; + Vec3 to_light = norm3(sub3(light_pos, pos)); + float diff = ImMax(0.0f, dot3(vtx_normals[v], to_light)); + float r = ImMin(1.0f, ambient.x + duck_col.x * light_col.x * diff); + float g = ImMin(1.0f, ambient.y + duck_col.y * light_col.y * diff); + float b = ImMin(1.0f, ambient.z + duck_col.z * light_col.z * diff); + vtx_colors[v] = IM_COL32((ImU8)(r * 255), (ImU8)(g * 255), (ImU8)(b * 255), 255); + } + + // Map per-vertex colors to index-buffer slots for Gouraud interpolation + for (int i = 0; i < DUCK_IDX_COUNT; i++) + duck_fill_colors[i] = vtx_colors[duck_idx[i]]; + + duck_colors_built = true; + } + if (ImPlot3D::BeginPlot("Gouraud Duck")) { + ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1); + ImPlot3D::PlotMesh("Duck", &duck_vtx[0].x, &duck_vtx[0].y, &duck_vtx[0].z, duck_idx, DUCK_VTX_COUNT, DUCK_IDX_COUNT, + {ImPlot3DProp_Stride, (int)sizeof(ImPlot3DPoint), ImPlot3DProp_FillColors, duck_fill_colors, ImPlot3DProp_Flags, + (int)ImPlot3DMeshFlags_NoLines}); + ImPlot3D::EndPlot(); + } +} + //----------------------------------------------------------------------------- // [SECTION] Axes //----------------------------------------------------------------------------- @@ -1469,6 +1700,18 @@ void DemoCustomPerPointStyle() { initialized = true; } + // Precompute per-point colors and flat XYZ arrays for each torus + static float xs[3][400], ys[3][400], zs[3][400]; + ImU32 point_colors[3][400]; + for (int torus = 0; torus < 3; torus++) { + for (int i = 0; i < 400; i++) { + xs[torus][i] = torus_data[torus][i][0]; + ys[torus][i] = torus_data[torus][i][1]; + zs[torus][i] = torus_data[torus][i][2]; + point_colors[torus][i] = ImGui::ColorConvertFloat4ToU32(ImPlot3D::SampleColormap(torus_data[torus][i][3], cmap)); + } + } + if (ImPlot3D::BeginPlot("##PerPointStyle", ImVec2(-1, 0))) { ImPlot3D::SetupAxes("X", "Y", "Z"); ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -0.5, 1.5); @@ -1485,21 +1728,14 @@ void DemoCustomPerPointStyle() { spec.MarkerSize = marker_size; for (int torus = 0; torus < 3; torus++) { - const int point_count = 400; - for (int i = 0; i < point_count; i++) { - float x = torus_data[torus][i][0]; - float y = torus_data[torus][i][1]; - float z = torus_data[torus][i][2]; - float t = torus_data[torus][i][3]; - - // Sample colormap and set marker style - ImVec4 color = ImPlot3D::SampleColormap(t, cmap); - spec.FillColor = color; - spec.LineColor = color; - ImPlot3D::PlotScatter(labels[torus], &x, &y, &z, 1, spec); - } + spec.MarkerFillColors = point_colors[torus]; + spec.MarkerLineColors = point_colors[torus]; + ImPlot3D::PlotScatter(labels[torus], xs[torus], ys[torus], zs[torus], 400, spec); // Override legend color with PlotDummy - spec.LineColor = legend_colors[torus]; + spec.MarkerFillColors = nullptr; + spec.MarkerLineColors = nullptr; + spec.MarkerFillColor = legend_colors[torus]; + spec.MarkerLineColor = legend_colors[torus]; ImPlot3D::PlotDummy(labels[torus], spec); } @@ -1645,6 +1881,7 @@ void ShowAllDemos() { DemoHeader("Legend Options", DemoLegendOptions); DemoHeader("Markers and Text", DemoMarkersAndText); DemoHeader("NaN Values", DemoNaNValues); + DemoHeader("Per-Index Colors", Demo_PerIndexColors); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Axes")) { diff --git a/implot3d_items.cpp b/implot3d_items.cpp index 373e909..6721c1c 100644 --- a/implot3d_items.cpp +++ b/implot3d_items.cpp @@ -344,9 +344,10 @@ struct RendererBase { const unsigned int VtxConsumed; // Number of vertices consumed per primitive }; -template struct RendererMarkersFill : RendererBase { - RendererMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col) - : RendererBase(getter.Count, (count - 2) * 3, count), Getter(getter), Marker(marker), Count(count), Size(size), Col(col) {} +template struct RendererMarkersFill : RendererBase { + RendererMarkersFill(const _Getter& getter, const _GetterColor& col_getter, const _GetterSize& size_getter, const ImVec2* marker, int count) + : RendererBase(getter.Count, (count - 2) * 3, count), Getter(getter), ColGetter(col_getter), SizeGetter(size_getter), Marker(marker), + Count(count) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; } @@ -355,12 +356,14 @@ template struct RendererMarkersFill : RendererBase { if (!cull_box.Contains(p_plot)) return false; ImVec2 p = PlotToPixels(p_plot); + ImU32 col = ColGetter(prim); + float size = SizeGetter(prim); // 3 vertices per triangle for (int i = 0; i < Count; i++) { - draw_list_3d._VtxWritePtr[0].pos.x = p.x + Marker[i].x * Size; - draw_list_3d._VtxWritePtr[0].pos.y = p.y + Marker[i].y * Size; + draw_list_3d._VtxWritePtr[0].pos.x = p.x + Marker[i].x * size; + draw_list_3d._VtxWritePtr[0].pos.y = p.y + Marker[i].y * size; draw_list_3d._VtxWritePtr[0].uv = UV; - draw_list_3d._VtxWritePtr[0].col = Col; + draw_list_3d._VtxWritePtr[0].col = col; draw_list_3d._VtxWritePtr++; } // 3 indices per triangle @@ -379,17 +382,18 @@ template struct RendererMarkersFill : RendererBase { return true; } const _Getter& Getter; + const _GetterColor ColGetter; + const _GetterSize SizeGetter; const ImVec2* Marker; const int Count; - const float Size; - const ImU32 Col; mutable ImVec2 UV; }; -template struct RendererMarkersLine : RendererBase { - RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col) - : RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), Getter(getter), Marker(marker), Count(count), - HalfWeight(ImMax(1.0f, weight) * 0.5f), Size(size), Col(col) {} +template struct RendererMarkersLine : RendererBase { + RendererMarkersLine(const _Getter& getter, const _GetterColor& col_getter, const _GetterSize& size_getter, const ImVec2* marker, int count, + float weight) + : RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), Getter(getter), ColGetter(col_getter), SizeGetter(size_getter), Marker(marker), + Count(count), HalfWeight(ImMax(1.0f, weight) * 0.5f) {} void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); } @@ -398,27 +402,29 @@ template struct RendererMarkersLine : RendererBase { if (!cull_box.Contains(p_plot)) return false; ImVec2 p = PlotToPixels(p_plot); + ImU32 col = ColGetter(prim); + float size = SizeGetter(prim); for (int i = 0; i < Count; i = i + 2) { - ImVec2 p1(p.x + Marker[i].x * Size, p.y + Marker[i].y * Size); - ImVec2 p2(p.x + Marker[i + 1].x * Size, p.y + Marker[i + 1].y * Size); - PrimLine(draw_list_3d, p1, p2, HalfWeight, Col, UV0, UV1, GetPointDepth(p_plot)); + ImVec2 p1(p.x + Marker[i].x * size, p.y + Marker[i].y * size); + ImVec2 p2(p.x + Marker[i + 1].x * size, p.y + Marker[i + 1].y * size); + PrimLine(draw_list_3d, p1, p2, HalfWeight, col, UV0, UV1, GetPointDepth(p_plot)); } return true; } const _Getter& Getter; + const _GetterColor ColGetter; + const _GetterSize SizeGetter; const ImVec2* Marker; const int Count; mutable float HalfWeight; - const float Size; - const ImU32 Col; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template struct RendererLineStrip : RendererBase { - RendererLineStrip(const _Getter& getter, ImU32 col, float weight) - : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) { +template struct RendererLineStrip : RendererBase { + RendererLineStrip(const _Getter& getter, const _GetterColor& col_getter, float weight) + : RendererBase(getter.Count - 1, 6, 4), Getter(getter), ColGetter(col_getter), HalfWeight(ImMax(1.0f, weight) * 0.5f) { // Initialize the first point in plot coordinates P1_plot = Getter(0); } @@ -437,7 +443,7 @@ template struct RendererLineStrip : RendererBase { ImVec2 P1_screen = PlotToPixels(P1_clipped); ImVec2 P2_screen = PlotToPixels(P2_clipped); // Render the line segment - PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5)); + PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, ColGetter(prim), UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5)); } // Update for next segment @@ -447,16 +453,16 @@ template struct RendererLineStrip : RendererBase { } const _Getter& Getter; - const ImU32 Col; + const _GetterColor ColGetter; mutable float HalfWeight; mutable ImPlot3DPoint P1_plot; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template struct RendererLineStripSkip : RendererBase { - RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) - : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) { +template struct RendererLineStripSkip : RendererBase { + RendererLineStripSkip(const _Getter& getter, const _GetterColor& col_getter, float weight) + : RendererBase(getter.Count - 1, 6, 4), Getter(getter), ColGetter(col_getter), HalfWeight(ImMax(1.0f, weight) * 0.5f) { // Initialize the first point in plot coordinates P1_plot = Getter(0); } @@ -480,7 +486,7 @@ template struct RendererLineStripSkip : RendererBase { ImVec2 P1_screen = PlotToPixels(P1_clipped); ImVec2 P2_screen = PlotToPixels(P2_clipped); // Render the line segment - PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5)); + PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, ColGetter(prim), UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5)); } } @@ -492,16 +498,16 @@ template struct RendererLineStripSkip : RendererBase { } const _Getter& Getter; - const ImU32 Col; + const _GetterColor ColGetter; mutable float HalfWeight; mutable ImPlot3DPoint P1_plot; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template struct RendererLineSegments : RendererBase { - RendererLineSegments(const _Getter& getter, ImU32 col, float weight) - : RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {} +template struct RendererLineSegments : RendererBase { + RendererLineSegments(const _Getter& getter, const _GetterColor& col_getter, float weight) + : RendererBase(getter.Count / 2, 6, 4), Getter(getter), ColGetter(col_getter), HalfWeight(ImMax(1.0f, weight) * 0.5f) {} void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); } @@ -522,7 +528,7 @@ template struct RendererLineSegments : RendererBase { ImVec2 P1_screen = PlotToPixels(P1_clipped); ImVec2 P2_screen = PlotToPixels(P2_clipped); // Render the line segment - PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5)); + PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, ColGetter(prim), UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5)); } return visible; } @@ -531,14 +537,15 @@ template struct RendererLineSegments : RendererBase { } const _Getter& Getter; - const ImU32 Col; + const _GetterColor ColGetter; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template struct RendererTriangleFill : RendererBase { - RendererTriangleFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 3, 3, 3), Getter(getter), Col(col) {} +template struct RendererTriangleFill : RendererBase { + RendererTriangleFill(const _Getter& getter, const _GetterColor& col_getter) + : RendererBase(getter.Count / 3, 3, 3), Getter(getter), ColGetter(col_getter) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; } @@ -562,15 +569,15 @@ template struct RendererTriangleFill : RendererBase { draw_list_3d._VtxWritePtr[0].pos.x = p[0].x; draw_list_3d._VtxWritePtr[0].pos.y = p[0].y; draw_list_3d._VtxWritePtr[0].uv = UV; - draw_list_3d._VtxWritePtr[0].col = Col; + draw_list_3d._VtxWritePtr[0].col = ColGetter(3 * prim); draw_list_3d._VtxWritePtr[1].pos.x = p[1].x; draw_list_3d._VtxWritePtr[1].pos.y = p[1].y; draw_list_3d._VtxWritePtr[1].uv = UV; - draw_list_3d._VtxWritePtr[1].col = Col; + draw_list_3d._VtxWritePtr[1].col = ColGetter(3 * prim + 1); draw_list_3d._VtxWritePtr[2].pos.x = p[2].x; draw_list_3d._VtxWritePtr[2].pos.y = p[2].y; draw_list_3d._VtxWritePtr[2].uv = UV; - draw_list_3d._VtxWritePtr[2].col = Col; + draw_list_3d._VtxWritePtr[2].col = ColGetter(3 * prim + 2); draw_list_3d._VtxWritePtr += 3; // 3 indices per triangle @@ -589,12 +596,13 @@ template struct RendererTriangleFill : RendererBase { } const _Getter& Getter; + const _GetterColor ColGetter; mutable ImVec2 UV; - const ImU32 Col; }; -template struct RendererQuadFill : RendererBase { - RendererQuadFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 4, 6, 4), Getter(getter), Col(col) {} +template struct RendererQuadFill : RendererBase { + RendererQuadFill(const _Getter& getter, const _GetterColor& col_getter) + : RendererBase(getter.Count / 4, 6, 4), Getter(getter), ColGetter(col_getter) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; } @@ -620,22 +628,22 @@ template struct RendererQuadFill : RendererBase { draw_list_3d._VtxWritePtr[0].pos.x = p[0].x; draw_list_3d._VtxWritePtr[0].pos.y = p[0].y; draw_list_3d._VtxWritePtr[0].uv = UV; - draw_list_3d._VtxWritePtr[0].col = Col; + draw_list_3d._VtxWritePtr[0].col = ColGetter(4 * prim); draw_list_3d._VtxWritePtr[1].pos.x = p[1].x; draw_list_3d._VtxWritePtr[1].pos.y = p[1].y; draw_list_3d._VtxWritePtr[1].uv = UV; - draw_list_3d._VtxWritePtr[1].col = Col; + draw_list_3d._VtxWritePtr[1].col = ColGetter(4 * prim + 1); draw_list_3d._VtxWritePtr[2].pos.x = p[2].x; draw_list_3d._VtxWritePtr[2].pos.y = p[2].y; draw_list_3d._VtxWritePtr[2].uv = UV; - draw_list_3d._VtxWritePtr[2].col = Col; + draw_list_3d._VtxWritePtr[2].col = ColGetter(4 * prim + 2); draw_list_3d._VtxWritePtr[3].pos.x = p[3].x; draw_list_3d._VtxWritePtr[3].pos.y = p[3].y; draw_list_3d._VtxWritePtr[3].uv = UV; - draw_list_3d._VtxWritePtr[3].col = Col; + draw_list_3d._VtxWritePtr[3].col = ColGetter(4 * prim + 3); draw_list_3d._VtxWritePtr += 4; @@ -663,8 +671,8 @@ template struct RendererQuadFill : RendererBase { } const _Getter& Getter; + const _GetterColor ColGetter; mutable ImVec2 UV; - const ImU32 Col; }; template struct RendererQuadImage : RendererBase { @@ -788,7 +796,16 @@ template struct RendererSurfaceFill : RendererBase { // Compute colors ImU32 cols[4] = {Col, Col, Col, Col}; const ImPlot3DNextItemData& n = GetItemData(); - if (n.IsAutoFill) { + const int vtx_idx[4] = {x + y * XCount, x + 1 + y * XCount, x + 1 + (y + 1) * XCount, x + (y + 1) * XCount}; + if (n.Spec.FillColors != nullptr) { + float alpha = n.Spec.FillAlpha; + for (int i = 0; i < 4; i++) { + ImU32 c = n.Spec.FillColors[vtx_idx[i]]; + ImVec4 cv = ImGui::ColorConvertU32ToFloat4(c); + cv.w *= alpha; + cols[i] = ImGui::ColorConvertFloat4ToU32(cv); + } + } else if (n.IsAutoFill) { float alpha = GImPlot3D->NextItemData.Spec.FillAlpha; double min = Min; double max = Max; @@ -983,29 +1000,68 @@ struct Getter3DPoints { const int Count; }; -struct GetterMeshTriangles { - GetterMeshTriangles(const ImPlot3DPoint* vtx, const unsigned int* idx, int idx_count) - : Vtx(vtx), Idx(idx), IdxCount(idx_count), TriCount(idx_count / 3), Count(idx_count) {} +template struct GetterMeshTriangles { + GetterMeshTriangles(TGX gx, TGY gy, TGZ gz, const unsigned int* idx, int idx_count) + : Gx(gx), Gy(gy), Gz(gz), Idx(idx), TriCount(idx_count / 3), Count(idx_count) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I i) const { unsigned int vi = Idx[i]; - return Vtx[vi]; + return ImPlot3DPoint(Gx(vi), Gy(vi), Gz(vi)); } - const ImPlot3DPoint* Vtx; + TGX Gx; + TGY Gy; + TGZ Gz; const unsigned int* Idx; - int IdxCount; int TriCount; int Count; }; +//----------------------------------------------------------------------------- +// [SECTION] Color and Size Getters +//----------------------------------------------------------------------------- + +struct GetterConstColor { + GetterConstColor(ImU32 col) : Col(col) {} + IMPLOT3D_INLINE ImU32 operator()(int) const { return Col; } + ImU32 Col; +}; + +struct GetterIdxColor { + GetterIdxColor(const ImU32* data, int count, float alpha = 1.0f) : Data(data), Count(count), Alpha(alpha) {} + IMPLOT3D_INLINE ImU32 operator()(int idx) const { + ImU32 col = Data[idx]; + if (Alpha < 1.0f) { + ImVec4 c = ImGui::ColorConvertU32ToFloat4(col); + c.w *= Alpha; + col = ImGui::ColorConvertFloat4ToU32(c); + } + return col; + } + const ImU32* Data; + const int Count; + const float Alpha; +}; + +struct GetterConstSize { + GetterConstSize(float size) : Size(size) {} + IMPLOT3D_INLINE float operator()(int) const { return Size; } + float Size; +}; + +struct GetterIdxSize { + GetterIdxSize(const float* data, int count) : Data(data), Count(count) {} + IMPLOT3D_INLINE float operator()(int idx) const { return Data[idx]; } + const float* Data; + const int Count; +}; + //----------------------------------------------------------------------------- // [SECTION] RenderPrimitives //----------------------------------------------------------------------------- -/// Renders primitive shapes -template