mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 08:44:19 -04:00
Merge branch 'master' into cmake
This commit is contained in:
commit
d4df1b3762
@ -656,13 +656,13 @@ get_preferred_name(CPPType *type) {
|
||||
*/
|
||||
string InterrogateBuilder::
|
||||
hash_string(const string &name, int shift_offset) {
|
||||
int hash = 0;
|
||||
unsigned int hash = 0;
|
||||
|
||||
int shift = 0;
|
||||
unsigned int shift = 0;
|
||||
string::const_iterator ni;
|
||||
for (ni = name.begin(); ni != name.end(); ++ni) {
|
||||
int c = (int)(unsigned char)(*ni);
|
||||
int shifted_c = (c << shift) & 0xffffff;
|
||||
unsigned int c = (unsigned char)*ni;
|
||||
unsigned int shifted_c = (c << shift) & 0xffffff;
|
||||
if (shift > 16) {
|
||||
// We actually want a circular shift, not an arithmetic shift.
|
||||
shifted_c |= ((c >> (24 - shift)) & 0xff) ;
|
||||
@ -675,10 +675,9 @@ hash_string(const string &name, int shift_offset) {
|
||||
// bits back at the bottom, to scramble up the bits a bit. This helps
|
||||
// reduce hash conflicts from names that are similar to each other, by
|
||||
// separating adjacent hash codes.
|
||||
int prime = 4999;
|
||||
int low_order = (hash * prime) & 0xffffff;
|
||||
int high_order = (int)((double)hash * (double)prime / (double)(1 << 24));
|
||||
hash = low_order ^ high_order;
|
||||
const unsigned int prime = 4999;
|
||||
unsigned long long product = (unsigned long long)hash * prime;
|
||||
hash = (product ^ (product >> 24)) & 0xffffff;
|
||||
|
||||
// Also add in the additional_number, times some prime factor. hash = (hash
|
||||
// + additional_number * 1657) & 0xffffff;
|
||||
@ -690,10 +689,9 @@ hash_string(const string &name, int shift_offset) {
|
||||
// deal, since we have to resolve hash conflicts anyway.
|
||||
|
||||
string result;
|
||||
int extract_h = hash;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int value = (extract_h & 0x3f);
|
||||
extract_h >>= 6;
|
||||
unsigned int value = (hash & 0x3f);
|
||||
hash >>= 6;
|
||||
if (value < 26) {
|
||||
result += (char)('A' + value);
|
||||
|
||||
|
@ -31,6 +31,14 @@
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#ifndef LINK_ALL_STATIC
|
||||
# define EXPCL_PYPANDA
|
||||
#elif defined(__GNUC__)
|
||||
# define EXPCL_PYPANDA __attribute__((weak))
|
||||
#else
|
||||
# define EXPCL_PYPANDA extern inline
|
||||
#endif
|
||||
|
||||
/* Python 2.4 */
|
||||
|
||||
// 2.4 macros which aren't available in 2.3
|
||||
@ -99,7 +107,7 @@ typedef int Py_ssize_t;
|
||||
// PyInt_FromSize_t automatically picks the right type.
|
||||
# define PyLongOrInt_AS_LONG PyInt_AsLong
|
||||
|
||||
size_t PyLongOrInt_AsSize_t(PyObject *);
|
||||
EXPCL_PYPANDA size_t PyLongOrInt_AsSize_t(PyObject *);
|
||||
#endif
|
||||
|
||||
// Which character to use in PyArg_ParseTuple et al for a byte string.
|
||||
|
@ -286,6 +286,43 @@ PyObject *_Dtool_Return(PyObject *value) {
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX < 0x03040000
|
||||
/**
|
||||
* This function converts an int value to the appropriate enum instance.
|
||||
*/
|
||||
PyObject *Dtool_EnumType_New(PyTypeObject *subtype, PyObject *args, PyObject *kwds) {
|
||||
PyObject *arg;
|
||||
if (!Dtool_ExtractArg(&arg, args, kwds, "value")) {
|
||||
return PyErr_Format(PyExc_TypeError,
|
||||
"%s() missing 1 required argument: 'value'",
|
||||
subtype->tp_name);
|
||||
}
|
||||
|
||||
if (Py_TYPE(arg) == subtype) {
|
||||
Py_INCREF(arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
PyObject *value2member = PyDict_GetItemString(subtype->tp_dict, "_value2member_map_");
|
||||
nassertr_always(value2member != nullptr, nullptr);
|
||||
|
||||
PyObject *member = PyDict_GetItem(value2member, arg);
|
||||
if (member != nullptr) {
|
||||
Py_INCREF(member);
|
||||
return member;
|
||||
}
|
||||
|
||||
PyObject *repr = PyObject_Repr(arg);
|
||||
PyErr_Format(PyExc_ValueError, "%s is not a valid %s",
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyUnicode_AS_STRING(repr),
|
||||
#else
|
||||
PyString_AS_STRING(repr),
|
||||
#endif
|
||||
subtype->tp_name);
|
||||
Py_DECREF(repr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *Dtool_EnumType_Str(PyObject *self) {
|
||||
PyObject *name = PyObject_GetAttrString(self, "name");
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
@ -337,6 +374,7 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha
|
||||
static PyObject *name_sunder_str;
|
||||
static PyObject *value_str;
|
||||
static PyObject *value_sunder_str;
|
||||
static PyObject *value2member_map_sunder_str;
|
||||
// Emulate something vaguely like the enum module.
|
||||
if (enum_class == nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
@ -344,11 +382,13 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha
|
||||
value_str = PyUnicode_InternFromString("value");
|
||||
name_sunder_str = PyUnicode_InternFromString("_name_");
|
||||
value_sunder_str = PyUnicode_InternFromString("_value_");
|
||||
value2member_map_sunder_str = PyUnicode_InternFromString("_value2member_map_");
|
||||
#else
|
||||
name_str = PyString_InternFromString("name");
|
||||
value_str = PyString_InternFromString("value");
|
||||
name_sunder_str = PyString_InternFromString("_name_");
|
||||
value_sunder_str = PyString_InternFromString("_value_");
|
||||
value2member_map_sunder_str = PyString_InternFromString("_value2member_map_");
|
||||
#endif
|
||||
PyObject *name_value_tuple = PyTuple_New(4);
|
||||
PyTuple_SET_ITEM(name_value_tuple, 0, name_str);
|
||||
@ -365,27 +405,39 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha
|
||||
enum_class = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s()N", "Enum", slots_dict);
|
||||
nassertr(enum_class != nullptr, nullptr);
|
||||
}
|
||||
PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, PyDict_New());
|
||||
|
||||
// Create a subclass of this generic Enum class we just created.
|
||||
PyObject *value2member = PyDict_New();
|
||||
PyObject *dict = PyDict_New();
|
||||
PyDict_SetItem(dict, value2member_map_sunder_str, value2member);
|
||||
PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, dict);
|
||||
nassertr(result != nullptr, nullptr);
|
||||
|
||||
((PyTypeObject *)result)->tp_new = Dtool_EnumType_New;
|
||||
((PyTypeObject *)result)->tp_str = Dtool_EnumType_Str;
|
||||
((PyTypeObject *)result)->tp_repr = Dtool_EnumType_Repr;
|
||||
|
||||
// Copy the names as instances of the above to the class dict.
|
||||
PyObject *empty_tuple = PyTuple_New(0);
|
||||
|
||||
// Copy the names as instances of the above to the class dict, and create a
|
||||
// reverse mapping in the _value2member_map_ dict.
|
||||
Py_ssize_t size = PyTuple_GET_SIZE(names);
|
||||
for (Py_ssize_t i = 0; i < size; ++i) {
|
||||
PyObject *item = PyTuple_GET_ITEM(names, i);
|
||||
PyObject *name = PyTuple_GET_ITEM(item, 0);
|
||||
PyObject *value = PyTuple_GET_ITEM(item, 1);
|
||||
PyObject *member = _PyObject_CallNoArg(result);
|
||||
PyObject *member = PyType_GenericNew((PyTypeObject *)result, empty_tuple, nullptr);
|
||||
PyObject_SetAttr(member, name_str, name);
|
||||
PyObject_SetAttr(member, name_sunder_str, name);
|
||||
PyObject_SetAttr(member, value_str, value);
|
||||
PyObject_SetAttr(member, value_sunder_str, value);
|
||||
PyObject_SetAttr(result, name, member);
|
||||
PyDict_SetItem(value2member, value, member);
|
||||
Py_DECREF(member);
|
||||
}
|
||||
Py_DECREF(names);
|
||||
Py_DECREF(value2member);
|
||||
Py_DECREF(empty_tuple);
|
||||
#endif
|
||||
|
||||
if (module != nullptr) {
|
||||
@ -665,7 +717,8 @@ PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) {
|
||||
|
||||
// We do expose a dictionay for dtool classes .. this should be removed at
|
||||
// some point..
|
||||
PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) {
|
||||
EXPCL_PYPANDA PyObject *
|
||||
Dtool_AddToDictionary(PyObject *self1, PyObject *args) {
|
||||
PyObject *self;
|
||||
PyObject *subject;
|
||||
PyObject *key;
|
||||
|
@ -179,19 +179,19 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
|
||||
|
||||
typedef std::map<std::string, Dtool_PyTypedObject *> Dtool_TypeMap;
|
||||
|
||||
Dtool_TypeMap *Dtool_GetGlobalTypeMap();
|
||||
EXPCL_PYPANDA Dtool_TypeMap *Dtool_GetGlobalTypeMap();
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer);
|
||||
EXPCL_PYPANDA void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer);
|
||||
|
||||
void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const std::string &function_name, bool const_ok, bool report_errors);
|
||||
EXPCL_PYPANDA void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const std::string &function_name, bool const_ok, bool report_errors);
|
||||
|
||||
bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer);
|
||||
EXPCL_PYPANDA bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer);
|
||||
|
||||
bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
|
||||
void **answer, const char *method_name);
|
||||
EXPCL_PYPANDA bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
|
||||
void **answer, const char *method_name);
|
||||
|
||||
template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into);
|
||||
template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef);
|
||||
@ -201,7 +201,7 @@ INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2);
|
||||
INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op);
|
||||
|
||||
// Functions related to error reporting.
|
||||
bool _Dtool_CheckErrorOccurred();
|
||||
EXPCL_PYPANDA bool _Dtool_CheckErrorOccurred();
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define Dtool_CheckErrorOccurred() (UNLIKELY(_PyErr_OCCURRED() != nullptr))
|
||||
@ -209,12 +209,12 @@ bool _Dtool_CheckErrorOccurred();
|
||||
#define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred()))
|
||||
#endif
|
||||
|
||||
PyObject *Dtool_Raise_AssertionError();
|
||||
PyObject *Dtool_Raise_TypeError(const char *message);
|
||||
PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name);
|
||||
PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute);
|
||||
EXPCL_PYPANDA PyObject *Dtool_Raise_AssertionError();
|
||||
EXPCL_PYPANDA PyObject *Dtool_Raise_TypeError(const char *message);
|
||||
EXPCL_PYPANDA PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name);
|
||||
EXPCL_PYPANDA PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute);
|
||||
|
||||
PyObject *_Dtool_Raise_BadArgumentsError();
|
||||
EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError();
|
||||
#ifdef NDEBUG
|
||||
// Define it to a function that just prints a generic message.
|
||||
#define Dtool_Raise_BadArgumentsError(x) _Dtool_Raise_BadArgumentsError()
|
||||
@ -226,9 +226,9 @@ PyObject *_Dtool_Raise_BadArgumentsError();
|
||||
// These functions are similar to Dtool_WrapValue, except that they also
|
||||
// contain code for checking assertions and exceptions when compiling with
|
||||
// NDEBUG mode on.
|
||||
PyObject *_Dtool_Return_None();
|
||||
PyObject *Dtool_Return_Bool(bool value);
|
||||
PyObject *_Dtool_Return(PyObject *value);
|
||||
EXPCL_PYPANDA PyObject *_Dtool_Return_None();
|
||||
EXPCL_PYPANDA PyObject *Dtool_Return_Bool(bool value);
|
||||
EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value);
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define Dtool_Return_None() (LIKELY(_PyErr_OCCURRED() == nullptr) ? (Py_INCREF(Py_None), Py_None) : nullptr)
|
||||
@ -241,19 +241,19 @@ PyObject *_Dtool_Return(PyObject *value);
|
||||
/**
|
||||
* Wrapper around Python 3.4's enum library, which does not have a C API.
|
||||
*/
|
||||
PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names,
|
||||
const char *module = nullptr);
|
||||
EXPCL_PYPANDA PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names,
|
||||
const char *module = nullptr);
|
||||
INLINE long Dtool_EnumValue_AsLong(PyObject *value);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int RunTimeType);
|
||||
EXPCL_PYPANDA PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int RunTimeType);
|
||||
|
||||
// DTool_CreatePyInstance .. wrapper function to finalize the existance of a
|
||||
// general dtool py instance..
|
||||
PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const);
|
||||
EXPCL_PYPANDA PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const);
|
||||
|
||||
// These template methods allow use when the Dtool_PyTypedObject is not known.
|
||||
// They require a get_class_type() to be defined for the class.
|
||||
@ -320,26 +320,26 @@ struct LibraryDef {
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def);
|
||||
EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def);
|
||||
#else
|
||||
PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename);
|
||||
EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename);
|
||||
#endif
|
||||
|
||||
// HACK.... Be carefull Dtool_BorrowThisReference This function can be used to
|
||||
// grab the "THIS" pointer from an object and use it Required to support fom
|
||||
// historical inharatence in the for of "is this instance of"..
|
||||
PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args);
|
||||
EXPCL_PYPANDA PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args);
|
||||
|
||||
#define DTOOL_PyObject_HashPointer DtoolInstance_HashPointer
|
||||
#define DTOOL_PyObject_ComparePointers DtoolInstance_ComparePointers
|
||||
|
||||
PyObject *
|
||||
EXPCL_PYPANDA PyObject *
|
||||
copy_from_make_copy(PyObject *self, PyObject *noargs);
|
||||
|
||||
PyObject *
|
||||
EXPCL_PYPANDA PyObject *
|
||||
copy_from_copy_constructor(PyObject *self, PyObject *noargs);
|
||||
|
||||
PyObject *
|
||||
EXPCL_PYPANDA PyObject *
|
||||
map_deepcopy_to_copy(PyObject *self, PyObject *args);
|
||||
|
||||
/**
|
||||
@ -348,14 +348,14 @@ map_deepcopy_to_copy(PyObject *self, PyObject *args);
|
||||
*/
|
||||
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args);
|
||||
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args, PyObject *kwds);
|
||||
bool Dtool_ExtractArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds, const char *keyword);
|
||||
bool Dtool_ExtractArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds);
|
||||
bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds, const char *keyword);
|
||||
bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds);
|
||||
EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds, const char *keyword);
|
||||
EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds);
|
||||
EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds, const char *keyword);
|
||||
EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
|
||||
PyObject *kwds);
|
||||
|
||||
/**
|
||||
* These functions convert a C++ value into the corresponding Python object.
|
||||
@ -390,7 +390,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value);
|
||||
template<class T1, class T2>
|
||||
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::pair<T1, T2> &value);
|
||||
|
||||
Dtool_PyTypedObject *Dtool_GetSuperBase();
|
||||
EXPCL_PYPANDA Dtool_PyTypedObject *Dtool_GetSuperBase();
|
||||
|
||||
#include "py_panda.I"
|
||||
|
||||
|
@ -49,12 +49,12 @@ struct Dtool_GeneratorWrapper {
|
||||
iternextfunc _iternext_func;
|
||||
};
|
||||
|
||||
Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name);
|
||||
Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name);
|
||||
Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name);
|
||||
Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name);
|
||||
PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func);
|
||||
PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
|
||||
EXPCL_PYPANDA Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name);
|
||||
EXPCL_PYPANDA Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name);
|
||||
EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name);
|
||||
EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name);
|
||||
EXPCL_PYPANDA PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func);
|
||||
EXPCL_PYPANDA PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
|
@ -808,9 +808,12 @@ if (COMPILER == "MSVC"):
|
||||
path = GetThirdpartyDir() + "vorbis/lib/{0}.lib".format(lib)
|
||||
LibName("VORBIS", path)
|
||||
if (PkgSkip("OPUS")==0):
|
||||
LibName("OPUS", GetThirdpartyDir() + "opus/lib/libogg_static.lib")
|
||||
LibName("OPUS", GetThirdpartyDir() + "opus/lib/libopus_static.lib")
|
||||
LibName("OPUS", GetThirdpartyDir() + "opus/lib/libopusfile_static.lib")
|
||||
IncDirectory("OPUS", GetThirdpartyDir() + "opus/include/opus")
|
||||
for lib in ('ogg', 'opus', 'opusfile'):
|
||||
path = GetThirdpartyDir() + "opus/lib/lib{0}_static.lib".format(lib)
|
||||
if not os.path.isfile(path):
|
||||
path = GetThirdpartyDir() + "opus/lib/{0}.lib".format(lib)
|
||||
LibName("OPUS", path)
|
||||
for pkg in MAYAVERSIONS:
|
||||
if (PkgSkip(pkg)==0):
|
||||
LibName(pkg, '"' + SDK[pkg] + '/lib/Foundation.lib"')
|
||||
|
@ -3362,8 +3362,13 @@ def CalcLocation(fn, ipath):
|
||||
|
||||
|
||||
def FindLocation(fn, ipath, pyabi=None):
|
||||
if (GetLinkAllStatic() and fn.endswith(".dll")):
|
||||
fn = fn[:-4] + ".lib"
|
||||
if GetLinkAllStatic():
|
||||
if fn.endswith(".dll"):
|
||||
fn = fn[:-4] + ".lib"
|
||||
elif fn.endswith(".pyd"):
|
||||
fn = "libpy.panda3d." \
|
||||
+ os.path.splitext(fn[:-4] + GetExtensionSuffix())[0] + ".lib"
|
||||
|
||||
loc = CalcLocation(fn, ipath)
|
||||
base, ext = os.path.splitext(fn)
|
||||
|
||||
|
@ -55,9 +55,9 @@ AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
|
||||
|
||||
_app = panda_android_app;
|
||||
|
||||
GraphicsWindowInputDevice device =
|
||||
GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
|
||||
PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
|
||||
add_input_device(device);
|
||||
_input = device;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -486,22 +486,22 @@ handle_key_event(const AInputEvent *event) {
|
||||
/*
|
||||
int32_t meta = AKeyEvent_getMetaState(event);
|
||||
if (meta | AMETA_ALT_ON) {
|
||||
_input_devices[0].button_down(KeyboardButton.alt());
|
||||
_input->button_down(KeyboardButton.alt());
|
||||
}
|
||||
if (meta | AMETA_ALT_LEFT_ON) {
|
||||
_input_devices[0].button_down(KeyboardButton.lalt());
|
||||
_input->button_down(KeyboardButton.lalt());
|
||||
}
|
||||
if (meta | AMETA_ALT_RIGHT_ON) {
|
||||
_input_devices[0].button_down(KeyboardButton.ralt());
|
||||
_input->button_down(KeyboardButton.ralt());
|
||||
}
|
||||
if (meta | AMETA_SHIFT_ON) {
|
||||
_input_devices[0].button_down(KeyboardButton.shift());
|
||||
_input->button_down(KeyboardButton.shift());
|
||||
}
|
||||
if (meta | AMETA_SHIFT_LEFT_ON) {
|
||||
_input_devices[0].button_down(KeyboardButton.lshift());
|
||||
_input->button_down(KeyboardButton.lshift());
|
||||
}
|
||||
if (meta | AMETA_SHIFT_RIGHT_ON) {
|
||||
_input_devices[0].button_down(KeyboardButton.rshift());
|
||||
_input->button_down(KeyboardButton.rshift());
|
||||
}*/
|
||||
|
||||
int32_t keycode = AKeyEvent_getKeyCode(event);
|
||||
@ -517,12 +517,12 @@ handle_key_event(const AInputEvent *event) {
|
||||
int32_t action = AKeyEvent_getAction(event);
|
||||
if (action == AKEY_EVENT_ACTION_DOWN) {
|
||||
if (AKeyEvent_getRepeatCount(event) > 0) {
|
||||
_input_devices[0].button_resume_down(button);
|
||||
_input->button_resume_down(button);
|
||||
} else {
|
||||
_input_devices[0].button_down(button);
|
||||
_input->button_down(button);
|
||||
}
|
||||
} else if (action == AKEY_EVENT_ACTION_UP) {
|
||||
_input_devices[0].button_up(button);
|
||||
_input->button_up(button);
|
||||
}
|
||||
// TODO AKEY_EVENT_ACTION_MULTIPLE
|
||||
|
||||
@ -549,16 +549,16 @@ handle_motion_event(const AInputEvent *event) {
|
||||
if (changed != 0) {
|
||||
if (changed & AMOTION_EVENT_BUTTON_PRIMARY) {
|
||||
if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) {
|
||||
_input_devices[0].button_down(MouseButton::one());
|
||||
_input->button_down(MouseButton::one());
|
||||
} else {
|
||||
_input_devices[0].button_up(MouseButton::one());
|
||||
_input->button_up(MouseButton::one());
|
||||
}
|
||||
}
|
||||
if (changed & AMOTION_EVENT_BUTTON_SECONDARY) {
|
||||
if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) {
|
||||
_input_devices[0].button_down(MouseButton::three());
|
||||
_input->button_down(MouseButton::three());
|
||||
} else {
|
||||
_input_devices[0].button_up(MouseButton::three());
|
||||
_input->button_up(MouseButton::three());
|
||||
}
|
||||
}
|
||||
_mouse_button_state = button_state;
|
||||
@ -568,7 +568,7 @@ handle_motion_event(const AInputEvent *event) {
|
||||
float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
|
||||
float y = AMotionEvent_getY(event, 0) - _app->contentRect.top;
|
||||
|
||||
_input_devices[0].set_pointer_in_window(x, y);
|
||||
_input->set_pointer_in_window(x, y);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ private:
|
||||
|
||||
int32_t _mouse_button_state;
|
||||
|
||||
const ARect *rect;
|
||||
GraphicsWindowInputDevice *_input;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
|
@ -57,6 +57,11 @@ enum QuirkBits {
|
||||
|
||||
// ABS_THROTTLE maps to rudder
|
||||
QB_rudder_from_throttle = 16,
|
||||
|
||||
// Special handling for Steam Controller, which has many peculiarities.
|
||||
// We only connect it if it is reporting any events, because when Steam is
|
||||
// running, the Steam controller is muted in favour of a dummy Xbox device.
|
||||
QB_steam_controller = 32,
|
||||
};
|
||||
|
||||
static const struct DeviceMapping {
|
||||
@ -71,8 +76,14 @@ static const struct DeviceMapping {
|
||||
{0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle | QB_rudder_from_throttle},
|
||||
// Xbox 360 Wireless Controller
|
||||
{0x045e, 0x0719, InputDevice::DeviceClass::gamepad, QB_connect_if_nonzero},
|
||||
// Steam Controller (wired)
|
||||
{0x28de, 0x1102, InputDevice::DeviceClass::unknown, QB_steam_controller},
|
||||
// Steam Controller (wireless)
|
||||
{0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
|
||||
// Jess Tech Colour Rumble Pad
|
||||
{0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
|
||||
// Trust GXT 24
|
||||
{0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
|
||||
// 3Dconnexion Space Traveller 3D Mouse
|
||||
{0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
|
||||
// 3Dconnexion Space Pilot 3D Mouse
|
||||
@ -110,7 +121,8 @@ EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) :
|
||||
_dpad_left_button(-1),
|
||||
_dpad_up_button(-1),
|
||||
_ltrigger_code(-1),
|
||||
_rtrigger_code(-1) {
|
||||
_rtrigger_code(-1),
|
||||
_quirks(0) {
|
||||
|
||||
char path[64];
|
||||
sprintf(path, "/dev/input/event%zd", index);
|
||||
@ -208,6 +220,26 @@ do_set_vibration(double strong, double weak) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case for Steam controllers; called if a Steam virtual device has
|
||||
* just been disconnected, and this is currently an inactive Steam Controller
|
||||
* previously blocked by Steam, waiting to be reactivated.
|
||||
* Returns true if the device has just been reconnected.
|
||||
*/
|
||||
bool EvdevInputDevice::
|
||||
reactivate_steam_controller() {
|
||||
LightMutexHolder holder(_lock);
|
||||
if (!_is_connected && (_quirks & QB_steam_controller) != 0) {
|
||||
// Just check to make sure the device is still readable.
|
||||
process_events();
|
||||
if (_fd != -1) {
|
||||
_is_connected = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the input device for new activity, to ensure it contains the latest
|
||||
* events. This will only have any effect for some types of input devices;
|
||||
@ -219,7 +251,7 @@ do_poll() {
|
||||
while (process_events()) {}
|
||||
|
||||
// If we got events, we are obviously connected. Mark us so.
|
||||
if (!_is_connected) {
|
||||
if (!_is_connected && _fd != -1) {
|
||||
_is_connected = true;
|
||||
if (_manager != nullptr) {
|
||||
_manager->add_device(this);
|
||||
@ -297,6 +329,24 @@ init_device() {
|
||||
++mapping;
|
||||
}
|
||||
|
||||
// The Steam Controller reports as multiple devices, one of which a gamepad.
|
||||
if (quirks & QB_steam_controller) {
|
||||
if (test_bit(BTN_GAMEPAD, keys)) {
|
||||
_device_class = DeviceClass::gamepad;
|
||||
|
||||
// If we have a virtual gamepad on the system, then careful: if Steam is
|
||||
// running, it may disable its own gamepad in favour of the virtual
|
||||
// device it registers. If the virtual device is present, we will only
|
||||
// register this gamepad as connected when it registers input.
|
||||
if (_manager->has_virtual_device(0x28de, 0x11ff)) {
|
||||
device_cat.debug()
|
||||
<< "Detected Steam virtual gamepad, disabling Steam Controller\n";
|
||||
quirks |= QB_connect_if_nonzero;
|
||||
}
|
||||
}
|
||||
}
|
||||
_quirks = quirks;
|
||||
|
||||
// Try to detect which type of device we have here
|
||||
if (_device_class == DeviceClass::unknown) {
|
||||
int device_scores[(size_t)DeviceClass::spatial_mouse] = {0};
|
||||
@ -376,7 +426,7 @@ init_device() {
|
||||
for (int i = 0; i <= KEY_MAX; ++i) {
|
||||
if (test_bit(i, keys)) {
|
||||
ButtonState button;
|
||||
button.handle = map_button(i, _device_class);
|
||||
button.handle = map_button(i, _device_class, quirks);
|
||||
|
||||
int button_index = (int)_buttons.size();
|
||||
if (button.handle == ButtonHandle::none()) {
|
||||
@ -525,6 +575,18 @@ init_device() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABS_HAT2X:
|
||||
if (quirks & QB_steam_controller) {
|
||||
axis = InputDevice::Axis::right_trigger;
|
||||
have_analog_triggers = true;
|
||||
}
|
||||
break;
|
||||
case ABS_HAT2Y:
|
||||
if (quirks & QB_steam_controller) {
|
||||
axis = InputDevice::Axis::left_trigger;
|
||||
have_analog_triggers = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the initial value and ranges.
|
||||
@ -738,7 +800,7 @@ process_events() {
|
||||
* Static function to map an evdev code to a ButtonHandle.
|
||||
*/
|
||||
ButtonHandle EvdevInputDevice::
|
||||
map_button(int code, DeviceClass device_class) {
|
||||
map_button(int code, DeviceClass device_class, int quirks) {
|
||||
if (code >= 0 && code < 0x80) {
|
||||
// See linux/input.h for the source of this mapping.
|
||||
static const ButtonHandle keyboard_map[] = {
|
||||
@ -895,7 +957,11 @@ map_button(int code, DeviceClass device_class) {
|
||||
}
|
||||
|
||||
} else if ((code & 0xfff0) == BTN_JOYSTICK) {
|
||||
if (device_class == DeviceClass::gamepad) {
|
||||
if (quirks & QB_steam_controller) {
|
||||
// BTN_THUMB and BTN_THUMB2 detect touching the touchpads.
|
||||
return ButtonHandle::none();
|
||||
|
||||
} else if (device_class == DeviceClass::gamepad) {
|
||||
// Based on "Jess Tech Colour Rumble Pad"
|
||||
static const ButtonHandle mapping[] = {
|
||||
GamepadButton::face_x(),
|
||||
@ -989,6 +1055,13 @@ map_button(int code, DeviceClass device_class) {
|
||||
case BTN_TRIGGER_HAPPY4:
|
||||
return GamepadButton::dpad_down();
|
||||
|
||||
// The next two are for the Steam Controller's grip buttons.
|
||||
case BTN_GEAR_DOWN:
|
||||
return GamepadButton::lgrip();
|
||||
|
||||
case BTN_GEAR_UP:
|
||||
return GamepadButton::rgrip();
|
||||
|
||||
default:
|
||||
return ButtonHandle::none();
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ public:
|
||||
EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index);
|
||||
virtual ~EvdevInputDevice();
|
||||
|
||||
bool reactivate_steam_controller();
|
||||
|
||||
private:
|
||||
virtual void do_set_vibration(double strong, double weak);
|
||||
virtual void do_poll();
|
||||
@ -41,6 +43,7 @@ private:
|
||||
LinuxInputDeviceManager *_manager;
|
||||
|
||||
int _fd;
|
||||
int _quirks;
|
||||
size_t _index;
|
||||
|
||||
bool _can_write;
|
||||
@ -64,7 +67,9 @@ private:
|
||||
int _rtrigger_code;
|
||||
|
||||
public:
|
||||
static ButtonHandle map_button(int code, DeviceClass device_class = DeviceClass::unknown);
|
||||
static ButtonHandle map_button(int code,
|
||||
DeviceClass device_class = DeviceClass::unknown,
|
||||
int quirks = 0);
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
@ -75,6 +80,10 @@ public:
|
||||
register_type(_type_handle, "EvdevInputDevice",
|
||||
InputDevice::get_class_type());
|
||||
}
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
|
@ -200,6 +200,55 @@ consider_add_js_device(size_t js_index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the "virtual" input devices on the system to check whether one with
|
||||
* the given vendor and product ID exists.
|
||||
*/
|
||||
bool LinuxInputDeviceManager::
|
||||
has_virtual_device(unsigned short vendor_id, unsigned short product_id) const {
|
||||
char path[294];
|
||||
sprintf(path, "/sys/devices/virtual/input");
|
||||
|
||||
DIR *dir = opendir(path);
|
||||
if (dir != nullptr) {
|
||||
dirent *entry = readdir(dir);
|
||||
while (entry != nullptr) {
|
||||
if (entry->d_name[0] != 'i') {
|
||||
entry = readdir(dir);
|
||||
continue;
|
||||
}
|
||||
FILE *f;
|
||||
|
||||
char vendor[5] = {0};
|
||||
sprintf(path, "/sys/devices/virtual/input/%s/id/vendor", entry->d_name);
|
||||
f = fopen(path, "r");
|
||||
if (f) {
|
||||
fgets(vendor, sizeof(vendor), f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
char product[5] = {0};
|
||||
sprintf(path, "/sys/devices/virtual/input/%s/id/product", entry->d_name);
|
||||
f = fopen(path, "r");
|
||||
if (f) {
|
||||
fgets(product, sizeof(product), f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (vendor[0] && std::stoi(std::string(vendor), nullptr, 16) == (int)vendor_id &&
|
||||
product[0] && std::stoi(std::string(product), nullptr, 16) == (int)product_id) {
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
entry = readdir(dir);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the system to see if there are any new devices. In some
|
||||
* implementations this is a no-op.
|
||||
@ -243,6 +292,7 @@ update() {
|
||||
LightMutexHolder holder(_lock);
|
||||
|
||||
// Iterate over the events in the buffer.
|
||||
bool removed_steam_virtual_device = false;
|
||||
char *ptr = buffer;
|
||||
char *end = buffer + avail;
|
||||
while (ptr < end) {
|
||||
@ -270,6 +320,12 @@ update() {
|
||||
device_cat.debug()
|
||||
<< "Removed input device " << *device << "\n";
|
||||
}
|
||||
|
||||
// Check for Steam virtual device; see comment below.
|
||||
if (device->get_vendor_id() == 0x28de &&
|
||||
device->get_product_id() == 0x11ff) {
|
||||
removed_steam_virtual_device = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -290,6 +346,25 @@ update() {
|
||||
|
||||
ptr += sizeof(inotify_event) + event->len;
|
||||
}
|
||||
|
||||
// If the Steam virtual device was just disconnected, the user may have just
|
||||
// shut down Steam, and we need to reactivate the real Steam Controller
|
||||
// device that was previously suppressed by Steam.
|
||||
if (removed_steam_virtual_device) {
|
||||
inactive_devices = _inactive_devices;
|
||||
|
||||
for (size_t i = 0; i < inactive_devices.size(); ++i) {
|
||||
InputDevice *device = inactive_devices[i];
|
||||
if (device != nullptr && device->is_of_type(EvdevInputDevice::get_class_type())) {
|
||||
PT(EvdevInputDevice) evdev_device = (EvdevInputDevice *)device;
|
||||
if (evdev_device->reactivate_steam_controller()) {
|
||||
_inactive_devices.remove_device(device);
|
||||
_connected_devices.add_device(device);
|
||||
throw_event("connect-device", device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PHAVE_LINUX_INPUT_H
|
||||
|
@ -30,6 +30,9 @@ private:
|
||||
InputDevice *consider_add_evdev_device(size_t index);
|
||||
InputDevice *consider_add_js_device(size_t index);
|
||||
|
||||
public:
|
||||
bool has_virtual_device(unsigned short vendor_id, unsigned short product_id) const;
|
||||
|
||||
virtual void update();
|
||||
|
||||
protected:
|
||||
|
@ -113,6 +113,7 @@ open_device() {
|
||||
ioctl(_fd, JSIOCGNAME(sizeof(name)), name);
|
||||
_name = name;
|
||||
|
||||
bool emulate_dpad = true;
|
||||
bool have_analog_triggers = false;
|
||||
|
||||
// Get the number of axes.
|
||||
@ -138,6 +139,8 @@ open_device() {
|
||||
_device_class = DeviceClass::gamepad;
|
||||
} else if (handle == GamepadButton::trigger()) {
|
||||
_device_class = DeviceClass::flight_stick;
|
||||
} else if (handle == GamepadButton::dpad_left()) {
|
||||
emulate_dpad = false;
|
||||
} else if (handle == GamepadButton::ltrigger()) {
|
||||
_ltrigger_button = i;
|
||||
} else if (handle == GamepadButton::rtrigger()) {
|
||||
@ -220,7 +223,7 @@ open_device() {
|
||||
break;
|
||||
|
||||
case ABS_HAT0X:
|
||||
if (_dpad_left_button == -1) {
|
||||
if (emulate_dpad) {
|
||||
// Emulate D-Pad or hat switch.
|
||||
_dpad_x_axis = i;
|
||||
_dpad_left_button = (int)_buttons.size();
|
||||
@ -236,7 +239,7 @@ open_device() {
|
||||
break;
|
||||
|
||||
case ABS_HAT0Y:
|
||||
if (_dpad_up_button == -1) {
|
||||
if (emulate_dpad) {
|
||||
// Emulate D-Pad.
|
||||
_dpad_y_axis = i;
|
||||
_dpad_up_button = (int)_buttons.size();
|
||||
@ -251,6 +254,18 @@ open_device() {
|
||||
}
|
||||
break;
|
||||
|
||||
case ABS_HAT2X:
|
||||
if (_device_class == DeviceClass::gamepad) {
|
||||
axis = InputDevice::Axis::right_trigger;
|
||||
}
|
||||
break;
|
||||
|
||||
case ABS_HAT2Y:
|
||||
if (_device_class == DeviceClass::gamepad) {
|
||||
axis = InputDevice::Axis::left_trigger;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (device_cat.is_debug()) {
|
||||
device_cat.debug() << "Unmapped /dev/input/js" << _index
|
||||
@ -278,7 +293,7 @@ open_device() {
|
||||
|
||||
if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
|
||||
// Emulate analog triggers.
|
||||
_ltrigger_control = (int)_axes.size();
|
||||
_ltrigger_axis = (int)_axes.size();
|
||||
add_axis(Axis::left_trigger, 0, 1, false);
|
||||
add_axis(Axis::right_trigger, 0, 1, false);
|
||||
} else {
|
||||
@ -398,9 +413,9 @@ process_events() {
|
||||
|
||||
if (events[i].type & JS_EVENT_BUTTON) {
|
||||
if (index == _ltrigger_button) {
|
||||
axis_changed(_ltrigger_control, events[i].value);
|
||||
axis_changed(_ltrigger_axis, events[i].value);
|
||||
} else if (index == _rtrigger_button) {
|
||||
axis_changed(_ltrigger_control + 1, events[i].value);
|
||||
axis_changed(_ltrigger_axis + 1, events[i].value);
|
||||
}
|
||||
button_changed(index, (events[i].value != 0));
|
||||
|
||||
|
@ -50,7 +50,7 @@ private:
|
||||
int _dpad_up_button;
|
||||
|
||||
// This is used for axis emulation.
|
||||
int _ltrigger_control;
|
||||
int _ltrigger_axis;
|
||||
int _ltrigger_button;
|
||||
int _rtrigger_button;
|
||||
|
||||
|
@ -82,6 +82,47 @@ WinInputDeviceManager() :
|
||||
device_cat.warning()
|
||||
<< "Failed to register raw input devices.\n";
|
||||
}
|
||||
|
||||
// Do we have any XInput devices plugged in now?
|
||||
int num_xinput = 0;
|
||||
HANDLE xinput_handle;
|
||||
RAWINPUTDEVICELIST devices[64];
|
||||
UINT num_devices = 64;
|
||||
num_devices = GetRawInputDeviceList(devices, &num_devices, sizeof(RAWINPUTDEVICELIST));
|
||||
if (num_devices == (UINT)-1) {
|
||||
return;
|
||||
}
|
||||
for (UINT i = 0; i < num_devices; ++i) {
|
||||
if (devices[i].dwType != RIM_TYPEHID) {
|
||||
continue;
|
||||
}
|
||||
HANDLE handle = devices[i].hDevice;
|
||||
UINT size;
|
||||
if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *path = (char *)alloca(size);
|
||||
if (path == nullptr ||
|
||||
GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strstr(path, "&IG_") != nullptr) {
|
||||
xinput_handle = handle;
|
||||
++num_xinput;
|
||||
}
|
||||
}
|
||||
if (num_xinput == 1) {
|
||||
// There's only one XInput device, so we know which one it is.
|
||||
on_input_device_arrival(xinput_handle);
|
||||
} else if (num_xinput > 0) {
|
||||
// Just poll all the XInput devices.
|
||||
_xinput_device0.detect(this);
|
||||
_xinput_device1.detect(this);
|
||||
_xinput_device2.detect(this);
|
||||
_xinput_device3.detect(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,7 +318,6 @@ on_input_device_arrival(HANDLE handle) {
|
||||
if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr) {
|
||||
// This is a device we should handle via the XInput API. Check which of
|
||||
// the four players was the lucky one.
|
||||
WinRawInputDevice idev(this, path);
|
||||
if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) {
|
||||
add_device(&_xinput_device0);
|
||||
}
|
||||
|
@ -273,6 +273,11 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
|
||||
// Well, it claims to be a gamepad...
|
||||
_device_class = DeviceClass::gamepad;
|
||||
}
|
||||
//TODO: better solution for this
|
||||
if (_vendor_id == 0x0079 && _product_id == 0x0006) {
|
||||
// Trust GXT 24
|
||||
_device_class = DeviceClass::gamepad;
|
||||
}
|
||||
|
||||
// Mice
|
||||
} else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
|
||||
@ -599,13 +604,16 @@ on_removal() {
|
||||
_is_connected = false;
|
||||
_handle = nullptr;
|
||||
if (_preparsed != nullptr) {
|
||||
delete _preparsed;
|
||||
free(_preparsed);
|
||||
_preparsed = nullptr;
|
||||
}
|
||||
_indices.clear();
|
||||
_report_buttons.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by InputDeviceManager when raw input is received for this device.
|
||||
*/
|
||||
void WinRawInputDevice::
|
||||
on_input(PRAWINPUT input) {
|
||||
nassertv(input != nullptr);
|
||||
@ -616,59 +624,68 @@ on_input(PRAWINPUT input) {
|
||||
return;
|
||||
}
|
||||
|
||||
PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count);
|
||||
nassertv(data != nullptr);
|
||||
ULONG count;
|
||||
|
||||
LightMutexHolder holder(_lock);
|
||||
|
||||
for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
|
||||
// The first byte is the report identifier. We need it to figure out
|
||||
// which buttons are off, since each report only contains the buttons that
|
||||
// are "on".
|
||||
UCHAR report_id = ptr[0];
|
||||
BitArray unset_buttons = _report_buttons[report_id];
|
||||
process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
|
||||
ptr += input->data.hid.dwSizeHid;
|
||||
}
|
||||
}
|
||||
|
||||
count = _max_data_count;
|
||||
NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, (PCHAR)ptr, input->data.hid.dwSizeHid);
|
||||
if (status == HIDP_STATUS_SUCCESS) {
|
||||
for (ULONG di = 0; di < count; ++di) {
|
||||
if (data[di].DataIndex != _hat_data_index) {
|
||||
const Index &idx = _indices[data[di].DataIndex];
|
||||
if (idx._axis >= 0) {
|
||||
if (idx._signed) {
|
||||
axis_changed(idx._axis, (SHORT)data[di].RawValue);
|
||||
} else {
|
||||
axis_changed(idx._axis, data[di].RawValue);
|
||||
}
|
||||
/**
|
||||
* Processes a single HID report. Assumes the lock is held.
|
||||
*/
|
||||
void WinRawInputDevice::
|
||||
process_report(PCHAR ptr, size_t size) {
|
||||
// The first byte is the report identifier. We need it to figure out which
|
||||
// buttons are off, since each report only contains the "on" buttons.
|
||||
UCHAR report_id = ptr[0];
|
||||
BitArray unset_buttons;
|
||||
|
||||
if (report_id < _report_buttons.size()) {
|
||||
unset_buttons = _report_buttons[report_id];
|
||||
}
|
||||
|
||||
PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count);
|
||||
nassertv(data != nullptr);
|
||||
|
||||
ULONG count = _max_data_count;
|
||||
NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
|
||||
if (status == HIDP_STATUS_SUCCESS) {
|
||||
for (ULONG di = 0; di < count; ++di) {
|
||||
if (data[di].DataIndex != _hat_data_index) {
|
||||
const Index &idx = _indices[data[di].DataIndex];
|
||||
if (idx._axis >= 0) {
|
||||
if (idx._signed) {
|
||||
axis_changed(idx._axis, (SHORT)data[di].RawValue);
|
||||
} else {
|
||||
axis_changed(idx._axis, data[di].RawValue);
|
||||
}
|
||||
if (idx._button >= 0) {
|
||||
unset_buttons.clear_bit(idx._button);
|
||||
button_changed(idx._button, (data[di].On != FALSE));
|
||||
}
|
||||
} else {
|
||||
int value = (int)data[di].RawValue - _hat_data_minimum;
|
||||
button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left
|
||||
button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right
|
||||
button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down
|
||||
button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up
|
||||
}
|
||||
if (idx._button >= 0) {
|
||||
unset_buttons.clear_bit(idx._button);
|
||||
button_changed(idx._button, (data[di].On != FALSE));
|
||||
}
|
||||
} else {
|
||||
int value = (int)data[di].RawValue - _hat_data_minimum;
|
||||
button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left
|
||||
button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right
|
||||
button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down
|
||||
button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up
|
||||
}
|
||||
|
||||
// Now unset the buttons in this report that aren't pressed.
|
||||
int button_index = unset_buttons.get_lowest_on_bit();
|
||||
while (button_index >= 0) {
|
||||
button_changed(button_index, false);
|
||||
unset_buttons.clear_bit(button_index);
|
||||
button_index = unset_buttons.get_lowest_on_bit();
|
||||
}
|
||||
} else if (device_cat.is_spam()) {
|
||||
device_cat.spam()
|
||||
<< "Failed to get data from raw device " << _path
|
||||
<< " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n";
|
||||
}
|
||||
|
||||
ptr += input->data.hid.dwSizeHid;
|
||||
// Now unset the buttons in this report that aren't pressed.
|
||||
int button_index = unset_buttons.get_lowest_on_bit();
|
||||
while (button_index >= 0) {
|
||||
button_changed(button_index, false);
|
||||
unset_buttons.clear_bit(button_index);
|
||||
button_index = unset_buttons.get_lowest_on_bit();
|
||||
}
|
||||
} else if (device_cat.is_spam()) {
|
||||
device_cat.spam()
|
||||
<< "Failed to get data from raw device " << _path
|
||||
<< " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
bool on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name);
|
||||
void on_removal();
|
||||
void on_input(PRAWINPUT input);
|
||||
void process_report(PCHAR ptr, size_t size);
|
||||
|
||||
private:
|
||||
virtual void do_poll();
|
||||
|
@ -161,6 +161,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (get_state(_index, &state) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extra check for VID/PID if we have it, just to be sure.
|
||||
if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) ||
|
||||
(caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) {
|
||||
@ -205,6 +209,10 @@ check_arrival(const RID_DEVICE_INFO &info, DEVINST inst,
|
||||
*/
|
||||
void XInputDevice::
|
||||
detect(InputDeviceManager *mgr) {
|
||||
if (!_initialized) {
|
||||
nassertv_always(init_xinput());
|
||||
}
|
||||
|
||||
bool connected = false;
|
||||
|
||||
XINPUT_CAPABILITIES_EX caps = {0};
|
||||
@ -225,6 +233,10 @@ detect(InputDeviceManager *mgr) {
|
||||
_is_connected = connected;
|
||||
|
||||
if (connected) {
|
||||
_name = "XInput Device #";
|
||||
_name += format_string(_index + 1);
|
||||
_vendor_id = caps.VendorID;
|
||||
_product_id = caps.ProductID;
|
||||
init_device(caps, state);
|
||||
mgr->add_device(this);
|
||||
} else {
|
||||
|
@ -478,15 +478,6 @@ ConfigVariableBool sync_video
|
||||
"cheesy estimate of scene complexity. Some drivers may ignore "
|
||||
"this request."));
|
||||
|
||||
ConfigVariableBool basic_shaders_only
|
||||
("basic-shaders-only", false,
|
||||
PRC_DESC("Set this to true if you aren't interested in shader model three "
|
||||
"and beyond. Setting this flag will cause panda to disable "
|
||||
"bleeding-edge shader functionality which tends to be unreliable "
|
||||
"or broken. At some point, when functionality that is currently "
|
||||
"flaky becomes reliable, we may expand the definition of what "
|
||||
"constitutes 'basic' shaders."));
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
* the functions or classes in this library can be used. Normally it will be
|
||||
|
@ -108,7 +108,6 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableDouble pixel_zoom;
|
||||
|
||||
extern EXPCL_PANDA_DISPLAY ConfigVariableColor background_color;
|
||||
extern EXPCL_PANDA_DISPLAY ConfigVariableBool sync_video;
|
||||
extern EXPCL_PANDA_DISPLAY ConfigVariableBool basic_shaders_only;
|
||||
|
||||
extern EXPCL_PANDA_DISPLAY void init_libdisplay();
|
||||
|
||||
|
@ -1255,7 +1255,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
|
||||
return &(_scene_setup->get_camera_transform()->get_mat());
|
||||
}
|
||||
case Shader::SMO_model_to_view: {
|
||||
return &(_inv_cs_transform->compose(_internal_transform)->get_mat());
|
||||
t = _inv_cs_transform->compose(_internal_transform)->get_mat();
|
||||
return &t;
|
||||
}
|
||||
case Shader::SMO_model_to_apiview: {
|
||||
return &(_internal_transform->get_mat());
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "modelNode.h"
|
||||
#include "animBundleNode.h"
|
||||
#include "animChannelMatrixXfmTable.h"
|
||||
#include "characterJointEffect.h"
|
||||
#include "characterJoint.h"
|
||||
#include "character.h"
|
||||
#include "string_utils.h"
|
||||
@ -159,16 +158,6 @@ convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent,
|
||||
convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal);
|
||||
|
||||
} else {
|
||||
// Is this a ModelNode that represents an exposed joint? If so, skip it,
|
||||
// as we'll take care of it when building the joint hierarchy.
|
||||
if (node->get_type() == ModelNode::get_class_type()) {
|
||||
ModelNode *model_node = (ModelNode *)node;
|
||||
if (model_node->get_preserve_transform() == ModelNode::PT_net &&
|
||||
model_node->has_effect(CharacterJointEffect::get_class_type())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Just a generic node.
|
||||
EggGroup *egg_group = new EggGroup(node->get_name());
|
||||
egg_parent->add_child(egg_group);
|
||||
@ -368,17 +357,6 @@ convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, Charac
|
||||
EggGroup *joint = new EggGroup(bundleNode->get_name());
|
||||
joint->add_matrix4(transformd);
|
||||
joint->set_group_type(EggGroup::GT_joint);
|
||||
|
||||
// Is this joint exposed?
|
||||
NodePathCollection coll = character_joint->get_net_transforms();
|
||||
for (size_t i = 0; i < coll.size(); ++i) {
|
||||
const NodePath &np = coll[i];
|
||||
if (np.get_name() == bundleNode->get_name() && np.node()->is_of_type(ModelNode::get_class_type())) {
|
||||
joint->set_dcs_type(EggGroup::DC_net);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
joint_group = joint;
|
||||
egg_parent->add_child(joint_group);
|
||||
if (joint_map != nullptr) {
|
||||
@ -409,33 +387,16 @@ convert_character_node(Character *node, const WorkingNodePath &node_path,
|
||||
|
||||
// A sequence node gets converted to an ordinary EggGroup, we only apply the
|
||||
// appropriate switch attributes to turn it into a sequence.
|
||||
// We have to use DT_structured since it is the only mode that preserves the
|
||||
// node hierarchy, including LODNodes and CollisionNodes that may be under
|
||||
// this Character node.
|
||||
EggGroup *egg_group = new EggGroup(node->get_name());
|
||||
egg_group->set_dart_type(EggGroup::DT_structured);
|
||||
egg_parent->add_child(egg_group);
|
||||
apply_node_properties(egg_group, node);
|
||||
|
||||
CharacterJointMap joint_map;
|
||||
bool is_structured = false;
|
||||
|
||||
int num_children = node->get_num_children();
|
||||
for (int i = 0; i < num_children; i++) {
|
||||
PandaNode *child = node->get_child(i);
|
||||
convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal, &joint_map);
|
||||
|
||||
TypeHandle type = child->get_type();
|
||||
if (child->get_num_children() > 0 ||
|
||||
(type != GeomNode::get_class_type() && type != ModelNode::get_class_type())) {
|
||||
is_structured = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We have to use DT_structured if it is necessary to preserve any node
|
||||
// hierarchy, such as LODNodes and CollisionNodes that may be under this
|
||||
// Character node.
|
||||
if (is_structured) {
|
||||
egg_group->set_dart_type(EggGroup::DT_structured);
|
||||
} else {
|
||||
egg_group->set_dart_type(EggGroup::DT_default);
|
||||
}
|
||||
recurse_nodes(node_path, egg_group, has_decal, &joint_map);
|
||||
|
||||
// turn it into a switch.. egg_group->set_switch_flag(true);
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
forcetype DConfig
|
||||
|
||||
forcetype PTA_uchar
|
||||
forcetype CPTA_uchar
|
||||
forcetype PTA_float
|
||||
|
@ -157,6 +157,16 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
|
||||
break;
|
||||
case 2: // gl_Normal
|
||||
loc = CA_normal;
|
||||
if (cgGetParameterColumns(p) == 4) {
|
||||
// Don't declare vtx_normal with 4 coordinates; it results in it
|
||||
// reading the w coordinate from random memory.
|
||||
GLCAT.error()
|
||||
<< "Cg varying " << cgGetParameterName(p);
|
||||
if (cgGetParameterSemantic(p)) {
|
||||
GLCAT.error(false) << " : " << cgGetParameterSemantic(p);
|
||||
}
|
||||
GLCAT.error(false) << " should be declared as float4, not float3!\n";
|
||||
}
|
||||
break;
|
||||
case 3: // gl_Color
|
||||
loc = CA_color;
|
||||
|
@ -1684,6 +1684,8 @@ reset() {
|
||||
}
|
||||
}
|
||||
|
||||
Shader::set_default_caps(_shader_caps);
|
||||
|
||||
} else if (_supports_glsl) {
|
||||
// No, but we do support GLSL...
|
||||
_shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV;
|
||||
|
@ -511,6 +511,15 @@ ConfigVariableBool stereo_lens_old_convergence
|
||||
"old, incorrect behavior, this may be set to 'true' to switch "
|
||||
"back to the old calculation."));
|
||||
|
||||
ConfigVariableBool basic_shaders_only
|
||||
("basic-shaders-only", false,
|
||||
PRC_DESC("Set this to true if you aren't interested in shader model three "
|
||||
"and beyond. Setting this flag will cause panda to disable "
|
||||
"bleeding-edge shader functionality which tends to be unreliable "
|
||||
"or broken. At some point, when functionality that is currently "
|
||||
"flaky becomes reliable, we may expand the definition of what "
|
||||
"constitutes 'basic' shaders."));
|
||||
|
||||
ConfigVariableString cg_glsl_version
|
||||
("cg-glsl-version", "",
|
||||
PRC_DESC("If this is set, it forces the Cg compiler to generate GLSL "
|
||||
|
@ -87,6 +87,7 @@ extern EXPCL_PANDA_GOBJ ConfigVariableDouble async_load_delay;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableInt lens_geom_segments;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableBool stereo_lens_old_convergence;
|
||||
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableBool basic_shaders_only;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableString cg_glsl_version;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableBool glsl_preprocess;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableInt glsl_include_recursion_limit;
|
||||
|
@ -1589,6 +1589,15 @@ get_compiled(unsigned int &format, string &binary) const {
|
||||
return !binary.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the graphics back-end to specify the caps with which we will
|
||||
* likely want to be compiling our shaders.
|
||||
*/
|
||||
void Shader::
|
||||
set_default_caps(const ShaderCaps &caps) {
|
||||
_default_caps = caps;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CG
|
||||
/**
|
||||
*
|
||||
@ -2364,9 +2373,15 @@ Shader(ShaderLanguage lang) :
|
||||
_cg_fprofile = CG_PROFILE_UNKNOWN;
|
||||
_cg_gprofile = CG_PROFILE_UNKNOWN;
|
||||
if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) {
|
||||
_default_caps._active_vprofile = CG_PROFILE_GENERIC;
|
||||
_default_caps._active_fprofile = CG_PROFILE_GENERIC;
|
||||
_default_caps._active_gprofile = CG_PROFILE_GENERIC;
|
||||
if (basic_shaders_only) {
|
||||
_default_caps._active_vprofile = CG_PROFILE_ARBVP1;
|
||||
_default_caps._active_fprofile = CG_PROFILE_ARBFP1;
|
||||
_default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
|
||||
} else {
|
||||
_default_caps._active_vprofile = CG_PROFILE_UNKNOWN;
|
||||
_default_caps._active_fprofile = CG_PROFILE_UNKNOWN;
|
||||
_default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
|
||||
}
|
||||
_default_caps._ultimate_vprofile = cgGetProfile("glslv");
|
||||
_default_caps._ultimate_fprofile = cgGetProfile("glslf");
|
||||
_default_caps._ultimate_gprofile = cgGetProfile("glslg");
|
||||
|
@ -527,6 +527,8 @@ public:
|
||||
void set_compiled(unsigned int format, const char *data, size_t length);
|
||||
bool get_compiled(unsigned int &format, std::string &binary) const;
|
||||
|
||||
static void set_default_caps(const ShaderCaps &caps);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_CG
|
||||
ShaderArgClass cg_parameter_class(CGparameter p);
|
||||
|
@ -156,7 +156,9 @@ bool ShaderTerrainMesh::generate() {
|
||||
* the chunks, and the PNMImage is destroyed afterwards.
|
||||
*/
|
||||
void ShaderTerrainMesh::do_extract_heightfield() {
|
||||
nassertv(_heightfield_tex->has_ram_image()); // Heightfield not in RAM, extract ram image first
|
||||
if (!_heightfield_tex->has_ram_image()) {
|
||||
_heightfield_tex->reload();
|
||||
}
|
||||
|
||||
_heightfield_tex->store(_heightfield);
|
||||
|
||||
|
@ -28,51 +28,6 @@ is_shadow_caster() const {
|
||||
return _shadow_caster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag indicating whether this light should cast shadows or not.
|
||||
* This is the variant without buffer size, meaning that the current buffer
|
||||
* size will be kept (512x512 is the default). Note that enabling shadows will
|
||||
* require the shader generator to be enabled on the scene.
|
||||
*/
|
||||
INLINE void LightLensNode::
|
||||
set_shadow_caster(bool caster) {
|
||||
if (_shadow_caster && !caster) {
|
||||
clear_shadow_buffers();
|
||||
}
|
||||
_shadow_caster = caster;
|
||||
set_active(caster);
|
||||
if (caster) {
|
||||
setup_shadow_map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag indicating whether this light should cast shadows or not.
|
||||
* The xsize and ysize parameters specify the size of the shadow buffer that
|
||||
* will be set up, the sort parameter specifies the sort. Note that enabling
|
||||
* shadows will require the shader generator to be enabled on the scene.
|
||||
*/
|
||||
INLINE void LightLensNode::
|
||||
set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_sort) {
|
||||
if ((_shadow_caster && !caster) || buffer_xsize != _sb_size[0] || buffer_ysize != _sb_size[1]) {
|
||||
clear_shadow_buffers();
|
||||
}
|
||||
_shadow_caster = caster;
|
||||
_sb_size.set(buffer_xsize, buffer_ysize);
|
||||
|
||||
if (buffer_sort != _sb_sort) {
|
||||
ShadowBuffers::iterator it;
|
||||
for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) {
|
||||
(*it).second->set_sort(buffer_sort);
|
||||
}
|
||||
_sb_sort = buffer_sort;
|
||||
}
|
||||
set_active(caster);
|
||||
if (caster) {
|
||||
setup_shadow_map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sort of the shadow buffer to be created for this light source.
|
||||
*/
|
||||
@ -115,3 +70,11 @@ get_shadow_buffer(GraphicsStateGuardianBase *gsg) {
|
||||
return (*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this light as having been used by the auto shader.
|
||||
*/
|
||||
INLINE void LightLensNode::
|
||||
mark_used_by_auto_shader() const {
|
||||
_used_by_auto_shader = true;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "renderState.h"
|
||||
#include "cullFaceAttrib.h"
|
||||
#include "colorWriteAttrib.h"
|
||||
#include "graphicsStateGuardianBase.h"
|
||||
|
||||
TypeHandle LightLensNode::_type_handle;
|
||||
|
||||
@ -29,7 +30,8 @@ LightLensNode::
|
||||
LightLensNode(const std::string &name, Lens *lens) :
|
||||
Camera(name, lens),
|
||||
_has_specular_color(false),
|
||||
_attrib_count(0)
|
||||
_attrib_count(0),
|
||||
_used_by_auto_shader(false)
|
||||
{
|
||||
set_active(false);
|
||||
_shadow_caster = false;
|
||||
@ -65,13 +67,67 @@ LightLensNode(const LightLensNode ©) :
|
||||
_sb_size(copy._sb_size),
|
||||
_sb_sort(-10),
|
||||
_has_specular_color(copy._has_specular_color),
|
||||
_attrib_count(0)
|
||||
_attrib_count(0),
|
||||
_used_by_auto_shader(false)
|
||||
{
|
||||
if (_shadow_caster) {
|
||||
setup_shadow_map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag indicating whether this light should cast shadows or not.
|
||||
* This is the variant without buffer size, meaning that the current buffer
|
||||
* size will be kept (512x512 is the default). Note that enabling shadows will
|
||||
* require the shader generator to be enabled on the scene.
|
||||
*/
|
||||
void LightLensNode::
|
||||
set_shadow_caster(bool caster) {
|
||||
if (_shadow_caster && !caster) {
|
||||
clear_shadow_buffers();
|
||||
}
|
||||
if (_shadow_caster != caster && _used_by_auto_shader) {
|
||||
// Make sure any shaders using this light are regenerated.
|
||||
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
|
||||
}
|
||||
_shadow_caster = caster;
|
||||
set_active(caster);
|
||||
if (caster) {
|
||||
setup_shadow_map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag indicating whether this light should cast shadows or not.
|
||||
* The xsize and ysize parameters specify the size of the shadow buffer that
|
||||
* will be set up, the sort parameter specifies the sort. Note that enabling
|
||||
* shadows will require the shader generator to be enabled on the scene.
|
||||
*/
|
||||
void LightLensNode::
|
||||
set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_sort) {
|
||||
if ((_shadow_caster && !caster) || buffer_xsize != _sb_size[0] || buffer_ysize != _sb_size[1]) {
|
||||
clear_shadow_buffers();
|
||||
}
|
||||
if (_shadow_caster != caster && _used_by_auto_shader) {
|
||||
// Make sure any shaders using this light are regenerated.
|
||||
GraphicsStateGuardianBase::mark_rehash_generated_shaders();
|
||||
}
|
||||
_shadow_caster = caster;
|
||||
_sb_size.set(buffer_xsize, buffer_ysize);
|
||||
|
||||
if (buffer_sort != _sb_sort) {
|
||||
ShadowBuffers::iterator it;
|
||||
for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) {
|
||||
(*it).second->set_sort(buffer_sort);
|
||||
}
|
||||
_sb_sort = buffer_sort;
|
||||
}
|
||||
set_active(caster);
|
||||
if (caster) {
|
||||
setup_shadow_map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the shadow buffers, meaning they will be automatically recreated
|
||||
* when the Shader Generator needs them.
|
||||
|
@ -38,8 +38,8 @@ PUBLISHED:
|
||||
INLINE bool has_specular_color() const;
|
||||
|
||||
INLINE bool is_shadow_caster() const;
|
||||
INLINE void set_shadow_caster(bool caster);
|
||||
INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
|
||||
void set_shadow_caster(bool caster);
|
||||
void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
|
||||
|
||||
INLINE int get_shadow_buffer_sort() const;
|
||||
|
||||
@ -52,6 +52,9 @@ PUBLISHED:
|
||||
MAKE_PROPERTY(shadow_caster, is_shadow_caster);
|
||||
MAKE_PROPERTY(shadow_buffer_size, get_shadow_buffer_size, set_shadow_buffer_size);
|
||||
|
||||
public:
|
||||
INLINE void mark_used_by_auto_shader() const;
|
||||
|
||||
protected:
|
||||
LightLensNode(const LightLensNode ©);
|
||||
void clear_shadow_buffers();
|
||||
@ -61,6 +64,7 @@ protected:
|
||||
bool _shadow_caster;
|
||||
bool _has_specular_color;
|
||||
int _sb_sort;
|
||||
mutable bool _used_by_auto_shader = false;
|
||||
|
||||
PT(Texture) _shadow_map;
|
||||
|
||||
|
@ -294,8 +294,14 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
|
||||
|
||||
if (node->is_of_type(LightLensNode::get_class_type())) {
|
||||
const LightLensNode *llnode = (const LightLensNode *)node;
|
||||
if (shader_attrib->auto_shadow_on() && llnode->is_shadow_caster()) {
|
||||
info._flags |= ShaderKey::LF_has_shadows;
|
||||
if (shader_attrib->auto_shadow_on()) {
|
||||
if (llnode->is_shadow_caster()) {
|
||||
info._flags |= ShaderKey::LF_has_shadows;
|
||||
}
|
||||
|
||||
// Make sure that the next time the shadows are toggled on this
|
||||
// light, it triggers a state rehash.
|
||||
llnode->mark_used_by_auto_shader();
|
||||
}
|
||||
if (llnode->has_specular_color()) {
|
||||
info._flags |= ShaderKey::LF_has_specular_color;
|
||||
|
@ -17,8 +17,7 @@
|
||||
#include "pandabase.h"
|
||||
#include "mutexImpl.h"
|
||||
#include "reMutexDirect.h"
|
||||
|
||||
class Thread;
|
||||
#include "thread.h"
|
||||
|
||||
#ifndef DEBUG_THREADS
|
||||
|
||||
|
@ -17,8 +17,7 @@
|
||||
#include "pandabase.h"
|
||||
#include "mutexTrueImpl.h"
|
||||
#include "conditionVarImpl.h"
|
||||
|
||||
class Thread;
|
||||
#include "thread.h"
|
||||
|
||||
#ifndef DEBUG_THREADS
|
||||
|
||||
|
@ -338,11 +338,12 @@ read_data(xel *array, xelval *alpha_data) {
|
||||
// format, mainly because we keep array and alpha data separately, and there
|
||||
// doesn't appear to be good support to get this stuff out row-at-a-time for
|
||||
// interlaced files.
|
||||
png_bytep *rows = (png_bytep *)PANDA_MALLOC_ARRAY(num_rows * sizeof(png_bytep));
|
||||
png_bytep *rows = (png_bytep *)alloca(num_rows * sizeof(png_bytep));
|
||||
int yi;
|
||||
|
||||
png_byte *alloc = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte) * num_rows);
|
||||
for (yi = 0; yi < num_rows; yi++) {
|
||||
rows[yi] = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte));
|
||||
rows[yi] = alloc + (row_byte_length * sizeof(png_byte)) * yi;
|
||||
}
|
||||
|
||||
png_read_image(_png, rows);
|
||||
@ -402,12 +403,10 @@ read_data(xel *array, xelval *alpha_data) {
|
||||
}
|
||||
|
||||
nassertr(source <= rows[yi] + row_byte_length, yi);
|
||||
PANDA_FREE_ARRAY(rows[yi]);
|
||||
}
|
||||
|
||||
PANDA_FREE_ARRAY(rows);
|
||||
|
||||
png_read_end(_png, nullptr);
|
||||
PANDA_FREE_ARRAY(alloc);
|
||||
|
||||
return _y_size;
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ DEFINE_GAMEPAD_BUTTON_HANDLE(lshoulder)
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(rshoulder)
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(ltrigger)
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(rtrigger)
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(lgrip)
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(rgrip)
|
||||
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_left)
|
||||
DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_right)
|
||||
@ -87,6 +89,8 @@ init_gamepad_buttons() {
|
||||
ButtonRegistry::ptr()->register_button(_rshoulder, "rshoulder");
|
||||
ButtonRegistry::ptr()->register_button(_ltrigger, "ltrigger");
|
||||
ButtonRegistry::ptr()->register_button(_rtrigger, "rtrigger");
|
||||
ButtonRegistry::ptr()->register_button(_lgrip, "lgrip");
|
||||
ButtonRegistry::ptr()->register_button(_rgrip, "rgrip");
|
||||
|
||||
ButtonRegistry::ptr()->register_button(_dpad_left, "dpad_left");
|
||||
ButtonRegistry::ptr()->register_button(_dpad_right, "dpad_right");
|
||||
|
@ -30,6 +30,8 @@ PUBLISHED:
|
||||
static ButtonHandle rshoulder();
|
||||
static ButtonHandle ltrigger();
|
||||
static ButtonHandle rtrigger();
|
||||
static ButtonHandle lgrip();
|
||||
static ButtonHandle rgrip();
|
||||
|
||||
static ButtonHandle dpad_left();
|
||||
static ButtonHandle dpad_right();
|
||||
|
@ -241,6 +241,7 @@ x11GraphicsPipe(const std::string &display) :
|
||||
_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
|
||||
_net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
|
||||
_net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
|
||||
_net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,6 +80,7 @@ public:
|
||||
Atom _net_wm_state_below;
|
||||
Atom _net_wm_state_add;
|
||||
Atom _net_wm_state_remove;
|
||||
Atom _net_wm_bypass_compositor;
|
||||
|
||||
// Extension functions.
|
||||
typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *);
|
||||
|
@ -1272,6 +1272,14 @@ set_wm_properties(const WindowProperties &properties, bool already_mapped) {
|
||||
XA_CARDINAL, 32, PropModeReplace,
|
||||
(unsigned char *)&pid, 1);
|
||||
|
||||
// Disable compositing effects in fullscreen mode.
|
||||
if (properties.has_fullscreen()) {
|
||||
int32_t compositor = properties.get_fullscreen() ? 1 : 0;
|
||||
XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
|
||||
XA_CARDINAL, 32, PropModeReplace,
|
||||
(unsigned char *)&compositor, 1);
|
||||
}
|
||||
|
||||
XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
|
||||
XA_ATOM, 32, PropModeReplace,
|
||||
(unsigned char *)type_data, next_type_data);
|
||||
|
@ -3,14 +3,14 @@
|
||||
//Cg profile arbvp1 arbfp1
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
float4 vtx_normal : NORMAL,
|
||||
out float4 l_position : POSITION,
|
||||
out float3 l_color : TEXCOORD0,
|
||||
uniform float4x4 mat_modelproj,
|
||||
uniform float4x4 itp_modelview)
|
||||
float3 vtx_normal : NORMAL,
|
||||
out float4 l_position : POSITION,
|
||||
out float3 l_color : TEXCOORD0,
|
||||
uniform float4x4 mat_modelproj,
|
||||
uniform float4x4 itp_modelview)
|
||||
{
|
||||
l_position=mul(mat_modelproj, vtx_position);
|
||||
l_color=(float3)mul(itp_modelview, vtx_normal);
|
||||
l_position = mul(mat_modelproj, vtx_position);
|
||||
l_color = (float3)mul(itp_modelview, float4(vtx_normal, 0));
|
||||
}
|
||||
|
||||
void fshader(float3 l_color: TEXCOORD0,
|
||||
@ -18,8 +18,6 @@ void fshader(float3 l_color: TEXCOORD0,
|
||||
{
|
||||
l_color = normalize(l_color);
|
||||
l_color = l_color/2;
|
||||
o_color.rgb = l_color + float4(0.5, 0.5, 0.5, 0.5);
|
||||
o_color.rgb = l_color.rgb + float3(0.5, 0.5, 0.5);
|
||||
o_color.a = 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
void vshader(float4 vtx_position : POSITION,
|
||||
float2 vtx_texcoord0 : TEXCOORD0,
|
||||
float4 vtx_normal : NORMAL,
|
||||
float3 vtx_normal : NORMAL,
|
||||
float4 vtx_color : COLOR,
|
||||
out float4 l_position : POSITION,
|
||||
out float2 l_texcoord0 : TEXCOORD0,
|
||||
@ -16,7 +16,7 @@ void vshader(float4 vtx_position : POSITION,
|
||||
l_position=mul(mat_modelproj, vtx_position);
|
||||
l_texcoord0 = vtx_texcoord0;
|
||||
l_color = vtx_color;
|
||||
l_normal = (float3)mul(itp_modelview, vtx_normal);
|
||||
l_normal = (float3)mul(itp_modelview, float4(vtx_normal, 0));
|
||||
}
|
||||
|
||||
void fshader(float2 l_texcoord0: TEXCOORD0,
|
||||
|
@ -113,6 +113,8 @@ class DeviceConnectivityMonitor(DirectObject):
|
||||
self.devices[self.current_panel].show()
|
||||
|
||||
def connect_device(self, device):
|
||||
if device in self.devices:
|
||||
return
|
||||
self.devices[device] = DeviceMonitor(device)
|
||||
self.switch_to_panel(device)
|
||||
self.create_menu_button(device)
|
||||
|
Loading…
x
Reference in New Issue
Block a user