source: trunk/SL/ITEM_SHADER/item_shader.h

Last change on this file was 17396, checked in by westram, 6 years ago
File size: 12.0 KB
Line 
1// ============================================================ //
2//                                                              //
3//   File      : item_shader.h                                  //
4//   Purpose   : external interface of ITEM_SHADER              //
5//                                                              //
6//   Coded by Ralf Westram (coder@reallysoft.de) in June 2016   //
7//   http://www.arb-home.de/                                    //
8//                                                              //
9// ============================================================ //
10
11#ifndef ITEM_SHADER_H
12#define ITEM_SHADER_H
13
14#ifndef SMARTPTR_H
15#include <smartptr.h>
16#endif
17#ifndef ARB_MSG_H
18#include <arb_msg.h>
19#endif
20#ifndef AW_BASE_HXX
21#include <aw_base.hxx>
22#endif
23#ifndef ITEMS_H
24#include <items.h>
25#endif
26#ifndef AW_COLOR_GROUPS_HXX
27#include <aw_color_groups.hxx>
28#endif
29#ifndef _GLIBCXX_STRING
30#include <string>
31#endif
32
33#define is_assert(cond) arb_assert(cond)
34
35class GBDATA;
36class AW_gc_manager;
37
38// ----------------
39//      Phaser
40
41class Phaser {
42    float frequency; // 1.0 => [0.0 .. 1.0] gets mapped to whole color range;
43                     // 2.0 => [0.0 .. 0.5] and [0.5 .. 1.0] each gets mapped to whole color range
44
45    float preshift;  // allowed values [0.0 .. 1.0]; applied BEFORE frequency mapping
46    float postshift; // allowed values [0.0 .. 1.0]; applied AFTER  frequency mapping
47
48    bool alternate;  // if frequency>1.0 => use alternate mapping direction
49
50    float result_lower_bound; // = result of rephase(0.0)
51    float result_upper_bound; // = result of rephase(1.0)
52
53    static CONSTEXPR_INLINE bool is_normalized(const float& f) {
54        return f>=0.0 && f<=1.0;
55    }
56    static CONSTEXPR_INLINE float shift_and_wrap(const float& val, const float& shift, float wrapto) {
57        return shift>val ? val-shift+wrapto : val-shift;
58    }
59
60    float rephase_inbound(float f) const {
61        is_assert(f>=0.0 && f<=1.0);
62
63        const float preshifted(shift_and_wrap(f, preshift, 1.0));
64        const float blow(preshifted*frequency);
65
66        int   phase(blow);
67        float rest(blow-phase);
68        if (rest <= 0.0 && phase>0) {
69            rest  = 1.0;
70            phase--;
71        }
72
73        rest += postshift;
74        if (rest>1.0) {
75            rest -= 1.0;
76            phase++;
77        }
78        is_assert(is_normalized(rest));
79
80        const bool  do_alter(alternate && (phase&1));
81        const float altered(do_alter ? 1.0-rest : rest);
82
83#if defined(DEBUG) && 0
84        fprintf(stderr,
85                "f=%f preshifted=%f blow=%f postshifted=%f phase=%i rest=%f do_alter=%i altered=%f\n",
86                f, preshifted, blow, postshifted, phase, rest, do_alter, altered);
87#endif
88
89        is_assert(is_normalized(altered));
90        return altered;
91    }
92
93    void calc_bound_results() {
94        result_lower_bound = rephase_inbound(0.0);
95        result_upper_bound = rephase_inbound(1.0);
96    }
97
98public:
99    Phaser() :  // this Phaser does "nothing"
100        frequency(1.0),
101        preshift(0.0),
102        postshift(0.0),
103        alternate(false),
104        result_lower_bound(0.0),
105        result_upper_bound(1.0)
106    {}
107
108    Phaser(float frequency_, bool alternate_, float preshift_, float postshift_) :
109        frequency(frequency_),
110        preshift(preshift_),
111        postshift(postshift_),
112        alternate(alternate_)
113    {
114        is_assert(is_normalized(preshift));
115        is_assert(is_normalized(postshift));
116
117        calc_bound_results();
118    }
119
120    float rephase(float f) const {
121        return
122            f<=0.0
123            ? result_lower_bound
124            : (f>=1.0
125               ? result_upper_bound
126               : rephase_inbound(f));
127    }
128};
129
130// --------------------
131//      ValueTuple
132
133class ValueTuple {
134    /*! Contains a value-tuple (used for shading items).
135     *
136     * Properties:
137     * - variable tuple-size (0-3)
138     * - values may be defined or not
139     * - values are normalized to [0.0 .. 1.0]
140     * - values can be mixed (using weights)
141     */
142
143    ValueTuple *undefined_reverse_mix() const { arb_assert(0); return NULp; }
144
145public:
146    typedef SmartPtr<ValueTuple> ShadedValue;
147
148    virtual ~ValueTuple() {}
149
150    virtual bool is_defined() const   = 0;
151    virtual ShadedValue clone() const = 0;
152    virtual int range_offset(const Phaser&) const  = 0; // returns int-offset into range [0 .. AW_RANGE_COLORS[
153
154#if defined(UNIT_TESTS)
155    virtual const char *inspect() const = 0;
156#endif
157
158    // ValueTuple factory:
159    static ShadedValue undefined();
160    static ShadedValue make(float f);
161    static ShadedValue make(float f1, float f2);
162    static ShadedValue make(float f1, float f2, float f3);
163
164    // mix interface (main function + reverse visitors):
165    virtual ShadedValue mix(float my_ratio, const ValueTuple& other) const = 0;
166    virtual ShadedValue reverse_mix(float /*other_ratio*/, const class NoTuple& /*other*/)      const { return undefined_reverse_mix(); }
167    virtual ShadedValue reverse_mix(float /*other_ratio*/, const class LinearTuple& /*other*/)  const { return undefined_reverse_mix(); }
168    virtual ShadedValue reverse_mix(float /*other_ratio*/, const class PlanarTuple& /*other*/)  const { return undefined_reverse_mix(); }
169    virtual ShadedValue reverse_mix(float /*other_ratio*/, const class SpatialTuple& /*other*/) const { return undefined_reverse_mix(); }
170};
171
172typedef ValueTuple::ShadedValue ShadedValue;
173
174inline ShadedValue mix(const ShadedValue& val1, float val1_ratio, const ShadedValue& val2) {
175    return val1->mix(val1_ratio, *val2);
176}
177
178// --------------------------
179//      ShaderPlugin
180
181class ItemShader;
182
183enum ReshadeMode {
184    SIMPLE_RESHADE         = 0,
185    CHECK_DIMENSION_CHANGE = 1,
186};
187
188class ShaderPlugin {
189    RefPtr<ItemShader> plugged_into;
190
191    std::string id;
192    std::string description;
193    std::string awar_prefix; // empty means "awars not initialized yet"
194
195    virtual void init_specific_awars(AW_root *awr) = 0;
196
197protected:
198    const ItemShader *shader_plugged_into() const { return plugged_into; }
199
200public:
201    ShaderPlugin(const std::string& id_, const std::string& description_) :
202        plugged_into(NULp),
203        id(id_),
204        description(description_)
205    {
206        /*! construct a shader plugin
207         * @param id_           a unique id
208         * @param description_  description of plugin (ia. used as title of config window)
209         */
210    }
211    virtual ~ShaderPlugin() {}
212
213    void announce_shader(ItemShader *shader) { plugged_into = shader; }
214
215    const std::string& get_id() const { return id; }
216    const std::string& get_description() const { return description; }
217
218    inline const char *get_shader_local_id() const;
219
220    void init_awars(AW_root *awr, const char *awar_prefix_);
221    const char *plugin_awar(const char *name) const {
222        is_assert(!awar_prefix.empty()); // forgot to call init_awars?
223        return GBS_global_string("%s/%s", awar_prefix.c_str(), name);
224    }
225    const char *dimension_awar(int dim, const char *name) const {
226        is_assert(!awar_prefix.empty()); // forgot to call init_awars?
227        is_assert(dim>=0 && dim<3); // invalid dimension specified
228        return GBS_global_string("%s/%s_%i", awar_prefix.c_str(), name, dim);
229    }
230
231    bool overlay_marked() const;       // true if shader-plugin currently likes to display marked species in marked color
232    bool overlay_color_groups() const; // true if shader-plugin currently likes to display color groups
233
234    virtual ShadedValue shade(GBDATA *gb_item) const = 0;
235
236    virtual int get_dimension() const = 0; // returns (current) dimension of shader-plugin
237
238    virtual bool customizable() const    = 0;
239    virtual void customize(AW_root *awr) = 0;
240
241    virtual char *store_config() const  = 0;
242    virtual void load_or_reset_config(const char *cfgstr) = 0;
243
244    virtual void activate(bool on) = 0; // called with true when plugin gets activated, with false when it gets deactivated
245
246    void trigger_reshade_if_active_cb(ReshadeMode mode);
247};
248
249typedef SmartPtr<ShaderPlugin> ShaderPluginPtr;
250
251// --------------------
252//      ItemShader
253
254#define NO_PLUGIN_SELECTED ""
255
256typedef void (*ReshadeCallback)();
257
258class ItemShader {
259    std::string     id;
260    std::string     description;
261    ReshadeCallback reshade_cb;
262
263    int undefined_gc;
264
265    mutable int  reshade_delay_level;
266    mutable bool reshade_was_suppressed;
267
268    void delay_reshade_callbacks(bool suppress) const {
269        reshade_delay_level += suppress ? 1 : -1;
270        is_assert(reshade_delay_level>=0);
271
272        if (!reshade_delay_level) { // switched off delay
273            if (reshade_was_suppressed) {
274                reshade_cb();
275                reshade_was_suppressed = false;
276            }
277        }
278
279#if defined(ASSERTION_USED)
280        bool start_of_delay = reshade_delay_level == 1 && suppress;
281        is_assert(implicated(start_of_delay, !reshade_was_suppressed));
282#endif
283    }
284    friend class DelayReshade;
285
286protected:
287    ShaderPluginPtr active_plugin;  // null means: no plugin active
288    int             first_range_gc; // has to be set by init()!
289    Phaser          phaser;
290
291public:
292    ItemShader(const std::string& id_, const std::string& description_, ReshadeCallback rcb, int undefined_gc_) :
293        id(id_),
294        description(description_),
295        reshade_cb(rcb),
296        undefined_gc(undefined_gc_),
297        reshade_delay_level(0),
298        reshade_was_suppressed(false),
299        first_range_gc(-1)
300    {}
301    virtual ~ItemShader() {}
302
303    virtual void register_plugin(ShaderPluginPtr plugin) = 0;
304    virtual bool activate_plugin(const std::string& id)  = 0; // returns 'true' on success
305    bool deactivate_plugin() { return activate_plugin(NO_PLUGIN_SELECTED); }
306    virtual std::string active_plugin_name() const = 0;
307
308    bool is_active_plugin(const ShaderPlugin& plugin) const {
309        return active_plugin_name() == plugin.get_id();
310    }
311
312    virtual void init() = 0; // call once after register_plugin was called (activates plugin stored in AWAR)
313    virtual void popup_config_window(AW_root *awr) = 0;
314
315    virtual void check_dimension_change() = 0;
316
317    const std::string& get_id() const { return id; }
318    const std::string& get_description() const { return description; }
319
320    bool active() const { return active_plugin.isSet(); }
321    bool overlay_marked()       const { return !active() || active_plugin->overlay_marked(); }       // if true, caller should use marked-GC
322    bool overlay_color_groups() const { return active() ? active_plugin->overlay_color_groups() : AW_color_groups_active(); } // if true, caller should use color-groups-GCs
323
324    ShadedValue shade(GBDATA *gb_item) const {
325        is_assert(active()); // don't call if no shader is active
326        return active() ? active_plugin->shade(gb_item) : ValueTuple::undefined();
327    }
328    int to_GC(const ShadedValue& val) const {
329        is_assert(first_range_gc>0);
330        if (val->is_defined()) {
331            return first_range_gc + val->range_offset(phaser);
332        }
333        return undefined_gc;
334    }
335
336    void trigger_reshade_callback(ReshadeMode mode) {
337        if (mode == CHECK_DIMENSION_CHANGE) check_dimension_change();
338
339        if (reshade_delay_level) reshade_was_suppressed = true;
340        else                     reshade_cb();
341    }
342};
343
344class DelayReshade : virtual Noncopyable {
345    const ItemShader *shader;
346public:
347    DelayReshade(const ItemShader *shader_) :
348        shader(shader_)
349    {
350        shader->delay_reshade_callbacks(true);
351    }
352    ~DelayReshade() {
353        shader->delay_reshade_callbacks(false);
354    }
355};
356
357inline const char *ShaderPlugin::get_shader_local_id() const {
358    is_assert(plugged_into);
359    return GBS_global_string("%s_%s", plugged_into->get_id().c_str(), get_id().c_str());
360}
361inline void ShaderPlugin::trigger_reshade_if_active_cb(ReshadeMode mode) {
362    if (plugged_into && plugged_into->is_active_plugin(*this)) {
363        plugged_into->trigger_reshade_callback(mode);
364    }
365}
366
367// -----------------------------
368//      ItemShader registry
369
370ItemShader *registerItemShader(AW_root *awr, AW_gc_manager *gcman, BoundItemSel& itemtype, const char *unique_id, const char *description, const char *help_id, ReshadeCallback reshade, int undef_gc);
371
372#else
373#error item_shader.h included twice
374#endif // ITEM_SHADER_H
Note: See TracBrowser for help on using the repository browser.