Merge remote-tracking branch 'origin/master' into deploy-ng

This commit is contained in:
rdb 2017-10-26 21:23:49 +02:00
commit fba41dafae
92 changed files with 7380 additions and 5537 deletions

View File

@ -1,10 +0,0 @@
/**
* @file direct.cxx
* @author drose
* @date 2000-05-18
*/
// This is a dummy file whose sole purpose is to give the compiler something
// to compile when making libdirect.so in NO_DEFER mode, which generates an
// empty library that itself links with all the other shared libraries that
// make up libdirect.

View File

@ -708,7 +708,7 @@ pack_object(PyObject *object) {
pack_int64(PyLong_AsLongLong(object));
#if PY_MAJOR_VERSION >= 3
} else if (PyUnicode_Check(object)) {
char *buffer;
const char *buffer;
Py_ssize_t length;
buffer = PyUnicode_AsUTF8AndSize(object, &length);
if (buffer) {

File diff suppressed because it is too large Load Diff

View File

@ -397,6 +397,7 @@ pop_struct() {
%type <u.type> class_derivation_name
%type <u.type> enum_element_type
%type <u.type> maybe_trailing_return_type
%type <u.identifier> maybe_comma_identifier
/*%type <u.type> typedefname*/
%type <u.identifier> name
%type <u.identifier> name_no_final
@ -548,202 +549,264 @@ declaration:
{
current_scope->set_current_vis(V_private);
}
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ')' ';'
{
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file);
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER maybe_comma_identifier ')' ';'
{
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
} else {
CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
CPPFunctionGroup *setter_func = NULL;
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
} else {
setter_func = setter->as_function_group();
if ($6 != nullptr) {
CPPDeclaration *setter = $6->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $6->get_fully_scoped_name(), @6);
} else {
make_property->_set_function = setter->as_function_group();
}
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
} else {
CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
CPPFunctionGroup *setter_func = NULL;
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
} else {
setter_func = setter->as_function_group();
make_property->_set_function = setter->as_function_group();
}
CPPDeclaration *deleter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid delete method: " + $9->get_fully_scoped_name(), @9);
deleter = NULL;
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
if (deleter) {
} else {
make_property->_del_function = deleter->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
length_getter = NULL;
length_getter = nullptr;
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
getter = nullptr;
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file);
make_property->_length_function = length_getter->as_function_group();
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
if (getter != nullptr && length_getter != nullptr) {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
make_property->_length_function = length_getter->as_function_group();
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
length_getter = NULL;
length_getter = nullptr;
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
getter = nullptr;
}
if (getter != nullptr && length_getter != nullptr) {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
make_property->_length_function = length_getter->as_function_group();
} else {
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
CPPFunctionGroup *setter_func = NULL;
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
setter_func = setter->as_function_group();
make_property->_set_function = setter->as_function_group();
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
make_property->_length_function = length_getter->as_function_group();
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
length_getter = NULL;
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
getter = nullptr;
}
if (getter != nullptr && length_getter != nullptr) {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
make_property->_length_function = length_getter->as_function_group();
} else {
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
CPPFunctionGroup *setter_func = NULL;
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
setter_func = setter->as_function_group();
make_property->_set_function = setter->as_function_group();
}
CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer);
if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11);
deleter = NULL;
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
make_property->_length_function = length_getter->as_function_group();
if (deleter) {
} else {
make_property->_del_function = deleter->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ')' ';'
{
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid item getter method: " + $5->get_fully_scoped_name(), @5);
} else {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
} else {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER maybe_comma_identifier ')' ';'
{
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
} else {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
}
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
make_property->_set_function = setter->as_function_group();
}
if ($10 != nullptr) {
CPPDeclaration *deleter = $10->find_symbol(current_scope, global_scope, current_lexer);
if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid delete method: " + $10->get_fully_scoped_name(), @10);
} else {
make_property->_del_function = deleter->as_function_group();
}
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5);
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
}
if (hasser && getter) {
} else {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3,
hasser->as_function_group(),
getter->as_function_group(),
NULL, NULL,
make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal,
current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5);
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
}
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
}
CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer);
if (clearer == (CPPDeclaration *)NULL || clearer->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid clear-function: " + $11->get_fully_scoped_name(), @11);
}
if (hasser && getter && setter && clearer) {
} else {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3,
hasser->as_function_group(),
getter->as_function_group(),
setter->as_function_group(),
clearer->as_function_group(),
make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal,
current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
}
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
make_property->_set_function = setter->as_function_group();
}
CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer);
if (clearer == nullptr || clearer->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid clear method: " + $11->get_fully_scoped_name(), @11);
} else {
make_property->_clear_function = clearer->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
@ -1723,6 +1786,18 @@ maybe_trailing_return_type:
;
maybe_comma_identifier:
empty
{
$$ = NULL;
}
| ',' IDENTIFIER
{
$$ = $2;
}
;
function_parameter_list:
empty
{

View File

@ -18,37 +18,17 @@
*
*/
CPPMakeProperty::
CPPMakeProperty(CPPIdentifier *ident,
CPPFunctionGroup *getter, CPPFunctionGroup *setter,
CPPMakeProperty(CPPIdentifier *ident, Type type,
CPPScope *current_scope, const CPPFile &file) :
CPPDeclaration(file),
_ident(ident),
_length_function(NULL),
_has_function(NULL),
_get_function(getter),
_set_function(setter),
_clear_function(NULL),
_del_function(NULL)
{
_ident->_native_scope = current_scope;
}
/**
*
*/
CPPMakeProperty::
CPPMakeProperty(CPPIdentifier *ident,
CPPFunctionGroup *hasser, CPPFunctionGroup *getter,
CPPFunctionGroup *setter, CPPFunctionGroup *clearer,
CPPScope *current_scope, const CPPFile &file) :
CPPDeclaration(file),
_ident(ident),
_length_function(NULL),
_has_function(hasser),
_get_function(getter),
_set_function(setter),
_clear_function(clearer),
_del_function(NULL)
_type(type),
_length_function(nullptr),
_has_function(nullptr),
_get_function(nullptr),
_set_function(nullptr),
_clear_function(nullptr),
_del_function(nullptr)
{
_ident->_native_scope = current_scope;
}

View File

@ -23,16 +23,72 @@
* This is a MAKE_PROPERTY() declaration appearing within a class body. It
* means to generate a property within Python, replacing (for instance)
* get_something()/set_something() with a synthetic 'something' attribute.
*
* This is an example of a simple property (MAKE_PROPERTY is defined as
* the built-in __make_property):
* @@code
* Thing get_thing() const;
* void set_thing(const Thing &);
*
* MAKE_PROPERTY(thing, get_thing, set_thing);
* @@endcode
* The setter may be omitted to make the property read-only.
*
* There is also a secondary macro that allows the property to be set to a
* cleared state using separate clear functions. In the scripting language,
* this would be represented by a "null" value, or an "optional" construct in
* languages that have no notion of a null value.
*
* @@code
* bool has_thing() const;
* Thing get_thing() const;
* void set_thing(const Thing &);
* void clear_thing();
* MAKE_PROPERTY2(thing, has_thing, get_thing, set_thing, clear_thing);
* @@endcode
* As with MAKE_PROPERTY, both the setter and clearer can be omitted to create
* a read-only property.
*
* Thirdly, there is a variant called MAKE_SEQ_PROPERTY. It takes a length
* function as argument and the getter and setter take an index as first
* argument:
* @@code
* size_t get_num_things() const;
* Thing &get_thing(size_t i) const;
* void set_thing(size_t i, Thing value) const;
* void remove_thing(size_t i) const;
*
* MAKE_SEQ_PROPERTY(get_num_things, get_thing, set_thing, remove_thing);
* @@endcode
*
* Lastly, there is the possibility to have properties with key/value
* associations, often called a "map" or "dictionary" in scripting languages:
* @@code
* bool has_thing(string key) const;
* Thing &get_thing(string key) const;
* void set_thing(string key, Thing value) const;
* void clear_thing(string key) const;
*
* MAKE_MAP_PROPERTY(things, has_thing, get_thing, set_thing, clear_thing);
* @@endcode
* You may also replace the "has" function with a "find" function that returns
* an index. If the returned index is negative (or in the case of an unsigned
* integer, the maximum value), the item is assumed not to be present in the
* mapping.
*
* It is also possible to use both MAKE_SEQ_PROPERTY and MAKE_MAP_PROPERTY on
* the same property name. This implies that this property has both a
* sequence and mapping interface.
*/
class CPPMakeProperty : public CPPDeclaration {
public:
CPPMakeProperty(CPPIdentifier *ident,
CPPFunctionGroup *getter, CPPFunctionGroup *setter,
CPPScope *current_scope, const CPPFile &file);
enum Type {
T_normal = 0x0,
T_sequence = 0x1,
T_mapping = 0x2,
};
CPPMakeProperty(CPPIdentifier *ident,
CPPFunctionGroup *hasser, CPPFunctionGroup *getter,
CPPFunctionGroup *setter, CPPFunctionGroup *clearer,
CPPMakeProperty(CPPIdentifier *ident, Type type,
CPPScope *current_scope, const CPPFile &file);
virtual string get_simple_name() const;
@ -46,8 +102,7 @@ public:
virtual CPPMakeProperty *as_make_property();
CPPIdentifier *_ident;
// If length_function is not NULL, this is actually a sequence property,
// and the other functions take an additional index argument.
Type _type;
CPPFunctionGroup *_length_function;
CPPFunctionGroup *_has_function;
CPPFunctionGroup *_get_function;

View File

@ -94,6 +94,14 @@
#define RETURNS_ALIGNED(x)
#endif
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
/*
include win32 defns for everything up to WinServer2003, and assume
I'm smart enough to use GetProcAddress for backward compat on
@ -456,6 +464,7 @@ typedef struct _object PyObject;
#define MAKE_PROPERTY2(property_name, ...) __make_property2(property_name, __VA_ARGS__)
#define MAKE_SEQ(seq_name, num_name, element_name) __make_seq(seq_name, num_name, element_name)
#define MAKE_SEQ_PROPERTY(property_name, ...) __make_seq_property(property_name, __VA_ARGS__)
#define MAKE_MAP_PROPERTY(property_name, ...) __make_map_property(property_name, __VA_ARGS__)
#define EXTENSION(x) __extension x
#define EXTEND __extension
#else
@ -466,6 +475,7 @@ typedef struct _object PyObject;
#define MAKE_PROPERTY2(property_name, ...)
#define MAKE_SEQ(seq_name, num_name, element_name)
#define MAKE_SEQ_PROPERTY(property_name, ...)
#define MAKE_MAP_PROPERTY(property_name, ...)
#define EXTENSION(x)
#define EXTEND
#endif

View File

@ -51,6 +51,10 @@ PUBLISHED:
static Filename get_cwd();
PUBLISHED:
MAKE_MAP_PROPERTY(environment_variables, has_environment_variable,
get_environment_variable, set_environment_variable);
MAKE_SEQ_PROPERTY(args, get_num_args, get_arg);
MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name);
MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name);

View File

@ -75,8 +75,8 @@ InterfaceMaker::MakeSeq::
MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
_name(name),
_imake_seq(imake_seq),
_length_getter(NULL),
_element_getter(NULL)
_length_getter(nullptr),
_element_getter(nullptr)
{
}
@ -86,12 +86,10 @@ MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
InterfaceMaker::Property::
Property(const InterrogateElement &ielement) :
_ielement(ielement),
_length_function(NULL),
_getter(NULL),
_setter(NULL),
_has_function(NULL),
_clear_function(NULL),
_deleter(NULL),
_length_function(nullptr),
_has_function(nullptr),
_clear_function(nullptr),
_deleter(nullptr),
_has_this(false)
{
}

View File

@ -126,9 +126,9 @@ public:
Property(const InterrogateElement &ielement);
const InterrogateElement &_ielement;
vector<FunctionRemap *> _getter_remaps;
vector<FunctionRemap *> _setter_remaps;
Function *_length_function;
Function *_getter;
Function *_setter;
Function *_has_function;
Function *_clear_function;
Function *_deleter;

View File

@ -51,7 +51,7 @@ test_assert(ostream &out, int indent_level) const {
indent(out, indent_level)
<< "Notify *notify = Notify::ptr();\n";
indent(out, indent_level)
<< "if (notify->has_assert_failed()) {\n";
<< "if (UNLIKELY(notify->has_assert_failed())) {\n";
indent(out, indent_level + 2)
<< "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n";
indent(out, indent_level + 2)

View File

@ -2640,8 +2640,7 @@ write_module_class(ostream &out, Object *obj) {
for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
Property *property = (*pit);
const InterrogateElement &ielem = property->_ielement;
if (!property->_has_this ||
property->_getter == NULL || !is_function_legal(property->_getter)) {
if (!property->_has_this || property->_getter_remaps.empty()) {
continue;
}
@ -2652,8 +2651,7 @@ write_module_class(ostream &out, Object *obj) {
string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
string setter = "NULL";
if (property->_length_function == NULL &&
property->_setter != NULL && is_function_legal(property->_setter)) {
if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
}
@ -3190,8 +3188,7 @@ write_module_class(ostream &out, Object *obj) {
for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
Property *property = (*pit);
const InterrogateElement &ielem = property->_ielement;
if (property->_has_this ||
property->_getter == NULL || !is_function_legal(property->_getter)) {
if (property->_has_this || property->_getter_remaps.empty()) {
continue;
}
@ -3200,8 +3197,7 @@ write_module_class(ostream &out, Object *obj) {
string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
string setter = "NULL";
if (property->_length_function == NULL &&
property->_setter != NULL && is_function_legal(property->_setter)) {
if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
}
@ -4389,57 +4385,6 @@ write_function_forset(ostream &out,
std::sort(remaps.begin(), remaps.end(), RemapCompareLess);
std::vector<FunctionRemap *>::const_iterator sii;
// Check if all of them have an InternalName pointer as first parameter.
// This is a dirty hack, of course, to work around an awkward overload
// resolution problem in NodePath::set_shader_input() (while perhaps also
// improving its performance). If I had more time I'd create a better
// solution.
bool first_internalname = false;
string first_pexpr2(first_pexpr);
if (first_pexpr.empty() && args_type != AT_no_args) {
first_internalname = true;
for (sii = remaps.begin(); sii != remaps.end(); ++sii) {
remap = (*sii);
if (remap->_parameters.size() > (size_t)remap->_has_this) {
ParameterRemap *param = remap->_parameters[(size_t)remap->_has_this]._remap;
string param_name = param->get_orig_type()->get_local_name(&parser);
if (param_name != "CPT_InternalName" &&
param_name != "InternalName const *" &&
param_name != "InternalName *") {
// Aw.
first_internalname = false;
break;
}
} else {
first_internalname = false;
break;
}
}
if (first_internalname) {
// Yeah, all remaps have a first InternalName parameter, so process
// that and remove it from the args tuple.
if (args_type == AT_single_arg) {
// Bit of a weird case, but whatever.
indent(out, indent_level) << "PyObject *name_obj = arg;\n";
args_type = AT_no_args;
} else if (min_num_args == 2 && max_num_args == 2) {
indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n";
indent(out, indent_level) << "PyObject *arg = PyTuple_GET_ITEM(args, 1);\n";
args_type = AT_single_arg;
} else {
indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n";
indent(out, indent_level) << "args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));\n";
return_flags |= RF_decref_args;
}
indent(out, indent_level) << "PT(InternalName) name;\n";
indent(out, indent_level) << "if (Dtool_Coerce_InternalName(name_obj, name)) {\n";
indent_level += 2;
first_pexpr2 = "name";
}
}
int num_coercion_possible = 0;
sii = remaps.begin();
while (sii != remaps.end()) {
@ -4474,7 +4419,7 @@ write_function_forset(ostream &out,
write_function_instance(out, remap, min_num_args, max_num_args,
expected_params, indent_level + 2,
false, false, args_type, return_flags,
check_exceptions, first_pexpr2);
check_exceptions, first_pexpr);
indent(out, indent_level) << "}\n\n";
}
@ -4507,28 +4452,11 @@ write_function_forset(ostream &out,
write_function_instance(out, remap, min_num_args, max_num_args,
ignore_expected_params, indent_level + 2,
true, false, args_type, return_flags,
check_exceptions, first_pexpr2);
check_exceptions, first_pexpr);
indent(out, indent_level) << "}\n\n";
}
}
if (first_internalname) {
indent_level -= 2;
if (report_errors) {
indent(out, indent_level) << "} else {\n";
string class_name = remap->_cpptype->get_simple_name();
ostringstream msg;
msg << classNameFromCppName(class_name, false) << "."
<< methodNameFromCppName(remap, class_name, false)
<< "() first argument must be str or InternalName";
error_raise_return(out, indent_level + 2, return_flags,
"TypeError", msg.str());
}
indent(out, indent_level) << "}\n";
}
} else {
// There is only one possible overload with this number of parameters.
// Just call it.
@ -4891,7 +4819,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
<< default_value->_str.size() << ";\n";
}
} else {
indent(out, indent_level) << "char *" << param_name << "_str = NULL;\n";
indent(out, indent_level) << "const char *" << param_name << "_str = NULL;\n";
indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n";
}
@ -4900,7 +4828,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
// As a special hack to fix pickling in Python 3, if the method name
// starts with py_decode_, we take a bytes object instead of a str.
if (remap->_cppfunc->get_local_name().substr(0, 10) == "py_decode_") {
indent(out, indent_level) << "if (PyBytes_AsStringAndSize(arg, &"
indent(out, indent_level) << "if (PyBytes_AsStringAndSize(arg, (char **)&"
<< param_name << "_str, &" << param_name << "_len) == -1) {\n";
indent(out, indent_level + 2) << param_name << "_str = NULL;\n";
indent(out, indent_level) << "}\n";
@ -4910,7 +4838,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
<< param_name << "_len);\n";
}
out << "#else\n"; // NB. PyString_AsStringAndSize also accepts a PyUnicode.
indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, &"
indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, (char **)&"
<< param_name << "_str, &" << param_name << "_len) == -1) {\n";
indent(out, indent_level + 2) << param_name << "_str = NULL;\n";
indent(out, indent_level) << "}\n";
@ -6065,14 +5993,14 @@ write_function_instance(ostream &out, FunctionRemap *remap,
// this for coercion constructors since they are called by other wrapper
// functions which already check this on their own. Generated getters
// obviously can't raise asserts.
if (watch_asserts && (return_flags & RF_coerced) == 0 &&
if (watch_asserts && (return_flags & (RF_coerced | RF_raise_keyerror)) == 0 &&
remap->_type != FunctionRemap::T_getter &&
remap->_type != FunctionRemap::T_setter) {
out << "#ifndef NDEBUG\n";
indent(out, indent_level)
<< "Notify *notify = Notify::ptr();\n";
indent(out, indent_level)
<< "if (notify->has_assert_failed()) {\n";
<< "if (UNLIKELY(notify->has_assert_failed())) {\n";
if (manage_return) {
// Output code to delete any temporary object we may have allocated.
@ -6194,6 +6122,27 @@ write_function_instance(ostream &out, FunctionRemap *remap,
indent(out, indent_level) << "manage = true;\n";
indent(out, indent_level) << "return true;\n";
}
} else if (return_flags & RF_raise_keyerror) {
CPPType *orig_type = remap->_return_type->get_orig_type();
if (TypeManager::is_bool(orig_type) || TypeManager::is_pointer(orig_type)) {
indent(out, indent_level) << "if (!" << return_expr << ") {\n";
} else if (TypeManager::is_unsigned_integer(orig_type)) {
indent(out, indent_level) << "if ((int)" << return_expr << " == -1) {\n";
} else if (TypeManager::is_integer(orig_type)) {
indent(out, indent_level) << "if (" << return_expr << " < 0) {\n";
} else {
indent(out, indent_level) << "if (false) {\n";
}
if (args_type == AT_single_arg) {
indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, arg);\n";
} else {
indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, key);\n";
}
error_return(out, indent_level + 2, return_flags);
indent(out, indent_level) << "}\n";
}
// Close the extra braces opened earlier.
@ -6466,19 +6415,21 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName,
*/
void InterfaceMakerPythonNative::
write_getset(ostream &out, Object *obj, Property *property) {
// We keep around this empty vector for passing to get_call_str.
const vector_string pexprs;
string ClassName = make_safe_name(obj->_itype.get_scoped_name());
std::string cClassName = obj->_itype.get_true_name();
const InterrogateElement &ielem = property->_ielement;
if (property->_length_function != NULL) {
FunctionRemap *len_remap = nullptr;
if (property->_length_function != nullptr) {
// This is actually a sequence. Wrap this with a special class.
FunctionRemap *len_remap = property->_length_function->_remaps.front();
vector_string pexprs;
len_remap = property->_length_function->_remaps.front();
out << "/**\n"
" * sequence length function for property " << cClassName << "::" << ielem.get_name() << "\n"
" * sequence length function for property " << ielem.get_scoped_name() << "\n"
" */\n"
"static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n";
if (property->_length_function->_has_this) {
@ -6492,72 +6443,72 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n";
}
out << "}\n\n";
}
// Now write out the getitem helper function.
if (property->_getter != NULL) {
if (property->_getter_remaps.empty()) {
return;
}
if (ielem.is_sequence()) {
out <<
"/**\n"
" * sequence getter for property " << ielem.get_scoped_name() << "\n"
" */\n"
"static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Getitem(PyObject *self, Py_ssize_t index) {\n";
if (property->_has_this) {
out <<
"/**\n"
" * sequence getter for property " << cClassName << "::" << ielem.get_name() << "\n"
" */\n"
"static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getitem(PyObject *self, Py_ssize_t index) {\n";
if (property->_getter->_has_this ||
(property->_has_function && property->_has_function->_has_this)) {
out <<
" " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
" return NULL;\n"
" }\n";
}
// This is a getitem of a sequence type. This means we *need* to raise
// IndexError if we're out of bounds.
out << " if (index < 0 || index >= (Py_ssize_t)"
<< len_remap->get_call_str("local_this", pexprs) << ") {\n";
out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
out << " return NULL;\n";
out << " }\n";
if (property->_has_function != NULL) {
if (property->_has_function->_has_this) {
out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
} else {
out << " if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
}
out << " Py_INCREF(Py_None);\n"
<< " return Py_None;\n"
<< " }\n";
}
std::set<FunctionRemap*> remaps;
// Extract only the getters that take one argument.
Function::Remaps::iterator it;
for (it = property->_getter->_remaps.begin();
it != property->_getter->_remaps.end();
++it) {
FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args();
if (min_num_args <= 1 && max_num_args >= 1) {
remaps.insert(remap);
}
}
string expected_params;
write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
AT_no_args, RF_pyobject | RF_err_null, false, true, "index");
out << " if (!_PyErr_OCCURRED()) {\n";
out << " return Dtool_Raise_BadArgumentsError(\n";
output_quoted(out, 6, expected_params);
out << ");\n"
" }\n"
"}\n\n";
" " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
" return NULL;\n"
" }\n";
}
// This is a getitem of a sequence type. This means we *need* to raise
// IndexError if we're out of bounds.
out << " if (index < 0 || index >= (Py_ssize_t)"
<< len_remap->get_call_str("local_this", pexprs) << ") {\n";
out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
out << " return NULL;\n";
out << " }\n";
/*if (property->_has_function != NULL) {
out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n"
<< " Py_INCREF(Py_None);\n"
<< " return Py_None;\n"
<< " }\n";
}*/
std::set<FunctionRemap*> remaps;
// Extract only the getters that take one integral argument.
Function::Remaps::iterator it;
for (it = property->_getter_remaps.begin();
it != property->_getter_remaps.end();
++it) {
FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args();
if (min_num_args <= 1 && max_num_args >= 1 &&
TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) {
remaps.insert(remap);
}
}
string expected_params;
write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
AT_no_args, RF_pyobject | RF_err_null, false, true, "index");
out << " if (!_PyErr_OCCURRED()) {\n";
out << " return Dtool_Raise_BadArgumentsError(\n";
output_quoted(out, 6, expected_params);
out << ");\n"
" }\n"
"}\n\n";
// Write out a setitem if this is not a read-only property.
if (property->_setter != NULL) {
out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
if (!property->_setter_remaps.empty()) {
out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
if (property->_has_this) {
out << " " << cClassName << " *local_this = NULL;\n";
out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
@ -6595,13 +6546,14 @@ write_getset(ostream &out, Object *obj, Property *property) {
// Extract only the setters that take two arguments.
Function::Remaps::iterator it;
for (it = property->_setter->_remaps.begin();
it != property->_setter->_remaps.end();
for (it = property->_setter_remaps.begin();
it != property->_setter_remaps.end();
++it) {
FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args();
if (min_num_args <= 2 && max_num_args >= 2) {
if (min_num_args <= 2 && max_num_args >= 2 &&
TypeManager::is_integer(remap->_parameters[1]._remap->get_new_type())) {
remaps.insert(remap);
}
}
@ -6619,8 +6571,192 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " return -1;\n";
out << "}\n\n";
}
}
// Now write the getter, which returns a special wrapper object.
// Write the getitem functions.
if (ielem.is_mapping()) {
out <<
"/**\n"
" * mapping getitem for property " << ielem.get_scoped_name() << "\n"
" */\n"
"static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getitem(PyObject *self, PyObject *arg) {\n";
// Before we do the has_function: if this is also a sequence, then we have
// to also handle the case here that we were passed an index.
if (ielem.is_sequence()) {
out <<
"#if PY_MAJOR_VERSION >= 3\n"
" if (PyLong_CheckExact(arg)) {\n"
"#else\n"
" if (PyLong_CheckExact(arg) || PyInt_CheckExact(arg)) {\n"
"#endif\n"
" return Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem(self, PyLongOrInt_AsSize_t(arg));\n"
" }\n\n";
}
if (property->_has_this) {
out <<
" " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
" return NULL;\n"
" }\n";
}
if (property->_has_function != NULL) {
std::set<FunctionRemap*> remaps;
remaps.insert(property->_has_function->_remaps.begin(),
property->_has_function->_remaps.end());
out << " {\n";
string expected_params;
write_function_forset(out, remaps, 1, 1, expected_params, 4, true, true,
AT_single_arg, RF_raise_keyerror | RF_err_null, false, true);
out << " }\n";
}
std::set<FunctionRemap*> remaps;
// Extract only the getters that take one argument. Fish out the ones
// already taken by the sequence getter.
Function::Remaps::iterator it;
for (it = property->_getter_remaps.begin();
it != property->_getter_remaps.end();
++it) {
FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args();
if (min_num_args <= 1 && max_num_args >= 1 &&
(!ielem.is_sequence() || !TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type()))) {
remaps.insert(remap);
}
}
string expected_params;
write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
AT_single_arg, RF_pyobject | RF_err_null, false, true);
out << " if (!_PyErr_OCCURRED()) {\n";
out << " return Dtool_Raise_BadArgumentsError(\n";
output_quoted(out, 6, expected_params);
out << ");\n"
" }\n"
" return NULL;\n"
"}\n\n";
// Write out a setitem if this is not a read-only property.
if (!property->_setter_remaps.empty()) {
out <<
"/**\n"
" * mapping setitem for property " << ielem.get_scoped_name() << "\n"
" */\n"
"static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Setitem(PyObject *self, PyObject *key, PyObject *value) {\n";
if (property->_has_this) {
out <<
" " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
<< classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"
" return -1;\n"
" }\n\n";
}
out << " if (value == (PyObject *)NULL) {\n";
if (property->_deleter != NULL) {
out << " PyObject *arg = key;\n";
std::set<FunctionRemap*> remaps;
remaps.insert(property->_deleter->_remaps.begin(),
property->_deleter->_remaps.end());
string expected_params;
write_function_forset(out, remaps, 1, 1,
expected_params, 4, true, true, AT_single_arg,
RF_int, false, false);
out << " return -1;\n";
} else {
out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n"
" return -1;\n";
}
out << " }\n";
if (property->_clear_function != NULL) {
out << " if (value == Py_None) {\n"
<< " local_this->" << property->_clear_function->_ifunc.get_name() << "(key);\n"
<< " return 0;\n"
<< " }\n";
}
std::set<FunctionRemap*> remaps;
remaps.insert(property->_setter_remaps.begin(),
property->_setter_remaps.end());
// We have to create an args tuple only to unpack it alter, ugh.
out << " PyObject *args = PyTuple_New(2);\n"
<< " PyTuple_SET_ITEM(args, 0, key);\n"
<< " PyTuple_SET_ITEM(args, 1, value);\n"
<< " Py_INCREF(key);\n"
<< " Py_INCREF(value);\n";
string expected_params;
write_function_forset(out, remaps, 2, 2,
expected_params, 2, true, true, AT_varargs,
RF_int | RF_decref_args, false, false);
out << " if (!_PyErr_OCCURRED()) {\n";
out << " Dtool_Raise_BadArgumentsError(\n";
output_quoted(out, 6, expected_params);
out << ");\n";
out << " }\n";
out << " Py_DECREF(args);\n";
out << " return -1;\n";
out << "}\n\n";
}
}
// Now write the actual getter wrapper. It will be a different wrapper
// depending on whether it's a mapping, sequence, or both.
if (ielem.is_mapping() && ielem.is_sequence()) {
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
if (property->_has_this) {
out << " nassertr(self != NULL, NULL);\n"
" Py_INCREF(self);\n";
} else {
out << " Py_XINCREF(self);\n";
}
out << " Dtool_SeqMapWrapper *wrap = PyObject_New(Dtool_SeqMapWrapper, &Dtool_SeqMapWrapper_Type);\n"
" wrap->_seq._base._self = self;\n"
" wrap->_seq._len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
" wrap->_seq._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"
" wrap->_map_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
if (!property->_setter_remaps.empty()) {
out << " wrap->_seq._setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
out << " wrap->_map_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
} else {
out << " wrap->_seq._setitem_func = NULL;\n";
out << " wrap->_map_setitem_func = NULL;\n";
}
out << " return (PyObject *)wrap;\n"
"}\n\n";
} else if (ielem.is_mapping()) {
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
if (property->_has_this) {
out << " nassertr(self != NULL, NULL);\n"
" Py_INCREF(self);\n";
} else {
out << " Py_XINCREF(self);\n";
}
out << " Dtool_MappingWrapper *wrap = PyObject_New(Dtool_MappingWrapper, &Dtool_MappingWrapper_Type);\n"
" wrap->_base._self = self;\n"
" wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
if (!property->_setter_remaps.empty()) {
out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
} else {
out << " wrap->_setitem_func = NULL;\n";
}
out << " return (PyObject *)wrap;\n"
"}\n\n";
} else if (ielem.is_sequence()) {
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
if (property->_has_this) {
out << " nassertr(self != NULL, NULL);\n"
@ -6629,21 +6765,21 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " Py_XINCREF(self);\n";
}
out << " Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
" wrap->_base = self;\n"
" wrap->_base._self = self;\n"
" wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
" wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Getitem;\n";
if (property->_setter != NULL) {
out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Setitem;\n";
" wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n";
if (!property->_setter_remaps.empty()) {
out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
} else {
out << " wrap->_setitem_func = NULL;\n";
}
out << " return (PyObject *)wrap;\n"
"}\n\n";
"}\n\n";
} else if (property->_getter != NULL) {
} else {
// Write out a regular, unwrapped getter.
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
FunctionRemap *remap = property->_getter->_remaps.front();
FunctionRemap *remap = property->_getter_remaps.front();
if (remap->_has_this) {
if (remap->_const_method) {
@ -6679,7 +6815,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << "}\n\n";
// Write out a setter if this is not a read-only property.
if (property->_setter != NULL) {
if (!property->_setter_remaps.empty()) {
out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n";
if (remap->_has_this) {
out << " " << cClassName << " *local_this = NULL;\n";
@ -6698,7 +6834,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
<< " return 0;\n";
} else {
out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << " attribute\");\n"
" return -1;\n";
" return -1;\n";
}
out << " }\n";
@ -6717,9 +6853,9 @@ write_getset(ostream &out, Object *obj, Property *property) {
// Extract only the setters that take one argument.
Function::Remaps::iterator it;
for (it = property->_setter->_remaps.begin();
it != property->_setter->_remaps.end();
++it) {
for (it = property->_setter_remaps.begin();
it != property->_setter_remaps.end();
++it) {
FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args();
@ -6831,62 +6967,8 @@ record_object(TypeIndex type_index) {
ElementIndex element_index = itype.get_element(ei);
const InterrogateElement &ielement = idb->get_element(element_index);
Property *property = new Property(ielement);
if (ielement.has_setter()) {
FunctionIndex func_index = ielement.get_setter();
Function *setter = record_function(itype, func_index);
if (is_function_legal(setter)) {
property->_setter = setter;
property->_has_this |= setter->_has_this;
}
}
if (ielement.has_getter()) {
FunctionIndex func_index = ielement.get_getter();
Function *getter = record_function(itype, func_index);
if (is_function_legal(getter)) {
property->_getter = getter;
property->_has_this |= getter->_has_this;
}
}
if (ielement.has_has_function()) {
FunctionIndex func_index = ielement.get_has_function();
Function *has_function = record_function(itype, func_index);
if (is_function_legal(has_function)) {
property->_has_function = has_function;
property->_has_this |= has_function->_has_this;
}
}
if (ielement.has_clear_function()) {
FunctionIndex func_index = ielement.get_clear_function();
Function *clear_function = record_function(itype, func_index);
if (is_function_legal(clear_function)) {
property->_clear_function = clear_function;
property->_has_this |= clear_function->_has_this;
}
}
if (ielement.has_del_function()) {
FunctionIndex func_index = ielement.get_del_function();
Function *del_function = record_function(itype, func_index);
if (is_function_legal(del_function)) {
property->_deleter = del_function;
property->_has_this |= del_function->_has_this;
}
}
if (ielement.is_sequence()) {
FunctionIndex func_index = ielement.get_length_function();
property->_length_function = record_function(itype, func_index);
if (property->_length_function != nullptr) {
property->_has_this |= property->_length_function->_has_this;
}
}
if (property->_getter != NULL) {
Property *property = record_property(itype, itype.get_element(ei));
if (property != nullptr) {
object->_properties.push_back(property);
} else {
// No use exporting a property without a getter.
@ -6918,6 +7000,96 @@ record_object(TypeIndex type_index) {
}
return object;
}
/**
*
*/
InterfaceMaker::Property *InterfaceMakerPythonNative::
record_property(const InterrogateType &itype, ElementIndex element_index) {
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
const InterrogateElement &ielement = idb->get_element(element_index);
if (!ielement.has_getter()) {
// A property needs at the very least a getter.
return nullptr;
}
Property *property;
{
FunctionIndex func_index = ielement.get_getter();
if (func_index != 0) {
const InterrogateFunction &ifunc = idb->get_function(func_index);
property = new Property(ielement);
InterrogateFunction::Instances::const_iterator ii;
for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
CPPInstance *cppfunc = (*ii).second;
FunctionRemap *remap =
make_function_remap(itype, ifunc, cppfunc, 0);
if (remap != nullptr && is_remap_legal(remap)) {
property->_getter_remaps.push_back(remap);
property->_has_this |= remap->_has_this;
}
}
} else {
return nullptr;
}
}
if (ielement.has_setter()) {
FunctionIndex func_index = ielement.get_setter();
if (func_index != 0) {
const InterrogateFunction &ifunc = idb->get_function(func_index);
InterrogateFunction::Instances::const_iterator ii;
for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
CPPInstance *cppfunc = (*ii).second;
FunctionRemap *remap =
make_function_remap(itype, ifunc, cppfunc, 0);
if (remap != nullptr && is_remap_legal(remap)) {
property->_setter_remaps.push_back(remap);
property->_has_this |= remap->_has_this;
}
}
}
}
if (ielement.has_has_function()) {
FunctionIndex func_index = ielement.get_has_function();
Function *has_function = record_function(itype, func_index);
if (is_function_legal(has_function)) {
property->_has_function = has_function;
property->_has_this |= has_function->_has_this;
}
}
if (ielement.has_clear_function()) {
FunctionIndex func_index = ielement.get_clear_function();
Function *clear_function = record_function(itype, func_index);
if (is_function_legal(clear_function)) {
property->_clear_function = clear_function;
property->_has_this |= clear_function->_has_this;
}
}
if (ielement.has_del_function()) {
FunctionIndex func_index = ielement.get_del_function();
Function *del_function = record_function(itype, func_index);
if (is_function_legal(del_function)) {
property->_deleter = del_function;
property->_has_this |= del_function->_has_this;
}
}
if (ielement.is_sequence()) {
FunctionIndex func_index = ielement.get_length_function();
property->_length_function = record_function(itype, func_index);
}
return property;
}
/**
* Walks through the set of functions in the database and generates wrappers
* for each function, storing these in the database. No actual code should be

View File

@ -47,6 +47,7 @@ public:
virtual bool separate_overloading();
virtual Object *record_object(TypeIndex type_index);
Property *record_property(const InterrogateType &itype, ElementIndex element_index);
protected:
virtual string get_wrapper_prefix();
@ -111,6 +112,9 @@ private:
// Decref temporary args object before returning.
RF_decref_args = 0x1000,
// This raises a KeyError on falsey (or -1) return value.
RF_raise_keyerror = 0x4000,
};
class SlottedFunctionDef {

View File

@ -1798,34 +1798,46 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
}
string property_name = make_property->get_local_name(&parser);
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
// First, check to see if it's already there.
ElementIndex index = 0;
PropertiesByName::const_iterator tni =
_properties_by_name.find(property_name);
if (tni != _properties_by_name.end()) {
ElementIndex index = (*tni).second;
return index;
index = (*tni).second;
const InterrogateElement &ielem = idb->get_element(index);
if (ielem._make_property == make_property) {
// This is the same property.
return index;
}
// It is possible to have property definitions with the same name, but
// they cannot define conflicting interfaces.
if ((ielem.is_sequence() || ielem.is_mapping()) !=
(make_property->_type != CPPMakeProperty::T_normal)) {
cerr << "Conflicting property definitions for " << property_name << "!\n";
return index;
}
}
// If we have a length function (ie. this is a sequence property), we should
// find the function that will give us the length.
FunctionIndex length_function = 0;
bool is_seq = false;
CPPFunctionGroup::Instances::const_iterator fi;
CPPFunctionGroup *fgroup = make_property->_length_function;
if (fgroup != NULL) {
is_seq = true;
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
CPPInstance *function = (*fi);
CPPFunctionType *ftype =
function->_type->as_function_type();
if (ftype != NULL) {
length_function = get_function(function, "", struct_type,
struct_type->get_scope(), 0);
if (length_function != 0) {
break;
if (make_property->_type & CPPMakeProperty::T_sequence) {
CPPFunctionGroup::Instances::const_iterator fi;
CPPFunctionGroup *fgroup = make_property->_length_function;
if (fgroup != NULL) {
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
CPPInstance *function = (*fi);
CPPFunctionType *ftype =
function->_type->as_function_type();
if (ftype != NULL) {
length_function = get_function(function, "", struct_type,
struct_type->get_scope(), 0);
if (length_function != 0) {
break;
}
}
}
}
@ -1840,7 +1852,10 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
CPPInstance *getter = NULL;
CPPType *return_type = NULL;
fgroup = make_property->_get_function;
// How many arguments we expect the getter to have.
size_t num_args = (size_t)(make_property->_type != CPPMakeProperty::T_normal);
CPPFunctionGroup *fgroup = make_property->_get_function;
if (fgroup != NULL) {
CPPFunctionGroup::Instances::const_iterator fi;
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
@ -1852,9 +1867,13 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
const CPPParameterList::Parameters &params = ftype->_parameters->_parameters;
size_t expected_num_args = (size_t)is_seq;
size_t expected_num_args = 0;
size_t index_arg = 0;
if (make_property->_type != CPPMakeProperty::T_normal) {
++expected_num_args;
}
if (!params.empty() && params[0]->get_simple_name() == "self" &&
TypeManager::is_pointer_to_PyObject(params[0]->_type)) {
// Taking a PyObject *self argument.
@ -1867,7 +1886,8 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
(params.size() > expected_num_args &&
params[expected_num_args]->_initializer != NULL)) {
// If this is a sequence getter, it must take an index argument.
if (is_seq && !TypeManager::is_integer(params[index_arg]->_type)) {
if (make_property->_type == CPPMakeProperty::T_sequence &&
!TypeManager::is_integer(params[index_arg]->_type)) {
continue;
}
@ -1899,13 +1919,14 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
CPPInstance *function = (*fi);
CPPFunctionType *ftype =
function->_type->as_function_type();
if (ftype != NULL && TypeManager::is_bool(ftype->_return_type)) {
if (ftype != nullptr && (TypeManager::is_integer(ftype->_return_type) ||
TypeManager::is_pointer(ftype->_return_type))) {
hasser = function;
break;
}
}
if (hasser == NULL || return_type == NULL) {
if (hasser == nullptr) {
cerr << "No instance of has-function '"
<< fgroup->_name << "' is suitable!\n";
return 0;
@ -1921,44 +1942,77 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
CPPInstance *function = (*fi);
CPPFunctionType *ftype = function->_type->as_function_type();
if (ftype != NULL && ftype->_parameters->_parameters.size() == (size_t)is_seq) {
if (ftype != NULL && ftype->_parameters->_parameters.size() == num_args) {
deleter = function;
break;
}
}
if (deleter == NULL || return_type == NULL) {
if (deleter == nullptr) {
cerr << "No instance of delete-function '"
<< fgroup->_name << "' is suitable!\n";
return 0;
}
}
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
// It isn't here, so we'll have to define it.
ElementIndex index = idb->get_next_index();
_properties_by_name[property_name] = index;
if (index == 0) {
// It isn't here, so we'll have to define it.
index = idb->get_next_index();
_properties_by_name[property_name] = index;
InterrogateElement iproperty;
iproperty._name = make_property->get_simple_name();
iproperty._scoped_name = descope(make_property->get_local_name(&parser));
InterrogateElement iproperty;
iproperty._name = make_property->get_simple_name();
iproperty._scoped_name = descope(make_property->get_local_name(&parser));
idb->add_element(index, iproperty);
}
InterrogateElement &iproperty = idb->update_element(index);
if (return_type != NULL) {
iproperty._type = get_type(TypeManager::unwrap_reference(return_type), false);
TypeIndex return_index = get_type(TypeManager::unwrap_reference(return_type), false);
if (iproperty._type != 0 && iproperty._type != return_index) {
cerr << "Property " << property_name << " has inconsistent element type!\n";
}
} else {
iproperty._type = 0;
}
if (length_function != 0) {
if (make_property->_type & CPPMakeProperty::T_sequence) {
iproperty._flags |= InterrogateElement::F_sequence;
iproperty._length_function = length_function;
}
if (getter != NULL) {
iproperty._flags |= InterrogateElement::F_has_getter;
iproperty._getter = get_function(getter, "", struct_type,
struct_type->get_scope(), 0);
nassertr(iproperty._getter, 0);
if (make_property->_type & CPPMakeProperty::T_mapping) {
iproperty._flags |= InterrogateElement::F_mapping;
}
if (make_property->_type == CPPMakeProperty::T_normal) {
if (getter != NULL) {
iproperty._flags |= InterrogateElement::F_has_getter;
iproperty._getter = get_function(getter, "", struct_type,
struct_type->get_scope(), 0);
nassertr(iproperty._getter, 0);
}
} else {
// We could have a mixed sequence/mapping property, so synthesize a
// getitem function. We don't really care what's in here; we just use
// this to store the remaps.
if (!iproperty.has_getter()) {
iproperty._flags |= InterrogateElement::F_has_getter;
iproperty._getter = InterrogateDatabase::get_ptr()->get_next_index();
InterrogateFunction *ifunction = new InterrogateFunction;
ifunction->_instances = new InterrogateFunction::Instances;
InterrogateDatabase::get_ptr()->add_function(iproperty._getter, ifunction);
}
// Add our getter to the generated getitem function.
string signature = TypeManager::get_function_signature(getter);
InterrogateFunction &ifunction =
InterrogateDatabase::get_ptr()->update_function(iproperty._getter);
if (ifunction._instances == nullptr) {
ifunction._instances = new InterrogateFunction::Instances;
}
ifunction._instances->insert(InterrogateFunction::Instances::value_type(signature, getter));
}
if (hasser != NULL) {
@ -2011,7 +2065,6 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
}
}
idb->add_element(index, iproperty);
return index;
}
@ -2621,7 +2674,9 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
} else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) {
ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype, scope);
itype._elements.push_back(element_index);
if (find(itype._elements.begin(), itype._elements.end(), element_index) == itype._elements.end()) {
itype._elements.push_back(element_index);
}
} else if ((*di)->get_subtype() == CPPDeclaration::ST_make_seq) {
MakeSeqIndex make_seq_index = get_make_seq((*di)->as_make_seq(), cpptype);

View File

@ -26,6 +26,7 @@ InterrogateElement(InterrogateModuleDef *def) :
_clear_function = 0;
_del_function = 0;
_length_function = 0;
_make_property = nullptr;
}
/**
@ -52,6 +53,7 @@ operator = (const InterrogateElement &copy) {
_clear_function = copy._clear_function;
_del_function = copy._del_function;
_length_function = copy._length_function;
_make_property = copy._make_property;
}
/**
@ -199,6 +201,14 @@ get_length_function() const {
return _length_function;
}
/**
*
*/
INLINE bool InterrogateElement::
is_mapping() const {
return (_flags & F_mapping) != 0;
}
INLINE ostream &
operator << (ostream &out, const InterrogateElement &element) {

View File

@ -19,6 +19,7 @@
#include "interrogateComponent.h"
class IndexRemapper;
class CPPMakeProperty;
/**
* An internal representation of a data element, like a data member or a
@ -51,6 +52,7 @@ public:
INLINE FunctionIndex get_del_function() const;
INLINE bool is_sequence() const;
INLINE FunctionIndex get_length_function() const;
INLINE bool is_mapping() const;
void output(ostream &out) const;
void input(istream &in);
@ -80,6 +82,8 @@ private:
FunctionIndex _clear_function;
FunctionIndex _del_function;
CPPMakeProperty *_make_property;
friend class InterrogateBuilder;
};

View File

@ -322,11 +322,11 @@ PyObject *_Dtool_Raise_BadArgumentsError() {
* NULL, otherwise Py_None.
*/
PyObject *_Dtool_Return_None() {
if (_PyErr_OCCURRED()) {
if (UNLIKELY(_PyErr_OCCURRED())) {
return NULL;
}
#ifndef NDEBUG
if (Notify::ptr()->has_assert_failed()) {
if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
return Dtool_Raise_AssertionError();
}
#endif
@ -339,11 +339,11 @@ PyObject *_Dtool_Return_None() {
* NULL, otherwise the given boolean value as a PyObject *.
*/
PyObject *Dtool_Return_Bool(bool value) {
if (_PyErr_OCCURRED()) {
if (UNLIKELY(_PyErr_OCCURRED())) {
return NULL;
}
#ifndef NDEBUG
if (Notify::ptr()->has_assert_failed()) {
if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
return Dtool_Raise_AssertionError();
}
#endif
@ -358,11 +358,11 @@ PyObject *Dtool_Return_Bool(bool value) {
* increased.
*/
PyObject *_Dtool_Return(PyObject *value) {
if (_PyErr_OCCURRED()) {
if (UNLIKELY(_PyErr_OCCURRED())) {
return NULL;
}
#ifndef NDEBUG
if (Notify::ptr()->has_assert_failed()) {
if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
return Dtool_Raise_AssertionError();
}
#endif
@ -614,6 +614,14 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
}
if (PyType_Ready(&Dtool_MappingWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper)");
}
if (PyType_Ready(&Dtool_SeqMapWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_SeqMapWrapper)");
}
if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
}
@ -1028,34 +1036,79 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds)
}
/**
* This class is returned from properties that require a settable interface,
* ie. something.children[i] = 3.
* These classes are returned from properties that require a subscript
* interface, ie. something.children[i] = 3.
*/
static void Dtool_SequenceWrapper_dealloc(PyObject *self) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
static void Dtool_WrapperBase_dealloc(PyObject *self) {
Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
nassertv(wrap);
Py_XDECREF(wrap->_base);
Py_XDECREF(wrap->_self);
}
static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
nassertr(wrap, -1);
nassertr(wrap->_len_func, -1);
return wrap->_len_func(wrap->_base);
if (wrap->_len_func != nullptr) {
nassertr(wrap->_len_func, -1);
return wrap->_len_func(wrap->_base._self);
} else {
Dtool_Raise_TypeError("property does not support len()");
return -1;
}
}
static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
nassertr(wrap, NULL);
nassertr(wrap->_getitem_func, NULL);
return wrap->_getitem_func(wrap->_base, index);
nassertr(wrap, nullptr);
nassertr(wrap->_getitem_func, nullptr);
return wrap->_getitem_func(wrap->_base._self, index);
}
static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
nassertr(wrap, -1);
nassertr(wrap->_setitem_func, -1);
return wrap->_setitem_func(wrap->_base, index, value);
if (wrap->_setitem_func != nullptr) {
return wrap->_setitem_func(wrap->_base._self, index, value);
} else {
Dtool_Raise_TypeError("property does not support item assignment");
return -1;
}
}
static PyObject *Dtool_MappingWrapper_getitem(PyObject *self, PyObject *key) {
Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
nassertr(wrap, nullptr);
nassertr(wrap->_getitem_func, nullptr);
return wrap->_getitem_func(wrap->_base._self, key);
}
static int Dtool_MappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
nassertr(wrap, -1);
if (wrap->_setitem_func != nullptr) {
return wrap->_setitem_func(wrap->_base._self, key, value);
} else {
Dtool_Raise_TypeError("property does not support item assignment");
return -1;
}
}
static PyObject *Dtool_SeqMapWrapper_getitem(PyObject *self, PyObject *key) {
Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
nassertr(wrap, nullptr);
nassertr(wrap->_map_getitem_func, nullptr);
return wrap->_map_getitem_func(wrap->_seq._base._self, key);
}
static int Dtool_SeqMapWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
nassertr(wrap, -1);
if (wrap->_map_setitem_func != nullptr) {
return wrap->_map_setitem_func(wrap->_seq._base._self, key, value);
} else {
Dtool_Raise_TypeError("property does not support item assignment");
return -1;
}
}
static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
@ -1071,12 +1124,27 @@ static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
0, // sq_inplace_repeat
};
static PyMappingMethods Dtool_MappingWrapper_MappingMethods = {
0, // mp_length
Dtool_MappingWrapper_getitem,
Dtool_MappingWrapper_setitem,
};
static PyMappingMethods Dtool_SeqMapWrapper_MappingMethods = {
Dtool_SequenceWrapper_length,
Dtool_SeqMapWrapper_getitem,
Dtool_SeqMapWrapper_setitem,
};
/**
* This variant defines only a sequence interface.
*/
PyTypeObject Dtool_SequenceWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sequence wrapper",
sizeof(Dtool_SequenceWrapper),
0, // tp_itemsize
Dtool_SequenceWrapper_dealloc,
Dtool_WrapperBase_dealloc,
0, // tp_print
0, // tp_getattr
0, // tp_setattr
@ -1262,4 +1330,128 @@ PyTypeObject Dtool_StaticProperty_Type = {
#endif
};
/**
* This variant defines only a mapping interface.
*/
PyTypeObject Dtool_MappingWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"mapping wrapper",
sizeof(Dtool_MappingWrapper),
0, // tp_itemsize
Dtool_WrapperBase_dealloc,
0, // tp_print
0, // tp_getattr
0, // tp_setattr
#if PY_MAJOR_VERSION >= 3
0, // tp_reserved
#else
0, // tp_compare
#endif
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
&Dtool_MappingWrapper_MappingMethods,
0, // tp_hash
0, // tp_call
0, // tp_str
PyObject_GenericGetAttr,
PyObject_GenericSetAttr,
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
0, // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
PyType_GenericAlloc,
0, // tp_new
PyObject_Del,
0, // tp_is_gc
0, // tp_bases
0, // tp_mro
0, // tp_cache
0, // tp_subclasses
0, // tp_weaklist
0, // tp_del
#if PY_VERSION_HEX >= 0x02060000
0, // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
0, // tp_finalize
#endif
};
/**
* This variant defines both a sequence and mapping interface.
*/
PyTypeObject Dtool_SeqMapWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sequence/mapping wrapper",
sizeof(Dtool_SeqMapWrapper),
0, // tp_itemsize
Dtool_WrapperBase_dealloc,
0, // tp_print
0, // tp_getattr
0, // tp_setattr
#if PY_MAJOR_VERSION >= 3
0, // tp_reserved
#else
0, // tp_compare
#endif
0, // tp_repr
0, // tp_as_number
&Dtool_SequenceWrapper_SequenceMethods,
&Dtool_SeqMapWrapper_MappingMethods,
0, // tp_hash
0, // tp_call
0, // tp_str
PyObject_GenericGetAttr,
PyObject_GenericSetAttr,
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
0, // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
PyType_GenericAlloc,
0, // tp_new
PyObject_Del,
0, // tp_is_gc
0, // tp_bases
0, // tp_mro
0, // tp_cache
0, // tp_subclasses
0, // tp_weaklist
0, // tp_del
#if PY_VERSION_HEX >= 0x02060000
0, // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
0, // tp_finalize
#endif
};
#endif // HAVE_PYTHON

View File

@ -320,9 +320,9 @@ EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred();
#endif
#ifdef NDEBUG
#define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL)
#define Dtool_CheckErrorOccurred() (UNLIKELY(_PyErr_OCCURRED() != NULL))
#else
#define Dtool_CheckErrorOccurred() _Dtool_CheckErrorOccurred()
#define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred()))
#endif
EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError();
@ -339,6 +339,9 @@ EXPCL_INTERROGATEDB PyObject *_Dtool_Raise_BadArgumentsError();
#define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x)
#endif
// These functions are similar to Dtool_WrapValue, except that they also
// contain code for checking assertions and exceptions when compiling with
// NDEBUG mode on.
EXPCL_INTERROGATEDB PyObject *_Dtool_Return_None();
EXPCL_INTERROGATEDB PyObject *Dtool_Return_Bool(bool value);
EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value);
@ -465,18 +468,36 @@ EXPCL_INTERROGATEDB PyObject *
map_deepcopy_to_copy(PyObject *self, PyObject *args);
/**
* This class is returned from properties that require a settable interface,
* ie. something.children[i] = 3.
* These classes are returned from properties that require a subscript
* interface, ie. something.children[i] = 3.
*/
struct Dtool_WrapperBase {
PyObject_HEAD;
PyObject *_self;
};
struct Dtool_SequenceWrapper {
PyObject_HEAD
PyObject *_base;
Dtool_WrapperBase _base;
lenfunc _len_func;
ssizeargfunc _getitem_func;
ssizeobjargproc _setitem_func;
};
struct Dtool_MappingWrapper {
Dtool_WrapperBase _base;
binaryfunc _getitem_func;
objobjargproc _setitem_func;
};
struct Dtool_SeqMapWrapper {
Dtool_SequenceWrapper _seq;
binaryfunc _map_getitem_func;
objobjargproc _map_setitem_func;
};
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Type;
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SeqMapWrapper_Type;
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type;
EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);

View File

@ -71,11 +71,7 @@ is_on(NotifySeverity severity) const {
INLINE bool NotifyCategory::
is_spam() const {
// Instruct the compiler to optimize for the usual case.
#ifdef __GNUC__
return __builtin_expect(is_on(NS_spam), 0);
#else
return is_on(NS_spam);
#endif
return UNLIKELY(is_on(NS_spam));
}
/**
@ -84,11 +80,7 @@ is_spam() const {
INLINE bool NotifyCategory::
is_debug() const {
// Instruct the compiler to optimize for the usual case.
#ifdef __GNUC__
return __builtin_expect(is_on(NS_debug), 0);
#else
return is_on(NS_debug);
#endif
return UNLIKELY(is_on(NS_debug));
}
#else
/**

View File

@ -70,11 +70,7 @@ template<class GetCategory>
INLINE bool NotifyCategoryProxy<GetCategory>::
is_spam() {
// Instruct the compiler to optimize for the usual case.
#ifdef __GNUC__
return __builtin_expect(get_unsafe_ptr()->is_spam(), 0);
#else
return get_unsafe_ptr()->is_spam();
#endif
return UNLIKELY(get_unsafe_ptr()->is_spam());
}
#else
template<class GetCategory>
@ -92,11 +88,7 @@ template<class GetCategory>
INLINE bool NotifyCategoryProxy<GetCategory>::
is_debug() {
// Instruct the compiler to optimize for the usual case.
#ifdef __GNUC__
return __builtin_expect(get_unsafe_ptr()->is_debug(), 0);
#else
return get_unsafe_ptr()->is_debug();
#endif
return UNLIKELY(get_unsafe_ptr()->is_debug());
}
#else
template<class GetCategory>

View File

@ -3179,7 +3179,6 @@ if (PkgSkip("DIRECT")==0):
CopyAllHeaders('direct/src/distributed')
CopyAllHeaders('direct/src/interval')
CopyAllHeaders('direct/src/showbase')
CopyAllHeaders('direct/metalibs/direct')
CopyAllHeaders('direct/src/dcparse')
if (RUNTIME or RTDIST):
@ -5244,10 +5243,6 @@ if (PkgSkip("DIRECT")==0):
#
if (PkgSkip("DIRECT")==0):
OPTS=['DIR:direct/metalibs/direct', 'BUILDING:DIRECT']
TargetAdd('p3direct_direct.obj', opts=OPTS, input='direct.cxx')
TargetAdd('libp3direct.dll', input='p3direct_direct.obj')
TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj')
TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj')
if GetTarget() == 'darwin':
@ -5259,7 +5254,7 @@ if (PkgSkip("DIRECT")==0):
TargetAdd('libp3direct.dll', input=COMMON_PANDA_LIBS)
TargetAdd('libp3direct.dll', opts=['ADVAPI', 'OPENSSL', 'WINUSER', 'WINGDI'])
OPTS=['DIR:direct/metalibs/direct', 'PYTHON']
OPTS=['PYTHON']
TargetAdd('direct_module.obj', input='libp3dcparser.in')
TargetAdd('direct_module.obj', input='libp3showbase.in')
TargetAdd('direct_module.obj', input='libp3deadrec.in')

View File

@ -2620,6 +2620,8 @@ def SetupBuildEnvironment(compiler):
if os.path.isdir(pcbsd_inc):
SYS_INC_DIRS.append(pcbsd_inc)
null.close()
# Print out the search paths
if GetVerbose():
print("System library search path:")

View File

@ -19,6 +19,34 @@ ptr() const {
return (btStridingMeshInterface *)&_mesh;
}
/**
* Returns the number of vertices in this triangle mesh.
*/
INLINE size_t BulletTriangleMesh::
get_num_vertices() const {
return _vertices.size();
}
/**
* Returns the vertex at the given vertex index.
*/
INLINE LPoint3 BulletTriangleMesh::
get_vertex(size_t index) const {
nassertr(index < _vertices.size(), LPoint3::zero());
const btVector3 &vertex = _vertices[index];
return LPoint3(vertex[0], vertex[1], vertex[2]);
}
/**
* Returns the vertex indices making up the given triangle index.
*/
INLINE LVecBase3i BulletTriangleMesh::
get_triangle(size_t index) const {
index *= 3;
nassertr(index + 2 < _indices.size(), LVecBase3i::zero());
return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]);
}
/**
*
*/

View File

@ -39,7 +39,7 @@ BulletTriangleMesh()
/**
* Returns the number of triangles in this triangle mesh.
*/
int BulletTriangleMesh::
size_t BulletTriangleMesh::
get_num_triangles() const {
return _indices.size() / 3;
}

View File

@ -48,15 +48,24 @@ PUBLISHED:
void set_welding_distance(PN_stdfloat distance);
void preallocate(int num_verts, int num_indices);
int get_num_triangles() const;
size_t get_num_triangles() const;
PN_stdfloat get_welding_distance() const;
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level) const;
MAKE_PROPERTY(num_triangles, get_num_triangles);
public:
INLINE size_t get_num_vertices() const;
INLINE LPoint3 get_vertex(size_t index) const;
INLINE LVecBase3i get_triangle(size_t index) const;
PUBLISHED:
MAKE_PROPERTY(welding_distance, get_welding_distance, set_welding_distance);
MAKE_SEQ_PROPERTY(vertices, get_num_vertices, get_vertex);
MAKE_SEQ_PROPERTY(triangles, get_num_triangles, get_triangle);
public:
INLINE btStridingMeshInterface *ptr() const;

View File

@ -44,12 +44,13 @@ PUBLISHED:
AnimGroup *get_child(int n) const;
MAKE_SEQ(get_children, get_num_children, get_child);
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
AnimGroup *get_child_named(const string &name) const;
AnimGroup *find_child(const string &name) const;
void sort_descendants();
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
MAKE_MAP_PROPERTY(children, get_child_named, get_child_named);
public:
virtual TypeHandle get_value_type() const;

View File

@ -70,12 +70,14 @@ PUBLISHED:
int get_num_children() const;
PartGroup *get_child(int n) const;
MAKE_SEQ(get_children, get_num_children, get_child);
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
PartGroup *get_child_named(const string &name) const;
PartGroup *find_child(const string &name) const;
void sort_descendants();
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
MAKE_MAP_PROPERTY(children, get_child_named, get_child_named);
bool apply_freeze(const TransformState *transform);
virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale);
virtual bool apply_freeze_scalar(PN_stdfloat value);

View File

@ -12,7 +12,7 @@
*/
/**
* Create the Box by giving a Center and distances of of each of the sides of
* Create the Box by giving a Center and distances of each of the sides of
* box from the Center.
*/
INLINE CollisionBox::

View File

@ -1271,7 +1271,7 @@ set_window_sort(GraphicsOutput *window, int sort) {
* model begins with the "-" character.
*/
void GraphicsEngine::
cull_and_draw_together(const GraphicsEngine::Windows &wlist,
cull_and_draw_together(GraphicsEngine::Windows wlist,
Thread *current_thread) {
PStatTimer timer(_cull_pcollector, current_thread);
@ -1380,7 +1380,7 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
* drawing.
*/
void GraphicsEngine::
cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) {
PStatTimer timer(_cull_pcollector, current_thread);
_singular_warning_last_frame = _singular_warning_this_frame;

View File

@ -142,11 +142,11 @@ private:
void set_window_sort(GraphicsOutput *window, int sort);
void cull_and_draw_together(const Windows &wlist, Thread *current_thread);
void cull_and_draw_together(Windows wlist, Thread *current_thread);
void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
Thread *current_thread);
void cull_to_bins(const Windows &wlist, Thread *current_thread);
void cull_to_bins(Windows wlist, Thread *current_thread);
void cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg,
DisplayRegion *dr, SceneSetup *scene_setup,
CullResult *cull_result, Thread *current_thread);

View File

@ -227,14 +227,20 @@ clear_render_textures() {
* You can specify a bitplane to attach the texture to. the legal choices
* are:
*
* * RTP_depth * RTP_depth_stencil * RTP_color * RTP_aux_rgba_0 *
* RTP_aux_rgba_1 * RTP_aux_rgba_2 * RTP_aux_rgba_3
* - RTP_depth
* - RTP_depth_stencil
* - RTP_color
* - RTP_aux_rgba_0
* - RTP_aux_rgba_1
* - RTP_aux_rgba_2
* - RTP_aux_rgba_3
*
* If you do not specify a bitplane to attach the texture to, this routine
* will use a default based on the texture's format:
*
* * F_depth_component attaches to RTP_depth * F_depth_stencil attaches to
* RTP_depth_stencil * all other formats attach to RTP_color.
* - F_depth_component attaches to RTP_depth
* - F_depth_stencil attaches to RTP_depth_stencil
* - all other formats attach to RTP_color.
*
* The texture's format will be changed to match the format of the bitplane to
* which it is attached. For example, if you pass in an F_rgba texture and
@ -283,32 +289,41 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
// bitplane, while we're at it).
if (plane == RTP_depth) {
tex->set_format(Texture::F_depth_component);
_fb_properties.setup_depth_texture(tex);
tex->set_match_framebuffer_format(true);
} else if (plane == RTP_depth_stencil) {
tex->set_format(Texture::F_depth_stencil);
tex->set_component_type(Texture::T_unsigned_int_24_8);
if (_fb_properties.get_float_depth()) {
tex->set_component_type(Texture::T_float);
} else {
tex->set_component_type(Texture::T_unsigned_int_24_8);
}
tex->set_match_framebuffer_format(true);
} else if ((plane == RTP_color)||
(plane == RTP_aux_rgba_0)||
(plane == RTP_aux_rgba_1)||
(plane == RTP_aux_rgba_2)||
(plane == RTP_aux_rgba_3)) {
tex->set_format(Texture::F_rgba);
} else if (plane == RTP_color ||
plane == RTP_aux_rgba_0 ||
plane == RTP_aux_rgba_1 ||
plane == RTP_aux_rgba_2 ||
plane == RTP_aux_rgba_3) {
_fb_properties.setup_color_texture(tex);
tex->set_match_framebuffer_format(true);
} else if ((plane == RTP_aux_hrgba_0)||
(plane == RTP_aux_hrgba_1)||
(plane == RTP_aux_hrgba_2)||
(plane == RTP_aux_hrgba_3)) {
} else if (plane == RTP_aux_hrgba_0 ||
plane == RTP_aux_hrgba_1 ||
plane == RTP_aux_hrgba_2 ||
plane == RTP_aux_hrgba_3) {
tex->set_format(Texture::F_rgba16);
tex->set_match_framebuffer_format(true);
} else if ((plane == RTP_aux_float_0)||
(plane == RTP_aux_float_1)||
(plane == RTP_aux_float_2)||
(plane == RTP_aux_float_3)) {
} else if (plane == RTP_aux_float_0 ||
plane == RTP_aux_float_1 ||
plane == RTP_aux_float_2 ||
plane == RTP_aux_float_3) {
tex->set_format(Texture::F_rgba32);
tex->set_component_type(Texture::T_float);
tex->set_match_framebuffer_format(true);
} else {
display_cat.error() <<
"add_render_texture: invalid bitplane specified.\n";

View File

@ -281,6 +281,11 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
_gamma = 1.0f;
_texture_quality_override = Texture::QL_default;
// Give it a unique identifier. Unlike a pointer, we can guarantee that
// this value will never be reused.
static size_t next_index = 0;
_id = next_index++;
}
/**
@ -290,6 +295,19 @@ GraphicsStateGuardian::
~GraphicsStateGuardian() {
remove_gsg(this);
GeomMunger::unregister_mungers_for_gsg(this);
// Remove the munged states for this GSG. This requires going through all
// states, although destructing a GSG should be rare enough for this not to
// matter too much.
// Note that if uniquify-states is false, we can't iterate over all the
// states, and some GSGs will linger. Let's hope this isn't a problem.
LightReMutexHolder holder(*RenderState::_states_lock);
size_t size = RenderState::_states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
const RenderState *state = RenderState::_states->get_key(si);
state->_mungers.remove(_id);
state->_munged_states.remove(_id);
}
}
/**
@ -743,7 +761,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) {
// multiple times during a frame. Also, this might well be the only GSG
// in the world anyway.
int mi = state->_last_mi;
if (mi >= 0 && mungers.has_element(mi) && mungers.get_key(mi) == this) {
if (mi >= 0 && mi < mungers.get_num_entries() && mungers.get_key(mi) == _id) {
PT(GeomMunger) munger = mungers.get_data(mi);
if (munger->is_registered()) {
return munger;
@ -751,7 +769,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) {
}
// Nope, we have to look it up in the map.
mi = mungers.find(this);
mi = mungers.find(_id);
if (mi >= 0) {
PT(GeomMunger) munger = mungers.get_data(mi);
if (munger->is_registered()) {
@ -769,7 +787,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) {
nassertr(munger != (GeomMunger *)NULL && munger->is_registered(), munger);
nassertr(munger->is_of_type(StateMunger::get_class_type()), munger);
state->_last_mi = mungers.store(this, munger);
state->_last_mi = mungers.store(_id, munger);
return munger;
}
@ -3021,6 +3039,19 @@ determine_target_texture() {
nassertv(_target_texture->get_num_on_stages() <= max_texture_stages);
}
/**
* Assigns _target_shader based on the _target_rs.
*/
void GraphicsStateGuardian::
determine_target_shader() {
if (_target_rs->_generated_shader != nullptr) {
_target_shader = (const ShaderAttrib *)_target_rs->_generated_shader.p();
} else {
_target_shader = (const ShaderAttrib *)
_target_rs->get_attrib_def(ShaderAttrib::get_class_slot());
}
}
/**
* Frees some memory that was explicitly allocated within the glgsg.
*/
@ -3364,6 +3395,43 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
return tex;
}
/**
* Ensures that an appropriate shader has been generated for the given state.
* This is stored in the _generated_shader field on the RenderState.
*/
void GraphicsStateGuardian::
ensure_generated_shader(const RenderState *state) {
#ifdef HAVE_CG
const ShaderAttrib *shader_attrib;
state->get_attrib_def(shader_attrib);
if (shader_attrib->auto_shader()) {
if (_shader_generator == nullptr) {
if (!_supports_basic_shaders) {
return;
}
_shader_generator = new ShaderGenerator(this);
}
if (state->_generated_shader == nullptr ||
state->_generated_shader_seq != _generated_shader_seq) {
GeomVertexAnimationSpec spec;
// Currently we overload this flag to request vertex animation for the
// shader generator.
const ShaderAttrib *sattr;
state->get_attrib_def(sattr);
if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) {
spec.set_hardware(4, true);
}
// Cache the generated ShaderAttrib on the shader state.
state->_generated_shader = _shader_generator->synthesize_shader(state, spec);
state->_generated_shader_seq = _generated_shader_seq;
}
}
#endif
}
/**
* Returns true if the GSG implements the extension identified by the given
* string. This currently is only implemented by the OpenGL back-end.

View File

@ -426,6 +426,8 @@ public:
PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const;
PT(Texture) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host);
virtual void ensure_generated_shader(const RenderState *state);
#ifdef DO_PSTATS
static void init_frame_pstats();
#endif
@ -446,6 +448,7 @@ protected:
virtual void end_bind_clip_planes();
void determine_target_texture();
void determine_target_shader();
virtual void free_pointers();
virtual void close_gsg();

View File

@ -38,7 +38,17 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
_auto_shader(false),
_shader_skinning(false)
{
if (!get_gsg()->get_runtime_color_scale()) {
const ShaderAttrib *shader_attrib;
state->get_attrib_def(shader_attrib);
#ifdef HAVE_CG
_auto_shader = shader_attrib->auto_shader();
#endif
if (shader_attrib->get_flag(ShaderAttrib::F_hardware_skinning)) {
_shader_skinning = true;
}
if (!get_gsg()->get_runtime_color_scale() && !_auto_shader &&
shader_attrib->get_shader() == nullptr) {
// We might need to munge the colors.
const ColorAttrib *color_attrib;
const ColorScaleAttrib *color_scale_attrib;
@ -60,6 +70,7 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
_color[3] * cs[3]);
}
_munge_color = true;
_should_munge_state = true;
}
} else if (state->get_attrib(color_scale_attrib) &&
@ -74,6 +85,7 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
if ((color_scale_attrib->has_rgb_scale() && !get_gsg()->get_color_scale_via_lighting()) ||
(color_scale_attrib->has_alpha_scale() && !get_gsg()->get_alpha_scale_via_texture(tex_attrib))) {
_munge_color_scale = true;
_should_munge_state = true;
}
// Known bug: if there is a material on an object that would obscure the
@ -82,15 +94,6 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
// effort to detect this contrived situation and handle it correctly.
}
}
const ShaderAttrib *shader_attrib = (const ShaderAttrib *)
state->get_attrib_def(ShaderAttrib::get_class_slot());
if (shader_attrib->auto_shader()) {
_auto_shader = true;
}
if (shader_attrib->get_flag(ShaderAttrib::F_hardware_skinning)) {
_shader_skinning = true;
}
}
/**
@ -341,33 +344,5 @@ munge_state_impl(const RenderState *state) {
munged_state = munged_state->remove_attrib(ColorScaleAttrib::get_class_slot());
}
#ifdef HAVE_CG
if (_auto_shader) {
GraphicsStateGuardian *gsg = get_gsg();
ShaderGenerator *shader_generator = gsg->get_shader_generator();
if (shader_generator == nullptr) {
shader_generator = new ShaderGenerator(gsg);
gsg->set_shader_generator(shader_generator);
}
if (munged_state->_generated_shader == nullptr) {
// Cache the generated ShaderAttrib on the shader state.
GeomVertexAnimationSpec spec;
// Currently we overload this flag to request vertex animation for the
// shader generator.
const ShaderAttrib *sattr;
munged_state->get_attrib_def(sattr);
if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) {
spec.set_hardware(4, true);
}
munged_state->_generated_shader = shader_generator->synthesize_shader(munged_state, spec);
}
if (munged_state->_generated_shader != nullptr) {
munged_state = munged_state->set_attrib(munged_state->_generated_shader);
}
}
#endif
return munged_state;
}

View File

@ -3074,7 +3074,7 @@ set_state_and_transform(const RenderState *target,
}
_target_rs = target;
_target_shader = DCAST(ShaderAttrib, _target_rs->get_attrib_def(ShaderAttrib::get_class_slot()));
determine_target_shader();
int alpha_test_slot = AlphaTestAttrib::get_class_slot();
if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) ||
@ -4438,7 +4438,7 @@ set_texture_blend_mode(int i, const TextureStage *stage) {
set_texture_stage_state(i, D3DTSS_RESULTARG, D3DTA_CURRENT);
}
if (stage->uses_color()) {
if (stage->uses_color() || stage->involves_color_scale()) {
// Set up the constant color for this stage.
D3DCOLOR constant_color;

View File

@ -182,6 +182,46 @@ has_hook(const string &event_name) const {
}
/**
* Returns true if there is the hook added on the indicated event name and
* function pointer, false otherwise.
*/
bool EventHandler::
has_hook(const string &event_name, EventFunction *function) const {
assert(!event_name.empty());
Hooks::const_iterator hi;
hi = _hooks.find(event_name);
if (hi != _hooks.end()) {
const Functions& functions = (*hi).second;
if (functions.find(function) != functions.end()) {
return true;
}
}
return false;
}
/**
* Returns true if there is the hook added on the indicated event name,
* function pointer and callback data, false otherwise.
*/
bool EventHandler::
has_hook(const string &event_name, EventCallbackFunction *function, void *data) const {
assert(!event_name.empty());
CallbackHooks::const_iterator chi;
chi = _cbhooks.find(event_name);
if (chi != _cbhooks.end()) {
const CallbackFunctions& cbfunctions = (*chi).second;
if (cbfunctions.find(CallbackFunction(function, data)) != cbfunctions.end()) {
return true;
}
}
return false;
}
/**
* Removes the indicated function from the named event hook. Returns true if
* the hook was removed, false if it wasn't there in the first place.

View File

@ -55,6 +55,9 @@ public:
bool add_hook(const string &event_name, EventCallbackFunction *function,
void *data);
bool has_hook(const string &event_name) const;
bool has_hook(const string &event_name, EventFunction *function) const;
bool has_hook(const string &event_name, EventCallbackFunction *function,
void *data) const;
bool remove_hook(const string &event_name, EventFunction *function);
bool remove_hook(const string &event_name, EventCallbackFunction *function,
void *data);

View File

@ -105,9 +105,9 @@ PUBLISHED:
INLINE void set_element(size_type n, const Element &value);
EXTENSION(const Element &__getitem__(size_type n) const);
EXTENSION(void __setitem__(size_type n, const Element &value));
INLINE string get_data() const;
INLINE void set_data(const string &data);
INLINE string get_subdata(size_type n, size_type count) const;
EXTENSION(PyObject *get_data() const);
EXTENSION(void set_data(PyObject *data));
EXTENSION(PyObject *get_subdata(size_type n, size_type count) const);
INLINE void set_subdata(size_type n, size_type count, const string &data);
INLINE int get_ref_count() const;
INLINE int get_node_ref_count() const;
@ -255,14 +255,12 @@ PUBLISHED:
INLINE ConstPointerToArray(const PointerToArray<Element> &copy);
INLINE ConstPointerToArray(const ConstPointerToArray<Element> &copy);
EXTENSION(ConstPointerToArray(PyObject *self, PyObject *source));
typedef TYPENAME pvector<Element>::size_type size_type;
INLINE size_type size() const;
INLINE const Element &get_element(size_type n) const;
EXTENSION(const Element &__getitem__(size_type n) const);
INLINE string get_data() const;
INLINE string get_subdata(size_type n, size_type count) const;
EXTENSION(PyObject *get_data() const);
EXTENSION(PyObject *get_subdata(size_type n, size_type count) const);
INLINE int get_ref_count() const;
INLINE int get_node_ref_count() const;

View File

@ -79,93 +79,26 @@ INLINE void Extension<PointerToArray<Element> >::
__init__(PyObject *self, PyObject *source) {
#if PY_VERSION_HEX >= 0x02060000
if (PyObject_CheckBuffer(source)) {
// User passed a buffer object.
Py_buffer view;
if (PyObject_GetBuffer(source, &view, PyBUF_CONTIG_RO) == -1) {
PyErr_SetString(PyExc_TypeError,
"PointerToArray constructor requires a contiguous buffer");
return;
}
if (view.itemsize != 1 && view.itemsize != sizeof(Element)) {
PyErr_SetString(PyExc_TypeError,
"buffer.itemsize does not match PointerToArray element size");
return;
}
if (view.len % sizeof(Element) != 0) {
PyErr_Format(PyExc_ValueError,
"byte buffer is not a multiple of %zu bytes",
sizeof(Element));
return;
}
if (view.len > 0) {
this->_this->resize(view.len / sizeof(Element));
memcpy(this->_this->p(), view.buf, view.len);
}
PyBuffer_Release(&view);
#else
if (PyString_CheckExact(source)) {
#endif
// It's a byte sequence, or any object that exports the buffer protocol.
this->set_data(source);
return;
}
#endif
if (!PySequence_Check(source)) {
// Don't allow a unicode object even though it's a sequence.
if (!PySequence_Check(source) || PyUnicode_CheckExact(source)) {
// If passed with a non-sequence, this isn't the right constructor.
PyErr_SetString(PyExc_TypeError,
"PointerToArray constructor requires a sequence or buffer object");
return;
}
// If we were passed a Python string, then instead of storing it character-
// at-a-time, just load the whole string as a data buffer. Not sure if this
// case is still necessary - don't Python strbytes objects export the buffer
// protocol, as above?
#if PY_MAJOR_VERSION >= 3
if (PyBytes_Check(source)) {
int size = PyBytes_Size(source);
if (size % sizeof(Element) != 0) {
PyErr_Format(PyExc_ValueError,
"bytes object is not a multiple of %zu bytes",
sizeof(Element));
return;
}
int num_elements = size / sizeof(Element);
this->_this->insert(this->_this->begin(), num_elements, Element());
// Hope there aren't any constructors or destructors involved here.
if (size != 0) {
const char *data = PyBytes_AsString(source);
memcpy(this->_this->p(), data, size);
}
return;
}
#else
if (PyString_CheckExact(source)) {
int size = PyString_Size(source);
if (size % sizeof(Element) != 0) {
PyErr_Format(PyExc_ValueError,
"str object is not a multiple of %zu bytes",
sizeof(Element));
return;
}
int num_elements = size / sizeof(Element);
this->_this->insert(this->_this->begin(), num_elements, Element());
// Hope there aren't any constructors or destructors involved here.
if (size != 0) {
const char *data = PyString_AsString(source);
memcpy(this->_this->p(), data, size);
}
return;
}
#endif
// Now construct the internal list by copying the elements one-at-a-time
// from Python.
PyObject *push_back = PyObject_GetAttrString(self, "push_back");
PyObject *dict = ((Dtool_PyInstDef *)self)->_My_Type->_PyType.tp_dict;
PyObject *push_back = PyDict_GetItemString(dict, "push_back");
if (push_back == NULL) {
PyErr_BadArgument();
return;
@ -174,19 +107,20 @@ __init__(PyObject *self, PyObject *source) {
// We need to initialize the this pointer before we can call push_back.
((Dtool_PyInstDef *)self)->_ptr_to_object = (void *)this->_this;
int size = PySequence_Size(source);
for (int i = 0; i < size; ++i) {
Py_ssize_t size = PySequence_Size(source);
this->_this->reserve(size);
for (Py_ssize_t i = 0; i < size; ++i) {
PyObject *item = PySequence_GetItem(source, i);
if (item == NULL) {
return;
}
PyObject *result = PyObject_CallFunctionObjArgs(push_back, item, NULL);
PyObject *result = PyObject_CallFunctionObjArgs(push_back, self, item, NULL);
Py_DECREF(item);
if (result == NULL) {
// Unable to add item--probably it wasn't of the appropriate type.
PyErr_Print();
PyErr_Format(PyExc_TypeError,
"Element %d in sequence passed to PointerToArray "
"Element %zd in sequence passed to PointerToArray "
"constructor could not be added", i);
return;
}
@ -213,15 +147,110 @@ __setitem__(size_t n, const Element &value) {
}
/**
* This special constructor accepts a Python list of elements, or a Python
* string (or a bytes object, in Python 3).
* This returns the entire contents of the vector as a block of raw data in a
* string (or bytes object, in Python 3).
*
* @deprecated use memoryview(pta) or bytearray(pta) instead.
*/
template<class Element>
INLINE void Extension<ConstPointerToArray<Element> >::
__init__(PyObject *self, PyObject *source) {
PointerToArray<Element> array;
invoke_extension(&array).__init__(self, source);
*(this->_this) = MOVE(array);
INLINE PyObject *Extension<PointerToArray<Element> >::
get_data() const {
#if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
#else
return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
#endif
}
/**
* This method exists mainly to access the data of the array easily from a
* high-level language such as Python.
*
* This replaces the entire contents of the vector from a block of raw data
* in a string (or bytes object, in Python 3).
*/
template<class Element>
INLINE void Extension<PointerToArray<Element> >::
set_data(PyObject *data) {
#if PY_VERSION_HEX >= 0x02060000
if (PyObject_CheckBuffer(data)) {
// User passed a buffer object.
Py_buffer view;
if (PyObject_GetBuffer(data, &view, PyBUF_CONTIG_RO) == -1) {
PyErr_SetString(PyExc_TypeError,
"PointerToArray.set_data() requires a contiguous buffer");
return;
}
if (view.itemsize != 1 && view.itemsize != sizeof(Element)) {
PyErr_SetString(PyExc_TypeError,
"buffer.itemsize does not match PointerToArray element size");
return;
}
if (view.len % sizeof(Element) != 0) {
PyErr_Format(PyExc_ValueError,
"byte buffer is not a multiple of %zu bytes",
sizeof(Element));
return;
}
if (view.len > 0) {
this->_this->resize(view.len / sizeof(Element));
memcpy(this->_this->p(), view.buf, view.len);
} else {
this->_this->clear();
}
PyBuffer_Release(&view);
} else {
Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object");
}
#else
// In Python 2.5 we didn't have the new buffer protocol, only str.
if (PyString_CheckExact(data)) {
int size = PyString_Size(data);
if (size % sizeof(Element) != 0) {
PyErr_Format(PyExc_ValueError,
"str object is not a multiple of %zu bytes",
sizeof(Element));
return;
}
int num_elements = size / sizeof(Element);
this->_this->insert(this->_this->begin(), num_elements, Element());
// Hope there aren't any constructors or destructors involved here.
if (size != 0) {
const char *ptr = PyString_AsString(data);
memcpy(this->_this->p(), ptr, size);
} else {
this->_this->clear();
}
} else {
Dtool_Raise_TypeError("PointerToArray.set_data() requires a str");
}
#endif
}
/**
* This returns the contents of a portion of the vector--from element (n)
* through element (n + count - 1)--as a block of raw data in a string (or
* bytes object, in Python 3).
*
* @deprecated use memoryview(pta) or bytearray(pta) instead.
*/
template<class Element>
INLINE PyObject *Extension<PointerToArray<Element> >::
get_subdata(size_t n, size_t count) const {
n = min(n, this->_this->size());
count = max(count, n);
count = min(count, this->_this->size() - n);
#if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
#else
return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
#endif
}
/**
@ -233,6 +262,42 @@ __getitem__(size_t n) const {
return (*this->_this)[n];
}
/**
* This returns the entire contents of the vector as a block of raw data in a
* string (or bytes object, in Python 3).
*
* @deprecated use memoryview(pta) or bytearray(pta) instead.
*/
template<class Element>
INLINE PyObject *Extension<ConstPointerToArray<Element> >::
get_data() const {
#if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
#else
return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
#endif
}
/**
* This returns the contents of a portion of the vector--from element (n)
* through element (n + count - 1)--as a block of raw data in a string (or
* bytes object, in Python 3).
*
* @deprecated use memoryview(pta) or bytearray(pta) instead.
*/
template<class Element>
INLINE PyObject *Extension<ConstPointerToArray<Element> >::
get_subdata(size_t n, size_t count) const {
n = min(n, this->_this->size());
count = max(count, n);
count = min(count, this->_this->size() - n);
#if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
#else
return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
#endif
}
/**
* This is used to implement the buffer protocol, in order to allow efficient
* access to the array data through a Python multiview object.
@ -461,6 +526,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
#endif
}
/**
* Specialization on __getbuffer__ for LMatrix3f.
*/
template<>
INLINE int Extension<ConstPointerToArray<LMatrix3f> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
@ -488,6 +556,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
#endif
}
/**
* Specialization on __getbuffer__ for LMatrix3d.
*/
template<>
INLINE int Extension<ConstPointerToArray<LMatrix3d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
@ -515,6 +586,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
#endif
}
/**
* Specialization on __getbuffer__ for UnalignedLMatrix4f.
*/
template<>
INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4f> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
@ -542,6 +616,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
#endif
}
/**
* Specialization on __getbuffer__ for UnalignedLMatrix4d.
*/
template<>
INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {

View File

@ -36,18 +36,29 @@ public:
INLINE const Element &__getitem__(size_t n) const;
INLINE void __setitem__(size_t n, const Element &value);
INLINE PyObject *get_data() const;
INLINE void set_data(PyObject *data);
INLINE PyObject *get_subdata(size_t n, size_t count) const;
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
};
template<>
INLINE int Extension<PointerToArray<LMatrix3f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
INLINE int Extension<PointerToArray<LMatrix3f> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags);
template<>
INLINE int Extension<PointerToArray<LMatrix3d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
INLINE int Extension<PointerToArray<LMatrix3d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags);
template<>
INLINE int Extension<PointerToArray<UnalignedLMatrix4f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
INLINE int Extension<PointerToArray<UnalignedLMatrix4f> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags);
template<>
INLINE int Extension<PointerToArray<UnalignedLMatrix4d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
INLINE int Extension<PointerToArray<UnalignedLMatrix4d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags);
/**
* This class defines the extension methods for ConstPointerToArray, which are
@ -59,22 +70,30 @@ template<>
template<class Element>
class Extension<ConstPointerToArray<Element> > : public ExtensionBase<ConstPointerToArray<Element> > {
public:
INLINE void __init__(PyObject *self, PyObject *source);
INLINE const Element &__getitem__(size_t n) const;
INLINE PyObject *get_data() const;
INLINE PyObject *get_subdata(size_t n, size_t count) const;
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
};
template<>
INLINE int Extension<ConstPointerToArray<LMatrix3f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
INLINE int Extension<ConstPointerToArray<LMatrix3f> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
template<>
INLINE int Extension<ConstPointerToArray<LMatrix3d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
INLINE int Extension<ConstPointerToArray<LMatrix3d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
template<>
INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4f> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
template<>
INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
#ifdef _MSC_VER
// Ugh... MSVC needs this because they still don't have a decent linker.

View File

@ -174,7 +174,7 @@ static const string default_vshader =
"void main(void) {\n"
" gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n"
" texcoord = p3d_MultiTexCoord0;\n"
" color = p3d_Color;\n"
" color = p3d_Color * p3d_ColorScale;\n"
"}\n";
static const string default_fshader =
@ -3127,6 +3127,10 @@ reset() {
}
#endif
// Do we guarantee that we can apply the color scale via a shader? We set
// this false if there is a chance that the fixed-function pipeline is used.
_runtime_color_scale = !has_fixed_function_pipeline();
#ifndef OPENGLES
if (_gl_shadlang_ver_major >= 4 || has_extension("GL_NV_gpu_program5")) {
// gp5fp - OpenGL fragment profile for GeForce 400 Series and up
@ -10341,8 +10345,7 @@ set_state_and_transform(const RenderState *target,
_target_rs = target;
#ifndef OPENGLES_1
_target_shader = (const ShaderAttrib *)
_target_rs->get_attrib_def(ShaderAttrib::get_class_slot());
determine_target_shader();
_instance_count = _target_shader->get_instance_count();
if (_target_shader != _state_shader) {

View File

@ -95,6 +95,27 @@ reset_data() {
#endif
}
/**
* Returns an implementation-defined handle or pointer that can be used
* to interface directly with the underlying API.
* Returns 0 if the underlying implementation does not support this.
*/
uint64_t CLP(TextureContext)::
get_native_id() const {
return _index;
}
/**
* Similar to get_native_id, but some implementations use a separate
* identifier for the buffer object associated with buffer textures.
* Returns 0 if the underlying implementation does not support this, or
* if this is not a buffer texture.
*/
uint64_t CLP(TextureContext)::
get_native_buffer_id() const {
return _buffer;
}
/**
*
*/

View File

@ -33,6 +33,9 @@ public:
virtual void evict_lru();
void reset_data();
virtual uint64_t get_native_id() const;
virtual uint64_t get_native_buffer_id() const;
#ifndef OPENGLES
void make_handle_resident();
GLuint64 get_handle();

View File

@ -125,6 +125,9 @@ PUBLISHED:
MAKE_SEQ_PROPERTY(points, get_num_points, get_point);
MAKE_SEQ_PROPERTY(vectors, get_num_vectors, get_vector);
// We also define this as a mapping interface, for lookups by name.
MAKE_MAP_PROPERTY(columns, has_column, get_column);
void output(ostream &out) const;
void write(ostream &out, int indent_level = 0) const;
void write_with_data(ostream &out, int indent_level,

View File

@ -26,7 +26,7 @@ make(PyUnicodeObject *str) {
if (!PyUnicode_CHECK_INTERNED(str)) {
// Not an interned string; don't bother.
Py_ssize_t len = 0;
char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
const char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
if (c_str == NULL) {
return NULL;
}
@ -43,7 +43,7 @@ make(PyUnicodeObject *str) {
} else {
Py_ssize_t len = 0;
char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
const char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
string name(c_str, len);
#else

View File

@ -539,6 +539,8 @@ PUBLISHED:
void set_aux_data(const string &key, TypedReferenceCount *aux_data);
void clear_aux_data(const string &key);
TypedReferenceCount *get_aux_data(const string &key) const;
MAKE_MAP_PROPERTY(aux_data, get_aux_data, get_aux_data,
set_aux_data, clear_aux_data);
INLINE static void set_textures_power_2(AutoTextureScale scale);
INLINE static AutoTextureScale get_textures_power_2();

View File

@ -15,6 +15,27 @@
TypeHandle TextureContext::_type_handle;
/**
* Returns an implementation-defined handle or pointer that can be used
* to interface directly with the underlying API.
* Returns 0 if the underlying implementation does not support this.
*/
uint64_t TextureContext::
get_native_id() const {
return 0;
}
/**
* Similar to get_native_id, but some implementations use a separate
* identifier for the buffer object associated with buffer textures.
* Returns 0 if the underlying implementation does not support this, or
* if this is not a buffer texture.
*/
uint64_t TextureContext::
get_native_buffer_id() const {
return 0;
}
/**
*
*/

View File

@ -37,6 +37,8 @@ public:
PUBLISHED:
INLINE Texture *get_texture() const;
INLINE int get_view() const;
virtual uint64_t get_native_id() const;
virtual uint64_t get_native_buffer_id() const;
INLINE bool was_modified() const;
INLINE bool was_properties_modified() const;

View File

@ -15,7 +15,7 @@
* Initialize the texture stage from other
*/
INLINE TextureStage::
TextureStage(TextureStage &copy) {
TextureStage(const TextureStage &copy) {
(*this) = copy;
}
@ -52,6 +52,10 @@ set_sort(int sort) {
// Update the global flag to indicate that all TextureAttribs in the world
// must now re-sort their lists.
_sort_seq++;
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
/**
@ -80,6 +84,10 @@ set_priority(int priority) {
// Update the global flag to indicate that all TextureAttribs in the world
// must now re-sort their lists.
_sort_seq++;
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
/**
@ -99,7 +107,13 @@ get_priority() const {
*/
INLINE void TextureStage::
set_texcoord_name(InternalName *name) {
_texcoord_name = name;
if (name != _texcoord_name) {
_texcoord_name = name;
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
}
/**
@ -108,7 +122,7 @@ set_texcoord_name(InternalName *name) {
*/
INLINE void TextureStage::
set_texcoord_name(const string &name) {
_texcoord_name = InternalName::get_texcoord_name(name);
set_texcoord_name(InternalName::get_texcoord_name(name));
}
/**
@ -150,13 +164,16 @@ get_binormal_name() const {
*/
INLINE void TextureStage::
set_mode(TextureStage::Mode mode) {
_mode = mode;
if (mode != _mode) {
_mode = mode;
if (_mode != M_combine) {
_num_combine_rgb_operands = 0;
_num_combine_alpha_operands = 0;
if (_mode != M_combine) {
_num_combine_rgb_operands = 0;
_num_combine_alpha_operands = 0;
}
update_color_flags();
}
update_color_flags();
}
/**
@ -202,8 +219,14 @@ get_color() const {
*/
INLINE void TextureStage::
set_rgb_scale(int rgb_scale) {
nassertv(rgb_scale == 1 || rgb_scale == 2 || rgb_scale == 4);
_rgb_scale = rgb_scale;
if (rgb_scale != _rgb_scale) {
nassertv(rgb_scale == 1 || rgb_scale == 2 || rgb_scale == 4);
_rgb_scale = rgb_scale;
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
}
/**
@ -222,8 +245,14 @@ get_rgb_scale() const {
*/
INLINE void TextureStage::
set_alpha_scale(int alpha_scale) {
nassertv(alpha_scale == 1 || alpha_scale == 2 || alpha_scale == 4);
_alpha_scale = alpha_scale;
if (alpha_scale != _alpha_scale) {
nassertv(alpha_scale == 1 || alpha_scale == 2 || alpha_scale == 4);
_alpha_scale = alpha_scale;
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
}
/**
@ -247,7 +276,13 @@ get_alpha_scale() const {
*/
INLINE void TextureStage::
set_saved_result(bool saved_result) {
_saved_result = saved_result;
if (saved_result != _saved_result) {
_saved_result = saved_result;
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
}
/**
@ -641,6 +676,14 @@ get_sort_seq() {
return _sort_seq;
}
/**
* Marks this TextureStage as having been used by the auto shader.
*/
INLINE void TextureStage::
mark_used_by_auto_shader() const {
_used_by_auto_shader = true;
}
/**
* Updates _uses_color, _involves_color_scale, _uses_primary_color and
* _uses_last_saved_result appropriately.
@ -658,8 +701,7 @@ update_color_flags() {
_combine_alpha_source2 == CS_constant_color_scale)));
_uses_color =
(_involves_color_scale ||
_mode == M_blend ||
(_mode == M_blend ||
(_mode == M_combine &&
(_combine_rgb_source0 == CS_constant ||
_combine_rgb_source1 == CS_constant ||
@ -685,6 +727,10 @@ update_color_flags() {
_combine_alpha_source0 == CS_last_saved_result ||
_combine_alpha_source1 == CS_last_saved_result ||
_combine_alpha_source2 == CS_last_saved_result));
if (_used_by_auto_shader) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
INLINE ostream &

View File

@ -25,7 +25,7 @@ TypeHandle TextureStage::_type_handle;
* Initialize the texture stage at construction
*/
TextureStage::
TextureStage(const string &name) {
TextureStage(const string &name) : _used_by_auto_shader(false) {
_name = name;
_sort = 0;
_priority = 0;
@ -90,6 +90,8 @@ operator = (const TextureStage &other) {
_uses_color = other._uses_color;
_involves_color_scale = other._involves_color_scale;
_used_by_auto_shader = false;
}
/**

View File

@ -21,6 +21,7 @@
#include "typedWritableReferenceCount.h"
#include "updateSeq.h"
#include "luse.h"
#include "graphicsStateGuardianBase.h"
class FactoryParams;
@ -34,7 +35,7 @@ class FactoryParams;
class EXPCL_PANDA_GOBJ TextureStage : public TypedWritableReferenceCount {
PUBLISHED:
explicit TextureStage(const string &name);
INLINE TextureStage(TextureStage &copy);
INLINE TextureStage(const TextureStage &copy);
void operator = (const TextureStage &copy);
virtual ~TextureStage();
@ -206,6 +207,8 @@ PUBLISHED:
public:
INLINE static UpdateSeq get_sort_seq();
INLINE void mark_used_by_auto_shader() const;
private:
INLINE void update_color_flags();
@ -249,6 +252,8 @@ private:
static PT(TextureStage) _default_stage;
static UpdateSeq _sort_seq;
mutable bool _used_by_auto_shader;
public:
// Datagram stuff
static void register_with_read_factory();

View File

@ -142,6 +142,17 @@ get_weight(size_t n) const {
return _entries[n]._weight;
}
/**
* Removes the nth transform stored in the blend object.
*/
INLINE void TransformBlend::
remove_transform(size_t n) {
nassertv(n < _entries.size());
_entries.erase(_entries.begin() + n);
Thread *current_thread = Thread::get_current_thread();
clear_result(current_thread);
}
/**
* Replaces the nth transform stored in the blend object.
*/

View File

@ -62,9 +62,15 @@ PUBLISHED:
INLINE const VertexTransform *get_transform(size_t n) const;
MAKE_SEQ(get_transforms, get_num_transforms, get_transform);
INLINE PN_stdfloat get_weight(size_t n) const;
INLINE void remove_transform(size_t n);
INLINE void set_transform(size_t n, const VertexTransform *transform);
INLINE void set_weight(size_t n, PN_stdfloat weight);
MAKE_SEQ_PROPERTY(transforms, get_num_transforms, get_transform,
set_transform, remove_transform);
MAKE_SEQ_PROPERTY(weights, get_num_transforms, get_weight, set_weight);
MAKE_MAP_PROPERTY(weights, has_transform, get_weight);
INLINE void update_blend(Thread *current_thread) const;
INLINE void get_blend(LMatrix4 &result, Thread *current_thread) const;

View File

@ -16,6 +16,7 @@
#include <algorithm>
AtomicAdjust::Pointer GraphicsStateGuardianBase::_gsg_list;
UpdateSeq GraphicsStateGuardianBase::_generated_shader_seq;
TypeHandle GraphicsStateGuardianBase::_type_handle;
/**

View File

@ -223,6 +223,14 @@ public:
virtual void bind_light(Spotlight *light_obj, const NodePath &light,
int light_id) { }
virtual void ensure_generated_shader(const RenderState *state)=0;
static void mark_rehash_generated_shaders() {
#ifdef HAVE_CG
++_generated_shader_seq;
#endif
}
PUBLISHED:
static GraphicsStateGuardianBase *get_default_gsg();
static void set_default_gsg(GraphicsStateGuardianBase *default_gsg);
@ -235,6 +243,8 @@ public:
static void add_gsg(GraphicsStateGuardianBase *gsg);
static void remove_gsg(GraphicsStateGuardianBase *gsg);
size_t _id;
private:
struct GSGList {
LightMutex _lock;
@ -245,6 +255,9 @@ private:
};
static AtomicAdjust::Pointer _gsg_list;
protected:
static UpdateSeq _generated_shader_seq;
public:
static TypeHandle get_class_type() {
return _type_handle;

View File

@ -903,6 +903,14 @@ FLOATNAME(UnalignedLVecBase4)(const FLOATNAME(LVecBase4) &copy) {
set(copy[0], copy[1], copy[2], copy[3]);
}
/**
*
*/
INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)::
FLOATNAME(UnalignedLVecBase4)(FLOATTYPE fill_value) {
fill(fill_value);
}
/**
*
*/
@ -912,6 +920,19 @@ FLOATNAME(UnalignedLVecBase4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w
set(x, y, z, w);
}
/**
* Sets each element of the vector to the indicated fill_value. This is
* particularly useful for initializing to zero.
*/
INLINE_LINMATH void FLOATNAME(UnalignedLVecBase4)::
fill(FLOATTYPE fill_value) {
TAU_PROFILE("void UnalignedLVecBase4::fill()", " ", TAU_USER);
_v(0) = fill_value;
_v(1) = fill_value;
_v(2) = fill_value;
_v(3) = fill_value;
}
/**
*
*/

View File

@ -230,8 +230,10 @@ PUBLISHED:
INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)() DEFAULT_CTOR;
INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(const FLOATNAME(LVecBase4) &copy);
INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(FLOATTYPE fill_value);
INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w);
INLINE_LINMATH void fill(FLOATTYPE fill_value);
INLINE_LINMATH void set(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w);
INLINE_LINMATH FLOATTYPE operator [](int i) const;

View File

@ -91,12 +91,16 @@ PUBLISHED:
void clear_tag_states();
bool has_tag_state(const string &tag_state) const;
CPT(RenderState) get_tag_state(const string &tag_state) const;
MAKE_MAP_PROPERTY(tag_states, has_tag_state, get_tag_state,
set_tag_state, clear_tag_state);
void set_aux_scene_data(const NodePath &node_path, AuxSceneData *data);
bool clear_aux_scene_data(const NodePath &node_path);
AuxSceneData *get_aux_scene_data(const NodePath &node_path) const;
void list_aux_scene_data(ostream &out) const;
int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread());
MAKE_MAP_PROPERTY(aux_scene_data, get_aux_scene_data, get_aux_scene_data,
set_aux_scene_data, clear_aux_scene_data);
private:
void add_display_region(DisplayRegion *display_region);

View File

@ -213,7 +213,7 @@ make_default() {
/**
* Returns the basic operation type of the ClipPlaneAttrib. If this is O_set,
* the planes listed here completely replace any planes that were already on.
* If this is O_add, the planes here are added to the set of of planes that
* If this is O_add, the planes here are added to the set of planes that
* were already on, and if O_remove, the planes here are removed from the set
* of planes that were on.
*

View File

@ -227,7 +227,7 @@ ConfigVariableBool uniquify_states
"are pointerwise equal. This may improve caching performance, "
"but also adds additional overhead to maintain the cache, "
"including the need to check for a composition cycle in "
"the cache."));
"the cache. It is highly recommended to keep this on."));
ConfigVariableBool uniquify_attribs
("uniquify-attribs", true,

View File

@ -142,10 +142,15 @@ munge_geom(GraphicsStateGuardianBase *gsg, GeomMunger *munger,
DCAST(ShaderAttrib, ShaderAttrib::make())->set_flag(ShaderAttrib::F_hardware_skinning, true));
_state = _state->compose(state);
}
}
StateMunger *state_munger = (StateMunger *)munger;
_state = state_munger->munge_state(_state);
gsg->ensure_generated_shader(_state);
} else {
// We may need to munge the state for the fixed-function pipeline.
StateMunger *state_munger = (StateMunger *)munger;
if (state_munger->should_munge_state()) {
_state = state_munger->munge_state(_state);
}
}
// If there is any animation left in the vertex data after it has been
// munged--that is, we couldn't arrange to handle the animation in

View File

@ -257,7 +257,7 @@ make_default() {
/**
* Returns the basic operation type of the LightAttrib. If this is O_set, the
* lights listed here completely replace any lights that were already on. If
* this is O_add, the lights here are added to the set of of lights that were
* this is O_add, the lights here are added to the set of lights that were
* already on, and if O_remove, the lights here are removed from the set of
* lights that were on.
*
@ -833,6 +833,14 @@ compose_impl(const RenderAttrib *other) const {
++result;
}
// Increase the attrib_ref of all the lights in this new attribute.
Lights::const_iterator it;
for (it = new_attrib->_on_lights.begin(); it != new_attrib->_on_lights.end(); ++it) {
Light *lobj = (*it).node()->as_light();
nassertd(lobj != nullptr) continue;
lobj->attrib_ref();
}
return return_new(new_attrib);
}

View File

@ -630,9 +630,12 @@ PUBLISHED:
void clear_shader();
void set_shader_input(ShaderInput input);
INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
public:
INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0);
@ -654,11 +657,13 @@ PUBLISHED:
INLINE void set_shader_input(CPT_InternalName id, const LVecBase4i &v, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, const LVecBase3i &v, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, const LVecBase2i &v, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, int n1, int n2=0, int n3=0,
PUBLISHED:
INLINE void set_shader_input(CPT_InternalName id, int n1, int n2, int n3=0,
int n4=0, int priority=0);
INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2=0,
INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2,
PN_stdfloat n3=0, PN_stdfloat n4=0, int priority=0);
EXTENSION(void set_shader_input(CPT_InternalName, PyObject *, int priority=0));
EXTENSION(void set_shader_inputs(PyObject *args, PyObject *kwargs));
void clear_shader_input(CPT_InternalName id);
@ -908,6 +913,9 @@ PUBLISHED:
INLINE bool has_net_tag(const string &key) const;
NodePath find_net_tag(const string &key) const;
MAKE_MAP_PROPERTY(tags, has_tag, get_tag, set_tag, clear_tag);
MAKE_MAP_PROPERTY(net_tags, has_net_tag, get_net_tag);
EXTENSION(INLINE PyObject *get_tag_keys() const);
EXTENSION(PyObject *get_python_tags());

View File

@ -13,6 +13,7 @@
#include "nodePath_ext.h"
#include "typedWritable_ext.h"
#include "shaderInput_ext.h"
#include "shaderAttrib.h"
#ifdef HAVE_PYTHON
@ -25,35 +26,7 @@ extern struct Dtool_PyTypedObject Dtool_LPoint3d;
#else
extern struct Dtool_PyTypedObject Dtool_LPoint3f;
#endif
extern struct Dtool_PyTypedObject Dtool_Texture;
extern struct Dtool_PyTypedObject Dtool_NodePath;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_float;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_double;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_int;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4i;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3i;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2i;
extern struct Dtool_PyTypedObject Dtool_LVecBase4f;
extern struct Dtool_PyTypedObject Dtool_LVecBase3f;
extern struct Dtool_PyTypedObject Dtool_LVecBase2f;
extern struct Dtool_PyTypedObject Dtool_LVecBase4d;
extern struct Dtool_PyTypedObject Dtool_LVecBase3d;
extern struct Dtool_PyTypedObject Dtool_LVecBase2d;
extern struct Dtool_PyTypedObject Dtool_LVecBase4i;
extern struct Dtool_PyTypedObject Dtool_LVecBase3i;
extern struct Dtool_PyTypedObject Dtool_LVecBase2i;
extern struct Dtool_PyTypedObject Dtool_ShaderBuffer;
extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
#endif // CPPPARSER
/**
@ -241,6 +214,28 @@ py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &da
return NodePath::decode_from_bam_stream(data, reader);
}
/**
* Sets a single shader input.
*/
void Extension<NodePath>::
set_shader_input(CPT_InternalName name, PyObject *value, int priority) {
PT(PandaNode) node = _this->node();
CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot());
PT(ShaderAttrib) attrib;
if (prev_attrib == nullptr) {
attrib = new ShaderAttrib();
} else {
attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p());
}
ShaderInput &input = attrib->_inputs[name];
invoke_extension(&input).__init__(move(name), value);
if (!_PyErr_OCCURRED()) {
node->set_attrib(ShaderAttrib::return_new(attrib));
}
}
/**
* Sets multiple shader inputs at the same time. This can be significantly
* more efficient if many inputs need to be set at the same time.
@ -278,153 +273,13 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
}
CPT_InternalName name(string(buffer, length));
ShaderInput input(nullptr, 0);
if (PyTuple_CheckExact(value)) {
// A tuple is interpreted as a vector.
Py_ssize_t size = PyTuple_GET_SIZE(value);
if (size > 4) {
Dtool_Raise_TypeError("NodePath.set_shader_inputs tuple input should not have more than 4 scalars");
return;
}
// If any of them is a float, we are storing it as a float vector.
bool is_float = false;
for (Py_ssize_t i = 0; i < size; ++i) {
if (PyFloat_CheckExact(PyTuple_GET_ITEM(value, i))) {
is_float = true;
break;
}
}
if (is_float) {
LVecBase4 vec(0);
for (Py_ssize_t i = 0; i < size; ++i) {
vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i));
}
input = ShaderInput(move(name), vec);
} else {
LVecBase4i vec(0);
for (Py_ssize_t i = 0; i < size; ++i) {
vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i));
}
input = ShaderInput(move(name), vec);
}
} else if (DtoolCanThisBeAPandaInstance(value)) {
Dtool_PyInstDef *inst = (Dtool_PyInstDef *)value;
void *ptr;
if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) {
input = ShaderInput(move(name), (Texture *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) {
input = ShaderInput(move(name), *(const NodePath *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) {
input = ShaderInput(move(name), *(const PTA_float *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) {
input = ShaderInput(move(name), *(const PTA_double *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) {
input = ShaderInput(move(name), *(const PTA_int *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) {
input = ShaderInput(move(name), *(const PTA_LVecBase4f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) {
input = ShaderInput(move(name), *(const PTA_LVecBase3f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) {
input = ShaderInput(move(name), *(const PTA_LVecBase2f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) {
input = ShaderInput(move(name), *(const PTA_LMatrix4f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) {
input = ShaderInput(move(name), *(const PTA_LMatrix3f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) {
input = ShaderInput(move(name), *(const PTA_LVecBase4d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) {
input = ShaderInput(move(name), *(const PTA_LVecBase3d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) {
input = ShaderInput(move(name), *(const PTA_LVecBase2d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) {
input = ShaderInput(move(name), *(const PTA_LMatrix4d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) {
input = ShaderInput(move(name), *(const PTA_LMatrix3d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) {
input = ShaderInput(move(name), *(const PTA_LVecBase4i *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) {
input = ShaderInput(move(name), *(const PTA_LVecBase3i *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) {
input = ShaderInput(move(name), *(const PTA_LVecBase2i *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) {
input = ShaderInput(move(name), *(const LVecBase4f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) {
input = ShaderInput(move(name), *(const LVecBase3f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) {
input = ShaderInput(move(name), *(const LVecBase2f *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) {
input = ShaderInput(move(name), *(const LVecBase4d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) {
input = ShaderInput(move(name), *(const LVecBase3d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) {
input = ShaderInput(move(name), *(const LVecBase2d *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) {
input = ShaderInput(move(name), *(const LVecBase4i *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) {
input = ShaderInput(move(name), *(const LVecBase3i *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) {
input = ShaderInput(move(name), *(const LVecBase2i *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) {
input = ShaderInput(move(name), (ShaderBuffer *)ptr);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) {
input = ShaderInput(move(name), (ParamValueBase *)ptr);
} else {
Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
return;
}
} else if (PyFloat_Check(value)) {
input = ShaderInput(move(name), LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0));
#if PY_MAJOR_VERSION < 3
} else if (PyInt_Check(value)) {
input = ShaderInput(move(name), LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0));
#endif
} else if (PyLong_Check(value)) {
input = ShaderInput(move(name), LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0));
} else {
Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
return;
}
attrib->_inputs[input.get_name()] = move(input);
ShaderInput &input = attrib->_inputs[name];
invoke_extension(&input).__init__(move(name), value);
}
node->set_attrib(ShaderAttrib::return_new(attrib));
if (!_PyErr_OCCURRED()) {
node->set_attrib(ShaderAttrib::return_new(attrib));
}
}
/**

View File

@ -49,6 +49,7 @@ public:
// This is defined to implement cycle detection in Python tags.
INLINE int __traverse__(visitproc visit, void *arg);
void set_shader_input(CPT_InternalName id, PyObject *value, int priority=0);
void set_shader_inputs(PyObject *args, PyObject *kwargs);
PyObject *get_tight_bounds(const NodePath &other = NodePath()) const;

View File

@ -2,4 +2,6 @@
#include "nodePathCollection_ext.cxx"
#include "pandaNode_ext.cxx"
#include "renderState_ext.cxx"
#include "shaderAttrib_ext.cxx"
#include "shaderInput_ext.cxx"
#include "transformState_ext.cxx"

View File

@ -389,7 +389,7 @@ return_unique(RenderAttrib *attrib) {
// deleted while it's in it.
attrib->ref();
}
si = _attribs->store(attrib, Empty());
si = _attribs->store(attrib, nullptr);
// Save the index and return the input attrib.
attrib->_saved_entry = si;

View File

@ -185,9 +185,7 @@ public:
private:
// This mutex protects _attribs.
static LightReMutex *_attribs_lock;
class Empty {
};
typedef SimpleHashMap<const RenderAttrib *, Empty, indirect_compare_to_hash<const RenderAttrib *> > Attribs;
typedef SimpleHashMap<const RenderAttrib *, nullptr_t, indirect_compare_to_hash<const RenderAttrib *> > Attribs;
static Attribs *_attribs;
int _saved_entry;

View File

@ -93,7 +93,7 @@ register_slot(TypeHandle type_handle, int sort, RenderAttrib *default_attrib) {
// If this attribute was already registered, something odd is going on.
nassertr(RenderAttrib::_attribs->find(default_attrib) == -1, 0);
default_attrib->_saved_entry =
RenderAttrib::_attribs->store(default_attrib, RenderAttrib::Empty());
RenderAttrib::_attribs->store(default_attrib, nullptr);
}
// It effectively lives forever. Might as well make it official.

View File

@ -987,6 +987,7 @@ clear_munger_cache() {
for (size_t si = 0; si < size; ++si) {
RenderState *state = (RenderState *)(_states->get_key(si));
state->_mungers.clear();
state->_munged_states.clear();
state->_last_mi = -1;
}
}
@ -1338,7 +1339,7 @@ return_unique(RenderState *state) {
// deleted while it's in it.
state->cache_ref();
}
si = _states->store(state, Empty());
si = _states->store(state, nullptr);
// Save the index and return the input state.
state->_saved_entry = si;
@ -1865,7 +1866,7 @@ init_states() {
// is declared globally, and lives forever.
RenderState *state = new RenderState;
state->local_object();
state->_saved_entry = _states->store(state, Empty());
state->_saved_entry = _states->store(state, nullptr);
_empty_state = state;
}

View File

@ -30,10 +30,8 @@
#include "lightMutex.h"
#include "deletedChain.h"
#include "simpleHashMap.h"
#include "weakKeyHashMap.h"
#include "cacheStats.h"
#include "renderAttribRegistry.h"
#include "graphicsStateGuardianBase.h"
class FactoryParams;
class ShaderAttrib;
@ -109,6 +107,8 @@ PUBLISHED:
INLINE int get_override(TypeHandle type) const;
INLINE int get_override(int slot) const;
MAKE_MAP_PROPERTY(attribs, has_attrib, get_attrib);
INLINE CPT(RenderState) get_unique() const;
virtual bool unref() const;
@ -219,15 +219,14 @@ public:
// declare this as a ShaderAttrib because that would create a circular
// include-file dependency problem. Aaargh.
mutable CPT(RenderAttrib) _generated_shader;
mutable UpdateSeq _generated_shader_seq;
private:
// This mutex protects _states. It also protects any modification to the
// cache, which is encoded in _composition_cache and
// _invert_composition_cache.
static LightReMutex *_states_lock;
class Empty {
};
typedef SimpleHashMap<const RenderState *, Empty, indirect_compare_to_hash<const RenderState *> > States;
typedef SimpleHashMap<const RenderState *, nullptr_t, indirect_compare_to_hash<const RenderState *> > States;
static States *_states;
static const RenderState *_empty_state;
@ -264,10 +263,15 @@ private:
// in the RenderState pointer than vice-versa, since there are likely to be
// far fewer GSG's than RenderStates. The code to manage this map lives in
// GraphicsStateGuardian::get_geom_munger().
typedef WeakKeyHashMap<GraphicsStateGuardianBase, PT(GeomMunger) > Mungers;
typedef SimpleHashMap<size_t, PT(GeomMunger), size_t_hash> Mungers;
mutable Mungers _mungers;
mutable int _last_mi;
// Similarly, this is a cache of munged states. This map is managed by
// StateMunger::munge_state().
typedef SimpleHashMap<size_t, WCPT(RenderState), size_t_hash> MungedStates;
mutable MungedStates _munged_states;
// This is used to mark nodes as we visit them to detect cycles.
UpdateSeq _cycle_detect;
static UpdateSeq _last_cycle_detect;
@ -360,6 +364,8 @@ private:
friend class GraphicsStateGuardian;
friend class RenderAttribRegistry;
friend class Extension<RenderState>;
friend class ShaderGenerator;
friend class StateMunger;
};
// We can safely redefine this as a no-op.

View File

@ -73,6 +73,7 @@ PUBLISHED:
// Shader Inputs
CPT(RenderAttrib) set_shader_input(ShaderInput input) const;
public:
INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, Texture *tex, int priority=0) const;
INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0) const;
INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0) const;
@ -90,6 +91,10 @@ PUBLISHED:
INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, double n1=0, double n2=0, double n3=0, double n4=1,
int priority=0) const;
PUBLISHED:
EXTENSION(CPT(RenderAttrib) set_shader_input(CPT_InternalName, PyObject *, int priority=0) const);
EXTENSION(CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const);
CPT(RenderAttrib) set_instance_count(int instance_count) const;
CPT(RenderAttrib) set_flag(int flag, bool value) const;
@ -150,6 +155,7 @@ private:
Inputs _inputs;
friend class Extension<NodePath>;
friend class Extension<ShaderAttrib>;
PUBLISHED:
static int get_class_slot() {

View File

@ -0,0 +1,71 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file shaderAttrib_ext.cxx
* @author rdb
* @date 2017-10-08
*/
#include "shaderAttrib_ext.h"
#include "shaderInput_ext.h"
#ifdef HAVE_PYTHON
/**
* Returns a new ShaderAttrib with the given shader input set.
*/
CPT(RenderAttrib) Extension<ShaderAttrib>::
set_shader_input(CPT_InternalName name, PyObject *value, int priority) const {
ShaderAttrib *attrib = new ShaderAttrib(*_this);
ShaderInput &input = attrib->_inputs[name];
invoke_extension(&input).__init__(move(name), value);
return ShaderAttrib::return_new(attrib);
}
/**
* Returns a new ShaderAttrib with the given shader inputs set. This is a
* more efficient way to set multiple shader inputs than calling
* set_shader_input multiple times.
*/
CPT(RenderAttrib) Extension<ShaderAttrib>::
set_shader_inputs(PyObject *args, PyObject *kwargs) const {
if (PyObject_Size(args) > 0) {
Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs takes only keyword arguments");
return nullptr;
}
ShaderAttrib *attrib = new ShaderAttrib(*_this);
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(kwargs, &pos, &key, &value)) {
char *buffer;
Py_ssize_t length;
#if PY_MAJOR_VERSION >= 3
buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length);
if (buffer == nullptr) {
#else
if (PyString_AsStringAndSize(key, &buffer, &length) == -1) {
#endif
Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs accepts only string keywords");
delete attrib;
return nullptr;
}
CPT_InternalName name(string(buffer, length));
ShaderInput &input = attrib->_inputs[name];
invoke_extension(&input).__init__(move(name), value);
}
return ShaderAttrib::return_new(attrib);
}
#endif // HAVE_PYTHON

View File

@ -0,0 +1,38 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file shaderAttrib_ext.h
* @author rdb
* @date 2017-10-08
*/
#ifndef SHADERATTRIB_EXT_H
#define SHADERATTRIB_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "shaderAttrib.h"
#include "py_panda.h"
/**
* This class defines the extension methods for ShaderAttrib, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<ShaderAttrib> : public ExtensionBase<ShaderAttrib> {
public:
CPT(RenderAttrib) set_shader_input(CPT_InternalName id, PyObject *value, int priority=0) const;
CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const;
};
#endif // HAVE_PYTHON
#endif // SHADERATTRIB_EXT_H

View File

@ -417,6 +417,14 @@ ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) :
{
}
/**
*
*/
INLINE ShaderInput::
operator bool () const {
return _type != M_invalid;
}
/**
*
*/

View File

@ -46,7 +46,11 @@ PUBLISHED:
};
static const ShaderInput &get_blank();
INLINE ShaderInput(CPT_InternalName name, int priority=0);
INLINE explicit ShaderInput(CPT_InternalName name, int priority=0);
EXTENSION(explicit ShaderInput(CPT_InternalName name, PyObject *value, int priority=0));
public:
INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0);
@ -83,6 +87,8 @@ PUBLISHED:
INLINE ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority=0);
ShaderInput(CPT_InternalName name, const NodePath &np, int priority=0);
PUBLISHED:
ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority=0);
@ -98,6 +104,7 @@ PUBLISHED:
M_buffer,
};
INLINE operator bool() const;
INLINE bool operator == (const ShaderInput &other) const;
INLINE bool operator != (const ShaderInput &other) const;
INLINE bool operator < (const ShaderInput &other) const;
@ -132,6 +139,7 @@ private:
int _type;
friend class ShaderAttrib;
friend class Extension<ShaderInput>;
};
#include "shaderInput.I"

View File

@ -0,0 +1,539 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file shaderInput_ext.cxx
* @author rdb
* @date 2017-10-06
*/
#include "shaderInput_ext.h"
#include "paramNodePath.h"
#ifdef HAVE_PYTHON
#ifndef CPPPARSER
extern struct Dtool_PyTypedObject Dtool_Texture;
extern struct Dtool_PyTypedObject Dtool_NodePath;
extern struct Dtool_PyTypedObject Dtool_PointerToVoid;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_float;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_double;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_int;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3f;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3d;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4i;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3i;
extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2i;
extern struct Dtool_PyTypedObject Dtool_LMatrix4f;
extern struct Dtool_PyTypedObject Dtool_LMatrix3f;
extern struct Dtool_PyTypedObject Dtool_LMatrix4d;
extern struct Dtool_PyTypedObject Dtool_LMatrix3d;
extern struct Dtool_PyTypedObject Dtool_LVecBase4f;
extern struct Dtool_PyTypedObject Dtool_LVecBase3f;
extern struct Dtool_PyTypedObject Dtool_LVecBase2f;
extern struct Dtool_PyTypedObject Dtool_LVecBase4d;
extern struct Dtool_PyTypedObject Dtool_LVecBase3d;
extern struct Dtool_PyTypedObject Dtool_LVecBase2d;
extern struct Dtool_PyTypedObject Dtool_LVecBase4i;
extern struct Dtool_PyTypedObject Dtool_LVecBase3i;
extern struct Dtool_PyTypedObject Dtool_LVecBase2i;
extern struct Dtool_PyTypedObject Dtool_ShaderBuffer;
extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
#endif // CPPPARSER
/**
* Sets a shader input from an arbitrary Python object.
*/
void Extension<ShaderInput>::
__init__(CPT_InternalName name, PyObject *value, int priority) {
_this->_name = move(name);
_this->_priority = priority;
if (PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value) <= 4) {
// A tuple is interpreted as a vector.
Py_ssize_t size = PyTuple_GET_SIZE(value);
// If any of them is a float, we are storing it as a float vector.
bool is_float = false;
for (Py_ssize_t i = 0; i < size; ++i) {
if (PyFloat_CheckExact(PyTuple_GET_ITEM(value, i))) {
is_float = true;
break;
}
}
if (is_float) {
LVecBase4 vec(0);
for (Py_ssize_t i = 0; i < size; ++i) {
vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i));
}
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector = vec;
} else {
LVecBase4i vec(0);
for (Py_ssize_t i = 0; i < size; ++i) {
vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i));
}
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = vec;
_this->_stored_vector = LCAST(PN_stdfloat, vec);
}
} else if (DtoolCanThisBeAPandaInstance(value)) {
Dtool_PyInstDef *inst = (Dtool_PyInstDef *)value;
void *ptr;
if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) {
_this->_type = ShaderInput::M_texture;
_this->_value = (Texture *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) {
_this->_type = ShaderInput::M_nodepath;
_this->_value = new ParamNodePath(*(const NodePath *)ptr);
} else if (inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToVoid)) {
if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_float *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_double *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_int *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase4f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase3f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase2f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LMatrix4f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LMatrix3f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase4d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase3d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase2d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LMatrix4d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LMatrix3d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase4i *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase3i *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const PTA_LVecBase2i *)ptr;
} else {
Dtool_Raise_TypeError("unknown type passed to ShaderInput");
return;
}
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix4f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const LMatrix4f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix3f))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const LMatrix3f *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix4d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const LMatrix4d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix3d))) {
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = *(const LMatrix3d *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) {
const LVecBase4f &vec = *(const LVecBase4f *)ptr;
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector = LCAST(PN_stdfloat, vec);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) {
const LVecBase3f &vec = *(const LVecBase3f *)ptr;
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector.set(vec[0], vec[1], vec[2], 0);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) {
const LVecBase2f &vec = *(const LVecBase2f *)ptr;
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector.set(vec[0], vec[1], 0, 0);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) {
const LVecBase4d &vec = *(const LVecBase4d *)ptr;
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector = LCAST(PN_stdfloat, vec);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) {
const LVecBase3d &vec = *(const LVecBase3d *)ptr;
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector.set(vec[0], vec[1], vec[2], 0);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) {
const LVecBase2d &vec = *(const LVecBase2d *)ptr;
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector.set(vec[0], vec[1], 0, 0);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) {
const LVecBase4i &vec = *(const LVecBase4i *)ptr;
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = vec;
_this->_stored_vector = LCAST(PN_stdfloat, vec);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) {
const LVecBase3i &vec = *(const LVecBase3i *)ptr;
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = vec;
_this->_stored_vector.set(vec[0], vec[1], vec[2], 0);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) {
const LVecBase2i &vec = *(const LVecBase2i *)ptr;
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = vec;
_this->_stored_vector.set(vec[0], vec[1], 0, 0);
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) {
_this->_type = ShaderInput::M_buffer;
_this->_value = (ShaderBuffer *)ptr;
} else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) {
_this->_type = ShaderInput::M_param;
_this->_value = (ParamValueBase *)ptr;
} else {
Dtool_Raise_TypeError("unknown type passed to ShaderInput");
return;
}
} else if (PyFloat_Check(value)) {
LVecBase4 vec(PyFloat_AS_DOUBLE(value), 0, 0, 0);
_this->_type = ShaderInput::M_vector;
_this->_stored_ptr = vec;
_this->_stored_vector = vec;
#if PY_MAJOR_VERSION < 3
} else if (PyInt_Check(value)) {
LVecBase4i vec((int)PyInt_AS_LONG(value), 0, 0, 0);
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = vec;
_this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0);
#endif
} else if (PyLong_Check(value)) {
LVecBase4i vec((int)PyLong_AsLong(value), 0, 0, 0);
_this->_type = ShaderInput::M_numeric;
_this->_stored_ptr = vec;
_this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0);
} else if (PySequence_Check(value) && !PyUnicode_CheckExact(value)) {
// Iterate over the sequence to make sure all have the same type.
PyObject *fast = PySequence_Fast(value, "unknown type passed to ShaderInput");
if (fast == nullptr) {
return;
}
Py_ssize_t num_items = PySequence_Fast_GET_SIZE(value);
if (num_items <= 0) {
// We can't determine the type of a list of size 0.
_this->_type = ShaderInput::M_numeric;
Py_DECREF(fast);
return;
}
bool has_float = false;
Py_ssize_t known_itemsize = -1;
PyObject **items = PySequence_Fast_ITEMS(fast);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
Py_ssize_t itemsize = PySequence_Size(item);
if (known_itemsize >= 0 && itemsize != known_itemsize) {
Dtool_Raise_TypeError("inconsistent sequence length among elements of sequence passed to ShaderInput");
Py_DECREF(fast);
return;
}
known_itemsize = itemsize;
// Check their types.
for (Py_ssize_t j = 0; j < itemsize; ++j) {
PyObject *subitem = PySequence_ITEM(item, j);
if (PyFloat_CheckExact(subitem)) {
Py_DECREF(subitem);
has_float = true;
break;
} else if (PyLongOrInt_Check(subitem)) {
} else {
Dtool_Raise_TypeError("unknown element type in sequence passed as element of sequence passed to ShaderInput");
Py_DECREF(subitem);
Py_DECREF(fast);
break;
}
Py_DECREF(subitem);
}
} else if (PyFloat_CheckExact(item)) {
has_float = true;
} else if (PyLongOrInt_Check(item)) {
} else {
Dtool_Raise_TypeError("unknown element type in sequence passed to ShaderInput");
Py_DECREF(fast);
return;
}
}
// Now that we have verified the dimensions and type of the PTA, we can
// read in the actual elements.
switch (known_itemsize) {
case -1:
if (has_float) {
PTA_float pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
pta.push_back(PyFloat_AsDouble(items[i]));
}
_this->_stored_ptr = pta;
} else {
PTA_int pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
pta.push_back((int)PyLongOrInt_AS_LONG(items[i]));
}
_this->_stored_ptr = pta;
}
break;
case 1:
if (has_float) {
PTA_float pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem = PySequence_ITEM(item, 0);
pta.push_back(PyFloat_AsDouble(subitem));
Py_DECREF(subitem);
} else {
pta.push_back(PyFloat_AsDouble(item));
}
}
_this->_stored_ptr = pta;
} else {
PTA_int pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem = PySequence_ITEM(item, 0);
pta.push_back((int)PyLongOrInt_AS_LONG(subitem));
Py_DECREF(subitem);
} else {
pta.push_back((int)PyLongOrInt_AS_LONG(item));
}
}
_this->_stored_ptr = pta;
}
break;
case 2:
if (has_float) {
PTA_LVecBase2f pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem0 = PySequence_ITEM(item, 0);
PyObject *subitem1 = PySequence_ITEM(item, 1);
pta.push_back(LVecBase2f(PyFloat_AsDouble(subitem0),
PyFloat_AsDouble(subitem1)));
Py_DECREF(subitem0);
Py_DECREF(subitem1);
} else {
pta.push_back(PyFloat_AsDouble(item));
}
}
_this->_stored_ptr = pta;
} else {
PTA_LVecBase2i pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem0 = PySequence_ITEM(item, 0);
PyObject *subitem1 = PySequence_ITEM(item, 1);
pta.push_back(LVecBase2i((int)PyLongOrInt_AS_LONG(subitem0),
(int)PyLongOrInt_AS_LONG(subitem1)));
Py_DECREF(subitem0);
Py_DECREF(subitem1);
} else {
pta.push_back((int)PyLongOrInt_AS_LONG(item));
}
}
_this->_stored_ptr = pta;
}
break;
case 3:
if (has_float) {
PTA_LVecBase3f pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem0 = PySequence_ITEM(item, 0);
PyObject *subitem1 = PySequence_ITEM(item, 1);
PyObject *subitem2 = PySequence_ITEM(item, 2);
pta.push_back(LVecBase3f(PyFloat_AsDouble(subitem0),
PyFloat_AsDouble(subitem1),
PyFloat_AsDouble(subitem2)));
Py_DECREF(subitem0);
Py_DECREF(subitem1);
Py_DECREF(subitem2);
} else {
pta.push_back(PyFloat_AsDouble(item));
}
}
_this->_stored_ptr = pta;
} else {
PTA_LVecBase3i pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem0 = PySequence_ITEM(item, 0);
PyObject *subitem1 = PySequence_ITEM(item, 1);
PyObject *subitem2 = PySequence_ITEM(item, 2);
pta.push_back(LVecBase3i((int)PyLongOrInt_AS_LONG(subitem0),
(int)PyLongOrInt_AS_LONG(subitem1),
(int)PyLongOrInt_AS_LONG(subitem2)));
Py_DECREF(subitem0);
Py_DECREF(subitem1);
Py_DECREF(subitem2);
} else {
pta.push_back((int)PyLongOrInt_AS_LONG(item));
}
}
_this->_stored_ptr = pta;
}
break;
case 4:
if (has_float) {
PTA_LVecBase4f pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem0 = PySequence_ITEM(item, 0);
PyObject *subitem1 = PySequence_ITEM(item, 1);
PyObject *subitem2 = PySequence_ITEM(item, 2);
PyObject *subitem3 = PySequence_ITEM(item, 3);
pta.push_back(LVecBase4f(PyFloat_AsDouble(subitem0),
PyFloat_AsDouble(subitem1),
PyFloat_AsDouble(subitem2),
PyFloat_AsDouble(subitem3)));
Py_DECREF(subitem0);
Py_DECREF(subitem1);
Py_DECREF(subitem2);
Py_DECREF(subitem3);
} else {
pta.push_back(PyFloat_AsDouble(item));
}
}
_this->_stored_ptr = pta;
} else {
PTA_LVecBase4i pta;
pta.reserve(num_items);
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *item = items[i];
if (PySequence_Check(item)) {
PyObject *subitem0 = PySequence_ITEM(item, 0);
PyObject *subitem1 = PySequence_ITEM(item, 1);
PyObject *subitem2 = PySequence_ITEM(item, 2);
PyObject *subitem3 = PySequence_ITEM(item, 3);
pta.push_back(LVecBase4i((int)PyLongOrInt_AS_LONG(subitem0),
(int)PyLongOrInt_AS_LONG(subitem1),
(int)PyLongOrInt_AS_LONG(subitem2),
(int)PyLongOrInt_AS_LONG(subitem3)));
Py_DECREF(subitem0);
Py_DECREF(subitem1);
Py_DECREF(subitem2);
Py_DECREF(subitem3);
} else {
pta.push_back((int)PyLongOrInt_AS_LONG(item));
}
}
_this->_stored_ptr = pta;
}
break;
case 0:
Dtool_Raise_TypeError("sequence passed to ShaderInput contains an empty sequence");
break;
default:
Dtool_Raise_TypeError("sequence passed to ShaderInput contains a sequence of more than 4 elements");
break;
}
_this->_type = ShaderInput::M_numeric;
Py_DECREF(fast);
} else {
Dtool_Raise_TypeError("unknown type passed to ShaderInput");
}
}
#endif // HAVE_PYTHON

