Add RenderState::get_auto_shader_state()

This commit is contained in:
David Rose 2011-11-07 00:08:04 +00:00
parent cebecba2fc
commit 2a3ad0d4e5
36 changed files with 347 additions and 10 deletions

View File

@ -7141,13 +7141,14 @@ set_state_and_transform(const RenderState *target,
#ifndef OPENGLES_1
if (_target_shader->auto_shader()) {
// If we don't have a generated shader, make sure we have a ShaderGenerator, then generate the shader.
if (_target_rs->_generated_shader == NULL) {
CPT(RenderState) shader = _target_rs->get_auto_shader_state();
if (shader->_generated_shader == NULL) {
if (_shader_generator == NULL) {
_shader_generator = new ShaderGenerator(this, _scene_setup->get_display_region()->get_window());
}
const_cast<RenderState*>(_target_rs.p())->_generated_shader = DCAST(ShaderAttrib, _shader_generator->synthesize_shader(_target_rs));
const_cast<RenderState*>(shader.p())->_generated_shader = DCAST(ShaderAttrib, _shader_generator->synthesize_shader(shader));
}
_target_shader = DCAST(ShaderAttrib, _target_rs->_generated_shader);
_target_shader = DCAST(ShaderAttrib, shader->_generated_shader);
}
#endif

View File

@ -108,6 +108,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: AlphaTestAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) AlphaTestAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: AlphaTestAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -44,6 +44,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
PandaCompareFunc _mode;

View File

@ -114,6 +114,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: AuxBitplaneAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) AuxBitplaneAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: AuxBitplaneAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -77,6 +77,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
int _outputs;

View File

@ -908,6 +908,16 @@ invert_compose_impl(const RenderAttrib *other) const {
return other;
}
////////////////////////////////////////////////////////////////////
// Function: ClipPlaneAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ClipPlaneAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: ClipPlaneAttrib::sort_on_planes
// Access: Private

View File

@ -103,6 +103,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
INLINE void check_filtered() const;

View File

@ -153,6 +153,21 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: ColorAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ColorAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
// For a ColorAttrib, the only relevant information is the type: is
// it flat-shaded or vertex-shaded? The actual color value is read
// by the shader from the graphics state.
ColorAttrib *attrib = new ColorAttrib(_type, LColor(1.0f, 1.0f, 1.0f, 1.0f));
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: ColorAttrib::quantize_color
// Access: Private

View File

@ -51,6 +51,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
void quantize_color();

View File

@ -152,6 +152,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: ColorBlendAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ColorBlendAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: ColorBlendAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -96,6 +96,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
Mode _mode;

View File

@ -274,6 +274,20 @@ invert_compose_impl(const RenderAttrib *other) const {
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: ColorScaleAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ColorScaleAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
// A ColorScaleAttrib doesn't directly contribute to the auto-shader
// contents--instead, the shader is always written to query
// attr_colorscale at runtime. So the attrib itself means nothing
// to the shader.
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: ColorScaleAttrib::quantize_scale
// Access: Private

View File

@ -55,6 +55,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
void quantize_scale();

View File

@ -120,6 +120,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: FogAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) FogAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: FogAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -42,6 +42,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
PT(Fog) _fog;

View File

@ -940,6 +940,16 @@ invert_compose_impl(const RenderAttrib *other) const {
return other;
}
////////////////////////////////////////////////////////////////////
// Function: LightAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) LightAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: LightAttrib::sort_on_lights
// Access: Private

View File

@ -102,6 +102,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
INLINE void check_filtered() const;

View File

@ -282,6 +282,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: LightRampAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) LightRampAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: LightRampAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -62,6 +62,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
LightRampMode _mode;

View File

@ -121,6 +121,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: MaterialAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) MaterialAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: MaterialAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -45,6 +45,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
PT(Material) _material;

View File

