source: tags/ms_r16q4/SL/ITEM_SHADER/item_shader.h

Last change on this file was 15082, checked in by westram, 8 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 inline bool CONSTEXPR_RETURN is_normalized(const float& f) {
54        return f>=0.0 && f<=1.0;
55    }
56    static inline float CONSTEXPR_RETURN 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 NULL; }
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;
196public:
197    ShaderPlugin(const std::string& id_, const std::string& description_) :
198        plugged_into(NULL),
199        id(id_),
200        description(description_)
201    {
202        /*! construct a shader plugin
203         * @param id_           a unique id
204         * @param description_  description of plugin (ia. used as title of config window)
205         */
206    }
207    virtual ~ShaderPlugin() {}
208
209    void announce_shader(ItemShader *shader) { plugged_into = shader; }
210
211    const std::string& get_id() const { return id; }
212    const std::string& get_description() const { return description; }
213
214    inline const char *get_shader_local_id() const;
215
216    void init_awars(AW_root *awr, const char *awar_prefix_);
217    const char *plugin_awar(const char *name) const {
218        is_assert(!awar_prefix.empty()); // forgot to call init_awars?
219        return GBS_global_string("%s/%s", awar_prefix.c_str(), name);
220    }
221    const char *dimension_awar(int dim, const char *name) const {
222        is_assert(!awar_prefix.empty()); // forgot to call init_awars?
223        is_assert(dim>=0 && dim<3); // invalid dimension specified
224        return GBS_global_string("%s/%s_%i", awar_prefix.c_str(), name, dim);
225    }
226
227    bool overlay_marked() const;       // true if shader-plugin currently likes to display marked species in marked color
228    bool overlay_color_groups() const; // true if shader-plugin currently likes to display color groups
229
230    virtual ShadedValue shade(GBDATA *gb_item) const = 0;
231
232    virtual int get_dimension() const = 0; // returns (current) dimension of shader-plugin
233
234    virtual bool customizable() const    = 0;
235    virtual void customize(AW_root *awr) = 0;
236
237    virtual char *store_config() const  = 0;
238    virtual void load_or_reset_config(const char *cfgstr) = 0;
239
240    virtual void activate(bool on) = 0; // called with true when plugin gets activated, with false when it gets deactivated
241
242    void trigger_reshade_if_active_cb(ReshadeMode mode);
243};
244
245typedef SmartPtr<ShaderPlugin> ShaderPluginPtr;
246
247// --------------------
248//      ItemShader
249
250#define NO_PLUGIN_SELECTED ""
251
252typedef void (*ReshadeCallback)();
253
254class ItemShader {
255    std::string     id;
256    std::string     description;
257    ReshadeCallback reshade_cb;
258
259    int undefined_gc;
260
261    mutable int  reshade_delay_level;
262    mutable bool reshade_was_suppressed;
263
264    void delay_reshade_callbacks(bool suppress) const {
265        reshade_delay_level += suppress ? 1 : -1;
266        is_assert(reshade_delay_level>=0);
267
268        if (!reshade_delay_level) { // switched off delay
269            if (reshade_was_suppressed) {
270                reshade_cb();
271                reshade_was_suppressed = false;
272            }
273        }
274
275#if defined(ASSERTION_USED)
276        bool start_of_delay = reshade_delay_level == 1 && suppress;
277        is_assert(implicated(start_of_delay, !reshade_was_suppressed));
278#endif
279    }
280    friend class DelayReshade;
281
282protected:
283    ShaderPluginPtr active_plugin;  // null means: no plugin active
284    int             first_range_gc; // has to be set by init()!
285    Phaser          phaser;
286
287public:
288    ItemShader(const std::string& id_, const std::string& description_, ReshadeCallback rcb, int undefined_gc_) :
289        id(id_),
290        description(description_),
291        reshade_cb(rcb),
292        undefined_gc(undefined_gc_),
293        reshade_delay_level(0),
294        reshade_was_suppressed(false),
295        first_range_gc(-1)
296    {}
297    virtual ~ItemShader() {}
298
299    virtual void register_plugin(ShaderPluginPtr plugin) = 0;
300    virtual bool activate_plugin(const std::string& id)  = 0; // returns 'true' on success
301    bool deactivate_plugin() { return activate_plugin(NO_PLUGIN_SELECTED); }
302    virtual std::string active_plugin_name() const = 0;
303
304    bool is_active_plugin(const ShaderPlugin& plugin) const {
305        return active_plugin_name() == plugin.get_id();
306    }
307
308    virtual void init() = 0; // call once after register_plugin was called (activates plugin stored in AWAR)
309    virtual void popup_config_window(AW_root *awr) = 0;
310
311    virtual void check_dimension_change() = 0;
312
313    const std::string& get_id() const { return id; }
314    const std::string& get_description() const { return description; }
315
316    bool active() const { return active_plugin.isSet(); }
317    bool overlay_marked()       const { return !active() || active_plugin->overlay_marked(); }       // if true, caller should use marked-GC
318    bool overlay_color_groups() const { return active() ? active_plugin->overlay_color_groups() : AW_color_groups_active(); } // if true, caller should use color-groups-GCs
319
320    ShadedValue shade(GBDATA *gb_item) const {
321        is_assert(active()); // don't call if no shader is active
322        return active() ? active_plugin->shade(gb_item) : ValueTuple::undefined();
323    }
324    int to_GC(const ShadedValue& val) const {
325        is_assert(first_range_gc>0);
326        if (val->is_defined()) {
327            return first_range_gc + val->range_offset(phaser);
328        }
329        return undefined_gc;
330    }
331
332    void trigger_reshade_callback(ReshadeMode mode) {
333        if (mode == CHECK_DIMENSION_CHANGE) check_dimension_change();
334
335        if (reshade_delay_level) reshade_was_suppressed = true;
336        else                     reshade_cb();
337    }
338};
339
340class DelayReshade : virtual Noncopyable {
341    const ItemShader *shader;
342public:
343    DelayReshade(const ItemShader *shader_) :
344        shader(shader_)
345    {
346        shader->delay_reshade_callbacks(true);
347    }
348    ~DelayReshade() {
349        shader->delay_reshade_callbacks(false);
350    }
351};
352
353inline const char *ShaderPlugin::get_shader_local_id() const {
354    is_assert(plugged_into);
355    return GBS_global_string("%s_%s", plugged_into->get_id().c_str(), get_id().c_str());
356}
357inline void ShaderPlugin::trigger_reshade_if_active_cb(ReshadeMode mode) {
358    if (plugged_into && plugged_into->is_active_plugin(*this)) {
359        plugged_into->trigger_reshade_callback(mode);
360    }
361}
362
363// -----------------------------
364//      ItemShader registry
365
366ItemShader       *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);
367const ItemShader *findItemShader(const char *id);
368void              destroyAllItemShaders();
369
370#else
371#error item_shader.h included twice
372#endif // ITEM_SHADER_H
Note: See TracBrowser for help on using the repository browser.