View File

@ -0,0 +1,37 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file shaderInput_ext.h
* @author rdb
* @date 2017-10-06
*/
#ifndef SHADERINPUT_EXT_H
#define SHADERINPUT_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "shaderInput.h"
#include "py_panda.h"
/**
* This class defines the extension methods for NodePath, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<ShaderInput> : public ExtensionBase<ShaderInput> {
public:
void __init__(CPT_InternalName name, PyObject *value, int priority=0);
};
#endif // HAVE_PYTHON
#endif // SHADERINPUT_EXT_H

View File

@ -16,6 +16,15 @@
*/
INLINE StateMunger::
StateMunger(GraphicsStateGuardianBase *gsg) :
GeomMunger(gsg)
GeomMunger(gsg),
_should_munge_state(false)
{
}
/**
* Returns true if this munger has something interesting to do to the state.
*/
INLINE bool StateMunger::
should_munge_state() const {
return _should_munge_state;
}

View File

@ -27,15 +27,20 @@ StateMunger::
*/
CPT(RenderState) StateMunger::
munge_state(const RenderState *state) {
int mi = _state_map.find(state);
RenderState::MungedStates &munged_states = state->_munged_states;
int id = get_gsg()->_id;
int mi = munged_states.find(id);
if (mi != -1) {
if (!_state_map.get_data(mi).was_deleted()) {
return _state_map.get_data(mi).p();
if (!munged_states.get_data(mi).was_deleted()) {
return munged_states.get_data(mi).p();
} else {
munged_states.remove_element(mi);
}
}
CPT(RenderState) result = munge_state_impl(state);
_state_map.store(state, result.p());
munged_states.store(id, result.p());
return result;
}

View File

@ -30,11 +30,12 @@ public:
virtual ~StateMunger();
CPT(RenderState) munge_state(const RenderState *state);
INLINE bool should_munge_state() const;
protected:
virtual CPT(RenderState) munge_state_impl(const RenderState *state);
typedef WeakKeyHashMap<RenderState, WCPT(RenderState) > StateMap;
StateMap _state_map;
bool _should_munge_state;
public:
static TypeHandle get_class_type() {

View File

@ -64,12 +64,18 @@ PUBLISHED:
int find_on_stage(const TextureStage *stage) const;
MAKE_SEQ_PROPERTY(on_stages, get_num_on_stages, get_on_stage);
MAKE_MAP_PROPERTY(on_stages, find_on_stage, get_on_stage);
INLINE int get_num_off_stages() const;
INLINE TextureStage *get_off_stage(int n) const;
MAKE_SEQ(get_off_stages, get_num_off_stages, get_off_stage);
INLINE bool has_off_stage(TextureStage *stage) const;
INLINE bool has_all_off() const;
MAKE_SEQ_PROPERTY(off_stages, get_num_off_stages, get_off_stage);
MAKE_MAP_PROPERTY(off_stages, has_off_stage, get_off_stage);
INLINE bool is_identity() const;
CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const;

View File

@ -1512,7 +1512,7 @@ return_unique(TransformState *state) {
// deleted while it's in it.
state->cache_ref();
}
si = _states->store(state, Empty());
si = _states->store(state, nullptr);
// Save the index and return the input state.
state->_saved_entry = si;

View File

@ -253,9 +253,7 @@ private:
// cache, which is encoded in _composition_cache and
// _invert_composition_cache.
static LightReMutex *_states_lock;
class Empty {
};
typedef SimpleHashMap<const TransformState *, Empty, indirect_equals_hash<const TransformState *> > States;
typedef SimpleHashMap<const TransformState *, nullptr_t, indirect_equals_hash<const TransformState *> > States;
static States *_states;
static CPT(TransformState) _identity_state;
static CPT(TransformState) _invalid_state;

View File

@ -51,6 +51,14 @@ TypeHandle ShaderGenerator::_type_handle;
#ifdef HAVE_CG
#define PACK_COMBINE(src0, op0, src1, op1, src2, op2) ( \
((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) | \
((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) | \
((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u))
#define UNPACK_COMBINE_SRC(from, n) (TextureStage::CombineSource)((from >> ((uint16_t)n * 5u)) & 7u)
#define UNPACK_COMBINE_OP(from, n) (TextureStage::CombineOperand)(((from >> (((uint16_t)n * 5u) + 3u)) & 3u) + 1u)
static PStatCollector lookup_collector("*:Munge:ShaderGen:Lookup");
static PStatCollector synthesize_collector("*:Munge:ShaderGen:Synthesize");
@ -60,7 +68,7 @@ static PStatCollector synthesize_collector("*:Munge:ShaderGen:Synthesize");
* shader generator belongs.
*/
ShaderGenerator::
ShaderGenerator(GraphicsStateGuardianBase *gsg) {
ShaderGenerator(const GraphicsStateGuardianBase *gsg) {
// The ATTR# input semantics seem to map to generic vertex attributes in
// both arbvp1 and glslv, which behave more consistently. However, they
// don't exist in Direct3D 9. Use this silly little check for now.
@ -298,10 +306,17 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
Texture *tex = texture->get_on_texture(stage);
nassertd(tex != nullptr) continue;
// Mark this TextureStage as having been used by the shader generator, so
// that the next time its properties change, it will cause the state to be
// rehashed to ensure that the shader is regenerated if needed.
stage->mark_used_by_auto_shader();
ShaderKey::TextureInfo info;
info._type = tex->get_texture_type();
info._mode = stage->get_mode();
info._flags = 0;
info._combine_rgb = 0u;
info._combine_alpha = 0u;
// While we look at the mode, determine whether we need to change the mode
// in order to reflect disabled features.
@ -357,6 +372,40 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
info._flags = ShaderKey::TF_map_normal | ShaderKey::TF_map_gloss;
}
break;
case TextureStage::M_combine:
// If we have this rare, special mode, we encode all these extra
// parameters as flags to prevent bloating the shader key.
info._flags |= (uint32_t)stage->get_combine_rgb_mode() << ShaderKey::TF_COMBINE_RGB_MODE_SHIFT;
info._flags |= (uint32_t)stage->get_combine_alpha_mode() << ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT;
if (stage->get_rgb_scale() == 2) {
info._flags |= ShaderKey::TF_rgb_scale_2;
}
if (stage->get_rgb_scale() == 4) {
info._flags |= ShaderKey::TF_rgb_scale_4;
}
if (stage->get_alpha_scale() == 2) {
info._flags |= ShaderKey::TF_alpha_scale_2;
}
if (stage->get_alpha_scale() == 4) {
info._flags |= ShaderKey::TF_alpha_scale_4;
}
info._combine_rgb = PACK_COMBINE(
stage->get_combine_rgb_source0(), stage->get_combine_rgb_operand0(),
stage->get_combine_rgb_source1(), stage->get_combine_rgb_operand1(),
stage->get_combine_rgb_source2(), stage->get_combine_rgb_operand2());
info._combine_alpha = PACK_COMBINE(
stage->get_combine_alpha_source0(), stage->get_combine_alpha_operand0(),
stage->get_combine_alpha_source1(), stage->get_combine_alpha_operand1(),
stage->get_combine_alpha_source2(), stage->get_combine_alpha_operand2());
if (stage->uses_primary_color()) {
info._flags |= ShaderKey::TF_uses_primary_color;
}
if (stage->uses_last_saved_result()) {
info._flags |= ShaderKey::TF_uses_last_saved_result;
}
break;
}
// In fact, perhaps this stage should be disabled altogether?
@ -417,17 +466,31 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
info._gen_mode = TexGenAttrib::M_off;
}
// If we have this rare, special mode, just include a pointer to the
// TextureStage object, because I can't be bothered to bloat the shader
// key with all these extra relevant properties.
if (stage->get_mode() == TextureStage::M_combine) {
info._stage = stage;
// Does this stage require saving its result?
if (stage->get_saved_result()) {
info._flags |= ShaderKey::TF_saved_result;
}
// Does this stage need a texcolor_# input?
if (stage->uses_color()) {
info._flags |= ShaderKey::TF_uses_color;
}
key._textures.push_back(info);
key._texture_flags |= info._flags;
}
// Does nothing use the saved result? If so, don't bother saving it.
if ((key._texture_flags & ShaderKey::TF_uses_last_saved_result) == 0 &&
(key._texture_flags & ShaderKey::TF_saved_result) != 0) {
pvector<ShaderKey::TextureInfo>::iterator it;
for (it = key._textures.begin(); it != key._textures.end(); ++it) {
(*it)._flags &= ~ShaderKey::TF_saved_result;
}
key._texture_flags &= ~ShaderKey::TF_saved_result;
}
// Decide whether to separate ambient and diffuse calculations.
if (have_ambient) {
if (key._material_flags & Material::F_ambient) {
@ -463,6 +526,74 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
}
}
/**
* Rehashes all the states with generated shaders, removing the ones that are
* no longer fresh.
*
* Call this if certain state has changed in such a way as to require a rerun
* of the shader generator. This should be rare because in most cases, the
* shader generator will automatically regenerate shaders as necessary.
*/
void ShaderGenerator::
rehash_generated_shaders() {
LightReMutexHolder holder(*RenderState::_states_lock);
// With uniquify-states turned on, we can actually go through all the states
// and check whether their generated shader is still OK.
size_t size = RenderState::_states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
const RenderState *state = RenderState::_states->get_key(si);
if (state->_generated_shader != nullptr) {
ShaderKey key;
analyze_renderstate(key, state);
GeneratedShaders::const_iterator si;
si = _generated_shaders.find(key);
if (si != _generated_shaders.end()) {
if (si->second != state->_generated_shader) {
state->_generated_shader = si->second;
state->_munged_states.clear();
}
} else {
// We have not yet generated a shader for this modified state.
state->_generated_shader.clear();
state->_munged_states.clear();
}
}
}
// If we don't have uniquify-states, however, the above list won't contain
// all the state. We can change a global seq value to require Panda to
// rehash the states the next time it tries to render an object with it.
if (!uniquify_states) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
/**
* Removes all previously generated shaders, requiring all shaders to be
* regenerated. Does not clear cache of compiled shaders.
*/
void ShaderGenerator::
clear_generated_shaders() {
LightReMutexHolder holder(*RenderState::_states_lock);
size_t size = RenderState::_states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
const RenderState *state = RenderState::_states->get_key(si);
state->_generated_shader.clear();
}
_generated_shaders.clear();
// If we don't have uniquify-states, we can't clear all the ShaderAttribs
// that are cached on the states, but we can simulate the effect of that.
if (!uniquify_states) {
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
}
}
/**
* This is the routine that implements the next-gen fixed function pipeline by
* synthesizing a shader. It also takes care of setting up any buffers needed
@ -827,7 +958,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
text << "\t uniform float4x4 texmat_" << i << ",\n";
}
if (tex._mode == TextureStage::M_blend) {
if (tex._flags & ShaderKey::TF_uses_color) {
text << "\t uniform float4 texcolor_" << i << ",\n";
}
}
@ -1251,24 +1382,12 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
}
}
// Loop first to see if something is using primary_color or
// last_saved_result.
bool have_saved_result = false;
bool have_primary_color = false;
for (size_t i = 0; i < key._textures.size(); ++i) {
const ShaderKey::TextureInfo &tex = key._textures[i];
if (tex._stage == nullptr) {
continue;
}
if (tex._stage->uses_primary_color() && !have_primary_color) {
text << "\t float4 primary_color = result;\n";
have_primary_color = true;
}
if (tex._stage->uses_last_saved_result() && !have_saved_result) {
text << "\t float4 last_saved_result = result;\n";
have_saved_result = true;
}
// Store these if any stages will use it.
if (key._texture_flags & ShaderKey::TF_uses_primary_color) {
text << "\t float4 primary_color = result;\n";
}
if (key._texture_flags & ShaderKey::TF_uses_last_saved_result) {
text << "\t float4 last_saved_result = result;\n";
}
// Now loop through the textures to compose our magic blending formulas.
@ -1310,24 +1429,21 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
}
break;
case TextureStage::M_combine:
// Only in the case of M_combine have we filled in the _stage pointer.
text << "\t result.rgb = ";
if (tex._stage->get_combine_rgb_mode() != TextureStage::CM_undefined) {
text << combine_mode_as_string(tex._stage, tex._stage->get_combine_rgb_mode(), false, i);
} else {
text << "tex" << i << ".rgb";
text << combine_mode_as_string(tex, (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_RGB_MODE_MASK) >> ShaderKey::TF_COMBINE_RGB_MODE_SHIFT), false, i);
if (tex._flags & ShaderKey::TF_rgb_scale_2) {
text << " * 2";
}
if (tex._stage->get_rgb_scale() != 1) {
text << " * " << tex._stage->get_rgb_scale();
if (tex._flags & ShaderKey::TF_rgb_scale_4) {
text << " * 4";
}
text << ";\n\t result.a = ";
if (tex._stage->get_combine_alpha_mode() != TextureStage::CM_undefined) {
text << combine_mode_as_string(tex._stage, tex._stage->get_combine_alpha_mode(), true, i);
} else {
text << "tex" << i << ".a";
text << combine_mode_as_string(tex, (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_ALPHA_MODE_MASK) >> ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT), false, i);
if (tex._flags & ShaderKey::TF_alpha_scale_2) {
text << " * 2";
}
if (tex._stage->get_alpha_scale() != 1) {
text << " * " << tex._stage->get_alpha_scale();
if (tex._flags & ShaderKey::TF_alpha_scale_4) {
text << " * 4";
}
text << ";\n";
break;
@ -1337,7 +1453,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
default:
break;
}
if ((tex._flags & ShaderKey::TF_saved_result) != 0 && have_saved_result) {
if (tex._flags & ShaderKey::TF_saved_result) {
text << "\t last_saved_result = result;\n";
}
}
@ -1464,53 +1580,53 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
* This 'synthesizes' a combine mode into a string.
*/
const string ShaderGenerator::
combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode, bool alpha, short texindex) {
combine_mode_as_string(const ShaderKey::TextureInfo &info, TextureStage::CombineMode c_mode, bool alpha, short texindex) {
ostringstream text;
switch (c_mode) {
case TextureStage::CM_modulate:
text << combine_source_as_string(stage, 0, alpha, alpha, texindex);
text << " * ";
text << combine_source_as_string(stage, 1, alpha, alpha, texindex);
break;
case TextureStage::CM_add:
text << combine_source_as_string(stage, 0, alpha, alpha, texindex);
text << " + ";
text << combine_source_as_string(stage, 1, alpha, alpha, texindex);
break;
case TextureStage::CM_add_signed:
text << combine_source_as_string(stage, 0, alpha, alpha, texindex);
text << " + ";
text << combine_source_as_string(stage, 1, alpha, alpha, texindex);
if (alpha) {
text << " - 0.5";
} else {
text << " - float3(0.5, 0.5, 0.5)";
}
break;
case TextureStage::CM_interpolate:
text << "lerp(";
text << combine_source_as_string(stage, 1, alpha, alpha, texindex);
text << ", ";
text << combine_source_as_string(stage, 0, alpha, alpha, texindex);
text << ", ";
text << combine_source_as_string(stage, 2, alpha, true, texindex);
text << ")";
break;
case TextureStage::CM_subtract:
text << combine_source_as_string(stage, 0, alpha, alpha, texindex);
text << " + ";
text << combine_source_as_string(stage, 1, alpha, alpha, texindex);
break;
case TextureStage::CM_dot3_rgb:
pgraphnodes_cat.error() << "TextureStage::CombineMode DOT3_RGB not yet supported in per-pixel mode.\n";
break;
case TextureStage::CM_dot3_rgba:
pgraphnodes_cat.error() << "TextureStage::CombineMode DOT3_RGBA not yet supported in per-pixel mode.\n";
break;
case TextureStage::CM_replace:
default: // Not sure if this is correct as default value.
text << combine_source_as_string(stage, 0, alpha, alpha, texindex);
break;
case TextureStage::CM_modulate:
text << combine_source_as_string(info, 0, alpha, alpha, texindex);
text << " * ";
text << combine_source_as_string(info, 1, alpha, alpha, texindex);
break;
case TextureStage::CM_add:
text << combine_source_as_string(info, 0, alpha, alpha, texindex);
text << " + ";
text << combine_source_as_string(info, 1, alpha, alpha, texindex);
break;
case TextureStage::CM_add_signed:
text << combine_source_as_string(info, 0, alpha, alpha, texindex);
text << " + ";
text << combine_source_as_string(info, 1, alpha, alpha, texindex);
if (alpha) {
text << " - 0.5";
} else {
text << " - float3(0.5, 0.5, 0.5)";
}
break;
case TextureStage::CM_interpolate:
text << "lerp(";
text << combine_source_as_string(info, 1, alpha, alpha, texindex);
text << ", ";
text << combine_source_as_string(info, 0, alpha, alpha, texindex);
text << ", ";
text << combine_source_as_string(info, 2, alpha, true, texindex);
text << ")";
break;
case TextureStage::CM_subtract:
text << combine_source_as_string(info, 0, alpha, alpha, texindex);
text << " + ";
text << combine_source_as_string(info, 1, alpha, alpha, texindex);
break;
case TextureStage::CM_dot3_rgb:
pgraphnodes_cat.error() << "TextureStage::CombineMode DOT3_RGB not yet supported in per-pixel mode.\n";
break;
case TextureStage::CM_dot3_rgba:
pgraphnodes_cat.error() << "TextureStage::CombineMode DOT3_RGBA not yet supported in per-pixel mode.\n";
break;
case TextureStage::CM_replace:
default: // Not sure if this is correct as default value.
text << combine_source_as_string(info, 0, alpha, alpha, texindex);
break;
}
return text.str();
}
@ -1519,39 +1635,15 @@ combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode
* This 'synthesizes' a combine source into a string.
*/
const string ShaderGenerator::
combine_source_as_string(CPT(TextureStage) stage, short num, bool alpha, bool single_value, short texindex) {
TextureStage::CombineSource c_src = TextureStage::CS_undefined;
TextureStage::CombineOperand c_op = TextureStage::CO_undefined;
if (alpha) {
switch (num) {
case 0:
c_src = stage->get_combine_alpha_source0();
c_op = stage->get_combine_alpha_operand0();
break;
case 1:
c_src = stage->get_combine_alpha_source1();
c_op = stage->get_combine_alpha_operand1();
break;
case 2:
c_src = stage->get_combine_alpha_source2();
c_op = stage->get_combine_alpha_operand2();
break;
}
combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alpha, bool single_value, short texindex) {
TextureStage::CombineSource c_src;
TextureStage::CombineOperand c_op;
if (!alpha) {
c_src = UNPACK_COMBINE_SRC(info._combine_rgb, num);
c_op = UNPACK_COMBINE_OP(info._combine_rgb, num);
} else {
switch (num) {
case 0:
c_src = stage->get_combine_rgb_source0();
c_op = stage->get_combine_rgb_operand0();
break;
case 1:
c_src = stage->get_combine_rgb_source1();
c_op = stage->get_combine_rgb_operand1();
break;
case 2:
c_src = stage->get_combine_rgb_source2();
c_op = stage->get_combine_rgb_operand2();
break;
}
c_src = UNPACK_COMBINE_SRC(info._combine_alpha, num);
c_op = UNPACK_COMBINE_OP(info._combine_alpha, num);
}
ostringstream csource;
if (c_op == TextureStage::CO_one_minus_src_color ||
@ -1562,10 +1654,9 @@ combine_source_as_string(CPT(TextureStage) stage, short num, bool alpha, bool si
case TextureStage::CS_texture:
csource << "tex" << texindex;
break;
case TextureStage::CS_constant: {
LVecBase4 c = stage->get_color();
csource << "float4(" << c[0] << ", " << c[1] << ", " << c[2] << ", " << c[3] << ")";
break; }
case TextureStage::CS_constant:
csource << "texcolor_" << texindex;
break;
case TextureStage::CS_primary_color:
csource << "primary_color";
break;
@ -1684,8 +1775,11 @@ operator < (const ShaderKey &other) const {
if (tex._flags != other_tex._flags) {
return tex._flags < other_tex._flags;
}
if (tex._stage != other_tex._stage) {
return tex._stage < other_tex._stage;
if (tex._combine_rgb != other_tex._combine_rgb) {
return tex._combine_rgb < other_tex._combine_rgb;
}
if (tex._combine_alpha != other_tex._combine_alpha) {
return tex._combine_alpha < other_tex._combine_alpha;
}
}
if (_lights.size() != other._lights.size()) {
@ -1759,7 +1853,8 @@ operator == (const ShaderKey &other) const {
tex._mode != other_tex._mode ||
tex._gen_mode != other_tex._gen_mode ||
tex._flags != other_tex._flags ||
tex._stage != other_tex._stage) {
tex._combine_rgb != other_tex._combine_rgb ||
tex._combine_alpha != other_tex._combine_alpha) {
return false;
}
}

View File

@ -65,18 +65,15 @@ class GeomVertexAnimationSpec;
*/
class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public TypedReferenceCount {
PUBLISHED:
ShaderGenerator(GraphicsStateGuardianBase *gsg);
ShaderGenerator(const GraphicsStateGuardianBase *gsg);
virtual ~ShaderGenerator();
virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs,
const GeomVertexAnimationSpec &anim);
protected:
static const string combine_mode_as_string(CPT(TextureStage) stage,
TextureStage::CombineMode c_mode, bool alpha, short texindex);
static const string combine_source_as_string(CPT(TextureStage) stage,
short num, bool alpha, bool single_value, short texindex);
static const string texture_type_as_string(Texture::TextureType ttype);
void rehash_generated_shaders();
void clear_generated_shaders();
protected:
// Shader register allocation:
bool _use_generic_attr;
@ -101,15 +98,28 @@ protected:
GeomVertexAnimationSpec _anim_spec;
enum TextureFlags {
TF_has_rgb = 1,
TF_has_alpha = 2,
TF_has_texscale = 4,
TF_has_texmat = 8,
TF_saved_result = 16,
TF_map_normal = 32,
TF_map_height = 64,
TF_map_glow = 128,
TF_map_gloss = 256,
TF_has_rgb = 0x001,
TF_has_alpha = 0x002,
TF_has_texscale = 0x004,
TF_has_texmat = 0x008,
TF_saved_result = 0x010,
TF_map_normal = 0x020,
TF_map_height = 0x040,
TF_map_glow = 0x080,
TF_map_gloss = 0x100,
TF_uses_color = 0x200,
TF_uses_primary_color = 0x400,
TF_uses_last_saved_result = 0x800,
TF_rgb_scale_2 = 0x1000,
TF_rgb_scale_4 = 0x2000,
TF_alpha_scale_2 = 0x4000,
TF_alpha_scale_4 = 0x8000,
TF_COMBINE_RGB_MODE_SHIFT = 16,
TF_COMBINE_RGB_MODE_MASK = 0x0000f0000,
TF_COMBINE_ALPHA_MODE_SHIFT = 20,
TF_COMBINE_ALPHA_MODE_MASK = 0x000f00000,
};
ColorAttrib::Type _color_type;
@ -122,9 +132,8 @@ protected:
TextureStage::Mode _mode;
TexGenAttrib::Mode _gen_mode;
int _flags;
// Stored only if combine modes / blend color is used
CPT(TextureStage) _stage;
uint16_t _combine_rgb;
uint16_t _combine_alpha;
};
pvector<TextureInfo> _textures;
@ -159,6 +168,12 @@ protected:
void analyze_renderstate(ShaderKey &key, const RenderState *rs);
static const string combine_mode_as_string(const ShaderKey::TextureInfo &info,
TextureStage::CombineMode c_mode, bool alpha, short texindex);
static const string combine_source_as_string(const ShaderKey::TextureInfo &info,
short num, bool alpha, bool single_value, short texindex);
static const string texture_type_as_string(Texture::TextureType ttype);
public:
static TypeHandle get_class_type() {
return _type_handle;

View File

@ -128,7 +128,7 @@ store(const Key &key, const Value &data) {
}
if (is_element(index, key)) {
// This element is already in the map; replace the data at that key.
_table[index]._data = data;
set_data(index, data);
#ifdef _DEBUG
nassertr(validate(), index);
#endif
@ -151,7 +151,7 @@ store(const Key &key, const Value &data) {
return index;
}
if (is_element(index, key)) {
_table[index]._data = data;
set_data(index, data);
#ifdef _DEBUG
nassertr(validate(), index);
#endif
@ -200,8 +200,7 @@ remove(const Key &key) {
// Swap it with the last one, so that we don't get any gaps in the table
// of entries.
_table[index]._key = move(_table[last]._key);
_table[index]._data = move(_table[last]._data);
_table[index] = move(_table[last]);
index_array[(size_t)other_slot] = index;
}
@ -311,8 +310,8 @@ get_key(size_t n) const {
template<class Key, class Value, class Compare>
INLINE const Value &SimpleHashMap<Key, Value, Compare>::
get_data(size_t n) const {
nassertr(n < _num_entries, _table[n]._data);
return _table[n]._data;
nassertr(n < _num_entries, _table[n].get_data());
return _table[n].get_data();
}
/**
@ -323,8 +322,8 @@ get_data(size_t n) const {
template<class Key, class Value, class Compare>
INLINE Value &SimpleHashMap<Key, Value, Compare>::
modify_data(size_t n) {
nassertr(n < _num_entries, _table[n]._data);
return _table[n]._data;
nassertr(n < _num_entries, _table[n].modify_data());
return _table[n].modify_data();
}
/**
@ -336,7 +335,19 @@ template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
set_data(size_t n, const Value &data) {
nassertv(n < _num_entries);
_table[n]._data = data;
_table[n].set_data(data);
}
/**
* Changes the data for the nth entry of the table.
*
* @param n should be in the range 0 <= n < size().
*/
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
set_data(size_t n, Value &&data) {
nassertv(n < _num_entries);
_table[n].set_data(move(data));
}
/**

View File

@ -18,6 +18,52 @@
#include "pvector.h"
#include "config_util.h"
/**
* Entry in the SimpleHashMap.
*/
template<class Key, class Value>
class SimpleKeyValuePair {
public:
INLINE SimpleKeyValuePair(const Key &key, const Value &data) :
_key(key),
_data(data) {}
Key _key;
ALWAYS_INLINE const Value &get_data() const {
return _data;
}
ALWAYS_INLINE Value &modify_data() {
return _data;
}
ALWAYS_INLINE void set_data(const Value &data) {
_data = data;
}
ALWAYS_INLINE void set_data(Value &&data) {
_data = move(data);
}
private:
Value _data;
};
/**
* Specialisation of SimpleKeyValuePair to not waste memory for nullptr_t
* values. This allows effectively using SimpleHashMap as a set.
*/
template<class Key>
class SimpleKeyValuePair<Key, nullptr_t> {
public:
INLINE SimpleKeyValuePair(const Key &key, nullptr_t data) :
_key(key) {}
Key _key;
ALWAYS_INLINE_CONSTEXPR static nullptr_t get_data() { return nullptr; }
ALWAYS_INLINE_CONSTEXPR static nullptr_t modify_data() { return nullptr; }
ALWAYS_INLINE static void set_data(nullptr_t) {}
};
/**
* This template class implements an unordered map of keys to data,
* implemented as a hashtable. It is similar to STL's hash_map, but
@ -28,6 +74,8 @@
* (d) it allows for efficient iteration over the entries,
* (e) permits removal and resizing during forward iteration, and
* (f) it has a constexpr constructor.
*
* It can also be used as a set, by using nullptr_t as Value typename.
*/
template<class Key, class Value, class Compare = method_hash<Key, less<Key> > >
class SimpleHashMap {
@ -55,6 +103,7 @@ public:
INLINE const Value &get_data(size_t n) const;
INLINE Value &modify_data(size_t n);
INLINE void set_data(size_t n, const Value &data);
INLINE void set_data(size_t n, Value &&data);
void remove_element(size_t n);
INLINE size_t get_num_entries() const;
@ -67,8 +116,6 @@ public:
INLINE bool consider_shrink_table();
private:
class TableEntry;
INLINE size_t get_hash(const Key &key) const;
INLINE size_t next_hash(size_t hash) const;
@ -82,23 +129,8 @@ private:
INLINE bool consider_expand_table();
void resize_table(size_t new_size);
class TableEntry {
public:
INLINE TableEntry(const Key &key, const Value &data) :
_key(key),
_data(data) {}
INLINE TableEntry(const TableEntry &copy) :
_key(copy._key),
_data(copy._data) {}
#ifdef USE_MOVE_SEMANTICS
INLINE TableEntry(TableEntry &&from) NOEXCEPT :
_key(move(from._key)),
_data(move(from._data)) {}
#endif
Key _key;
Value _data;
};
public:
typedef SimpleKeyValuePair<Key, Value> TableEntry;
TableEntry *_table;
DeletedBufferChain *_deleted_chain;
size_t _table_size;

View File

@ -193,7 +193,7 @@ handle_args(ProgramBase::Args &args) {
file_path.append_directory(filename.get_dirname());
if (_force_complete) {
if (!file_data.load_externals()) {
if (!file_data.load_externals(file_path)) {
exit(1);
}
}

View File

@ -338,7 +338,7 @@ class FireflyDemo(ShowBase):
color_g = random.uniform(0.8, 1.0)
color_b = min(color_g, random.uniform(0.5, 1.0))
fly.setColor(color_r, color_g, color_b, 1.0)
fly.setShaderInput("lightcolor", color_r, color_g, color_b, 1.0)
fly.setShaderInput("lightcolor", (color_r, color_g, color_b, 1.0))
int1 = fly.posInterval(random.uniform(7, 12), pos1, pos2)
int2 = fly.posInterval(random.uniform(7, 12), pos2, pos1)
si1 = fly.scaleInterval(random.uniform(0.8, 1.5),

View File

@ -103,7 +103,7 @@ class World(DirectObject):
self.pandaModel = Actor.Actor('panda-model', {'walk': 'panda-walk4'})
self.pandaModel.reparentTo(self.pandaAxis)
self.pandaModel.setPos(9, 0, 0)
self.pandaModel.setShaderInput("scale", 0.01, 0.01, 0.01, 1.0)
self.pandaModel.setShaderInput("scale", (0.01, 0.01, 0.01, 1.0))
self.pandaWalk = self.pandaModel.actorInterval('walk', playRate=1.8)
self.pandaWalk.loop()
self.pandaMovement = self.pandaAxis.hprInterval(
@ -113,7 +113,7 @@ class World(DirectObject):
self.teapot = loader.loadModel('teapot')
self.teapot.reparentTo(render)
self.teapot.setPos(0, -20, 10)
self.teapot.setShaderInput("texDisable", 1, 1, 1, 1)
self.teapot.setShaderInput("texDisable", (1, 1, 1, 1))
self.teapotMovement = self.teapot.hprInterval(50, LPoint3(0, 360, 360))
self.teapotMovement.loop()
@ -145,9 +145,9 @@ class World(DirectObject):
# setting up shader
render.setShaderInput('light', self.LCam)
render.setShaderInput('Ldepthmap', Ldepthmap)
render.setShaderInput('ambient', self.ambient, 0, 0, 1.0)
render.setShaderInput('texDisable', 0, 0, 0, 0)
render.setShaderInput('scale', 1, 1, 1, 1)
render.setShaderInput('ambient', (self.ambient, 0, 0, 1.0))
render.setShaderInput('texDisable', (0, 0, 0, 0))
render.setShaderInput('scale', (1, 1, 1, 1))
# Put a shader on the Light camera.
lci = NodePath(PandaNode("Light Camera Initializer"))

View File

@ -80,7 +80,6 @@ class World(DirectObject):
self.teapot = loader.loadModel('teapot')
self.teapot.reparentTo(render)
self.teapot.setPos(0, -20, 10)
self.teapot.setShaderInput("texDisable", 1, 1, 1, 1)
self.teapotMovement = self.teapot.hprInterval(50, LPoint3(0, 360, 360))
self.teapotMovement.loop()