Merge branch 'master' into cmake

This commit is contained in:
Sam Edwards 2019-01-02 06:09:15 -07:00
commit d4df1b3762
46 changed files with 614 additions and 269 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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"

View File

@ -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

View File

@ -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"')

View File

@ -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)

View File

@ -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;
}

View File

@ -73,7 +73,7 @@ private:
int32_t _mouse_button_state;
const ARect *rect;
GraphicsWindowInputDevice *_input;
public:
static TypeHandle get_class_type() {

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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));

View File

@ -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;

View File

@ -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);
}

View File

@ -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";
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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

View File

@ -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();

View File

@ -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());

View File

@ -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);

View File

@ -1,5 +1,3 @@
forcetype DConfig
forcetype PTA_uchar
forcetype CPTA_uchar
forcetype PTA_float

View File

@ -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;

View File

@ -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;

View File

@ -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 "

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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 &copy) :
_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.

View File

@ -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 &copy);
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;

View File

@ -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;

View File

@ -17,8 +17,7 @@
#include "pandabase.h"
#include "mutexImpl.h"
#include "reMutexDirect.h"
class Thread;
#include "thread.h"
#ifndef DEBUG_THREADS

View File

@ -17,8 +17,7 @@
#include "pandabase.h"
#include "mutexTrueImpl.h"
#include "conditionVarImpl.h"
class Thread;
#include "thread.h"
#ifndef DEBUG_THREADS

View File

@ -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;
}

View File

@ -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");

View File

@ -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();

View File

@ -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);
}
/**

View File

@ -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 *);

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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)