@ -118,6 +118,31 @@ get_unique() const {
return return_unique((RenderAttrib *)this);
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::get_auto_shader_attrib
// Access: Published
// Description: Returns the variant of this RenderAttrib that's most
// relevant for associating with an auto-generated
// shader. This should be a new RenderAttrib of the
// same type as this one, with any superfluous data set
// to neutral. Only the parts of the attrib that
// contribute to the shader should be reflected in the
// returned attrib. The idea is to associate the
// auto-generated shader with the most neutral form of
// all states, to allow it to be shared across as many
// RenderState objects as possible.
//
// If this RenderAttrib is completely irrelevant to the
// auto-shader, this should return NULL to indicate that
// the attrib won't be assocaited with the shader at
// all. In this case the attrib does not contribute to
// the shader meaningfully.
////////////////////////////////////////////////////////////////////
INLINE CPT(RenderAttrib) RenderAttrib::
get_auto_shader_attrib(const RenderState *state) const {
return get_auto_shader_attrib_impl(state);
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::register_slot
// Access: Public, Static

View File

@ -140,6 +140,16 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) RenderAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::unref
// Access: Published, Virtual

View File

@ -28,6 +28,7 @@ class AttribSlots;
class GraphicsStateGuardianBase;
class CullTraverser;
class CullTraverserData;
class RenderState;
////////////////////////////////////////////////////////////////////
// Class : RenderAttrib
@ -81,6 +82,7 @@ PUBLISHED:
INLINE int compare_to(const RenderAttrib &other) const;
INLINE size_t get_hash() const;
INLINE CPT(RenderAttrib) get_unique() const;
INLINE CPT(RenderAttrib) get_auto_shader_attrib(const RenderState *state) const;
virtual bool unref() const;
@ -185,6 +187,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
void output_comparefunc(ostream &out, PandaCompareFunc fn) const;
public:

View File

@ -69,6 +69,7 @@ TypeHandle RenderState::_type_handle;
RenderState::
RenderState() :
_flags(0),
_auto_shader_state(NULL),
_lock("RenderState")
{
// Allocate the _attributes array.
@ -99,6 +100,7 @@ RenderState::
RenderState(const RenderState &copy) :
_filled_slots(copy._filled_slots),
_flags(0),
_auto_shader_state(NULL),
_lock("RenderState")
{
// Allocate the _attributes array.
@ -145,6 +147,14 @@ RenderState::
nassertv(_saved_entry == -1);
nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
// Make sure the _auto_shader_state cache pointer is cleared.
if (_auto_shader_state != (const RenderState *)NULL) {
if (_auto_shader_state != this) {
cache_unref_delete(_auto_shader_state);
}
_auto_shader_state = NULL;
}
// If this was true at the beginning of the destructor, but is no
// longer true now, probably we've been double-deleted.
nassertv(get_ref_count() == 0);
@ -843,9 +853,32 @@ get_invert_composition_cache() const {
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: RenderState::get_auto_shader_state
// Access: Published
// Description: Returns the base RenderState that should have the
// generated_shader stored within it, for generated
// shader states. The returned object might be the same
// as this object, or it might be a different
// RenderState with certain attributes removed, or set
// to their default values.
//
// The point is to avoid needless regeneration of the
// shader attrib by storing the generated shader on a
// common RenderState object, with all irrelevant
// attributes removed.
////////////////////////////////////////////////////////////////////
const RenderState *RenderState::
get_auto_shader_state() const {
if (_auto_shader_state == (const RenderState *)NULL) {
((RenderState *)this)->assign_auto_shader_state();
}
return _auto_shader_state;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::output
// Access: Published, Virtual
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void RenderState::
@ -875,7 +908,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
// Function: RenderState::write
// Access: Published, Virtual
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void RenderState::
@ -1490,6 +1523,58 @@ do_calc_hash() {
_flags |= F_hash_known;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::assign_auto_shader_state
// Access: Private
// Description: Sets _auto_shader_state to the appropriate
// RenderState object pointer, either the same pointer
// as this object, or some other (simpler) RenderState.
////////////////////////////////////////////////////////////////////
void RenderState::
assign_auto_shader_state() {
CPT(RenderState) state = do_calc_auto_shader_state();
{
LightReMutexHolder holder(*_states_lock);
if (_auto_shader_state == (const RenderState *)NULL) {
_auto_shader_state = state;
if (_auto_shader_state != this) {
_auto_shader_state->cache_ref();
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::do_calc_auto_shader_state
// Access: Private
// Description: Returns the appropriate RenderState that should be
// used to store the auto shader pointer for nodes that
// shader this RenderState.
////////////////////////////////////////////////////////////////////
CPT(RenderState) RenderState::
do_calc_auto_shader_state() {
RenderState *state = new RenderState;
SlotMask mask = _filled_slots;
int slot = mask.get_lowest_on_bit();
while (slot >= 0) {
const Attribute &attrib = _attributes[slot];
nassertr(attrib._attrib != (RenderAttrib *)NULL, this);
CPT(RenderAttrib) new_attrib = attrib._attrib->get_auto_shader_attrib(this);
if (new_attrib != NULL) {
nassertr(new_attrib->get_slot() == slot, this);
state->_attributes[slot].set(new_attrib, 0);
state->_filled_slots.set_bit(slot);
}
mask.clear_bit(slot);
slot = mask.get_lowest_on_bit();
}
return return_new(state);
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::return_new
@ -1941,6 +2026,14 @@ void RenderState::
remove_cache_pointers() {
nassertv(_states_lock->debug_is_locked());
// First, make sure the _auto_shader_state cache pointer is cleared.
if (_auto_shader_state != (const RenderState *)NULL) {
if (_auto_shader_state != this) {
cache_unref_delete(_auto_shader_state);
}
_auto_shader_state = NULL;
}
// Fortunately, since we added CompositionCache records in pairs, we
// know exactly the set of RenderState objects that have us in their
// cache: it's the same set of RenderState objects that we have in

View File

@ -131,6 +131,8 @@ PUBLISHED:
PyObject *get_invert_composition_cache() const;
#endif // HAVE_PYTHON
const RenderState *get_auto_shader_state() const;
void output(ostream &out) const;
void write(ostream &out, int indent_level) const;
@ -167,6 +169,8 @@ private:
INLINE bool do_node_unref() const;
INLINE void calc_hash();
void do_calc_hash();
void assign_auto_shader_state();
CPT(RenderState) do_calc_auto_shader_state();
class CompositionCycleDescEntry {
public:
@ -311,12 +315,14 @@ private:
int _draw_order;
size_t _hash;
const RenderState *_auto_shader_state;
enum Flags {
F_checked_bin_index = 0x000001,
F_checked_cull_callback = 0x000002,
F_has_cull_callback = 0x000004,
F_is_destructing = 0x000008,
F_hash_known = 0x000010,
F_checked_bin_index = 0x000001,
F_checked_cull_callback = 0x000002,
F_has_cull_callback = 0x000004,
F_is_destructing = 0x000008,
F_hash_known = 0x000010,
};
unsigned int _flags;

View File

@ -732,6 +732,27 @@ compose_impl(const RenderAttrib *other) const {
return return_new(attr);
}
////////////////////////////////////////////////////////////////////
// Function: ShaderAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ShaderAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
// For a ShaderAttrib, we only need to preserve the auto-shader
// flags. Custom shaders, and custom shader inputs, aren't relevant
// to the shader generator.
ShaderAttrib *attrib = new ShaderAttrib;
attrib->_auto_shader = _auto_shader;
attrib->_has_shader = _has_shader;
attrib->_auto_normal_on = _auto_normal_on;
attrib->_auto_glow_on = _auto_glow_on;
attrib->_auto_gloss_on = _auto_gloss_on;
attrib->_auto_ramp_on = _auto_ramp_on;
attrib->_auto_shadow_on = _auto_shadow_on;
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: ShaderAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -119,6 +119,7 @@ protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:

View File

@ -563,6 +563,16 @@ invert_compose_impl(const RenderAttrib *other) const {
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TexGenAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TexGenAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: TexGenAttrib::filled_stages
// Access: Private

View File

@ -79,6 +79,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
class ModeDef;

View File

@ -472,6 +472,30 @@ invert_compose_impl(const RenderAttrib *other) const {
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TexMatrixAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TexMatrixAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
// For a TexMatrixAttrib, the particular matrix per TextureStage
// isn't important, just whether there is a matrix at all. So we
// create a new state with an identity matrix everywhere there is a
// matrix at all in the original.
TexMatrixAttrib *attrib = new TexMatrixAttrib;
Stages::const_iterator ai;
for (ai = _stages.begin(); ai != _stages.end(); ++ai) {
StageNode sn((*ai)._stage);
sn._transform = TransformState::make_identity();
attrib->_stages.insert(attrib->_stages.end(), sn);
}
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TexMatrixAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -70,6 +70,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
INLINE void check_stage_list() const;

View File

@ -769,6 +769,16 @@ invert_compose_impl(const RenderAttrib *other) const {
return other;
}
////////////////////////////////////////////////////////////////////
// Function: TextureAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TextureAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: TextureAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -93,6 +93,7 @@ protected:
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
INLINE void check_sorted() const;

View File

@ -123,6 +123,16 @@ get_hash_impl() const {
return hash;
}
////////////////////////////////////////////////////////////////////
// Function: TransparencyAttrib::get_auto_shader_attrib_impl
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TransparencyAttrib::
get_auto_shader_attrib_impl(const RenderState *state) const {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: TransparencyAttrib::register_with_read_factory
// Access: Public, Static

View File

@ -63,6 +63,7 @@ public:
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
private:
Mode _mode;