diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 3287903f59..c6ce5dfba8 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -307,7 +307,9 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, string method_name = func->_ifunc.get_name(); bool is_unary_op = func->_ifunc.is_unary_op(); - if (method_name == "operator +") { + if (method_name == "operator +" || + method_name == "__add__" || + method_name == "__radd__") { def._answer_location = "nb_add"; def._wrapper_type = WT_binary_operator; return true; @@ -319,13 +321,17 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, return true; } - if (method_name == "operator -") { + if (method_name == "operator -" || + method_name == "__sub__" || + method_name == "__rsub__") { def._answer_location = "nb_subtract"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "operator *") { + if (method_name == "operator *" || + method_name == "__mul__" || + method_name == "__rmul__") { def._answer_location = "nb_multiply"; def._wrapper_type = WT_binary_operator; return true; @@ -337,37 +343,47 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, return true; } - if (method_name == "__truediv__") { + if (method_name == "__truediv__" || + method_name == "__rtruediv__") { def._answer_location = "nb_true_divide"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "__floordiv__") { + if (method_name == "__floordiv__" || + method_name == "__rfloordiv__") { def._answer_location = "nb_floor_divide"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "operator %") { + if (method_name == "operator %" || + method_name == "__mod__" || + method_name == "__rmod__") { def._answer_location = "nb_remainder"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "operator <<") { + if (method_name == "operator <<" || + method_name == "__lshift__" || + method_name == "__rlshift__") { def._answer_location = "nb_lshift"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "operator >>") { + if (method_name == "operator >>" || + method_name == "__rshift__" || + method_name == "__rrshift__") { def._answer_location = "nb_rshift"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "operator ^") { + if (method_name == "operator ^" || + method_name == "__xor__" || + method_name == "__rxor__") { def._answer_location = "nb_xor"; def._wrapper_type = WT_binary_operator; return true; @@ -379,13 +395,17 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, return true; } - if (method_name == "operator &") { + if (method_name == "operator &" || + method_name == "__and__" || + method_name == "__rand__") { def._answer_location = "nb_and"; def._wrapper_type = WT_binary_operator; return true; } - if (method_name == "operator |") { + if (method_name == "operator |" || + method_name == "__or__" || + method_name == "__ror__") { def._answer_location = "nb_or"; def._wrapper_type = WT_binary_operator; return true; @@ -1896,16 +1916,8 @@ write_module_class(ostream &out, Object *obj) { break; case WT_one_param: - case WT_binary_operator: - case WT_inplace_binary_operator: // PyObject *func(PyObject *self, PyObject *one) { - int return_flags = RF_err_null; - if (rfi->second._wrapper_type == WT_inplace_binary_operator) { - return_flags |= RF_self; - } else { - return_flags |= RF_pyobject; - } bool all_nonconst = true; for (FunctionRemap *remap : def._remaps) { if (remap->_const_method) { @@ -1918,20 +1930,7 @@ write_module_class(ostream &out, Object *obj) { out << "//////////////////\n"; out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n"; out << " " << cClassName << " *local_this = nullptr;\n"; - if (rfi->second._wrapper_type != WT_one_param) { - // WT_binary_operator means we must return NotImplemented, instead - // of raising an exception, if the this pointer doesn't match. - // This is for things like __sub__, which Python likes to call on - // the wrong-type objects. - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - if (all_nonconst) { - out << " if (local_this == nullptr || DtoolInstance_IS_CONST(self)) {\n"; - } else { - out << " if (local_this == nullptr) {\n"; - } - out << " Py_INCREF(Py_NotImplemented);\n"; - out << " return Py_NotImplemented;\n"; - } else if (all_nonconst) { + if (all_nonconst) { out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" << ClassName << "." << methodNameFromCppName(fname, "", false) << "\")) {\n"; @@ -1944,19 +1943,85 @@ write_module_class(ostream &out, Object *obj) { string expected_params; write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true, - AT_single_arg, return_flags, false, !all_nonconst); + AT_single_arg, RF_err_null | RF_pyobject, false, !all_nonconst); - if (rfi->second._wrapper_type != WT_one_param) { - out << " Py_INCREF(Py_NotImplemented);\n"; - out << " return Py_NotImplemented;\n"; + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " return nullptr;\n"; + out << "}\n\n"; + } + break; + + case WT_binary_operator: + case WT_inplace_binary_operator: + // PyObject *func(PyObject *self, PyObject *one) + { + int return_flags = RF_err_null; + if (rfi->second._wrapper_type == WT_inplace_binary_operator) { + return_flags |= RF_self; } else { - out << " if (!_PyErr_OCCURRED()) {\n"; - out << " return Dtool_Raise_BadArgumentsError(\n"; - output_quoted(out, 6, expected_params); - out << ");\n"; - out << " }\n"; - out << " return nullptr;\n"; + return_flags |= RF_pyobject; } + bool forward_all_nonconst = true; + bool reverse_all_nonconst = true; + set forward_remaps; + set reverse_remaps; + for (FunctionRemap *remap : def._remaps) { + std::string fname = remap->_cppfunc->get_simple_name(); + if (fname.compare(0, 3, "__r") == 0 && fname != "__rshift__") { + reverse_remaps.insert(remap); + if (remap->_const_method) { + reverse_all_nonconst = false; + } + } else { + forward_remaps.insert(remap); + if (remap->_const_method) { + forward_all_nonconst = false; + } + } + } + out << "//////////////////\n"; + out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; + out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n"; + out << "//////////////////\n"; + out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n"; + out << " " << cClassName << " *local_this = nullptr;\n"; + // WT_binary_operator means we must return NotImplemented, instead + // of raising an exception, if the this pointer doesn't match. + // This is for things like __sub__, which Python likes to call on + // the wrong-type objects. + if (!forward_remaps.empty()) { + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + if (forward_all_nonconst) { + out << " if (local_this != nullptr && !DtoolInstance_IS_CONST(self)) {\n"; + } else { + out << " if (local_this != nullptr) {\n"; + } + string expected_params; + write_function_forset(out, forward_remaps, 1, 1, expected_params, 4, true, true, + AT_single_arg, return_flags, false, !forward_all_nonconst); + out << " }\n"; + } + + if (!reverse_remaps.empty()) { + out << " std::swap(self, arg);\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + if (reverse_all_nonconst) { + out << " if (local_this != nullptr && !DtoolInstance_IS_CONST(self)) {\n"; + } else { + out << " if (local_this != nullptr) {\n"; + } + string expected_params; + write_function_forset(out, reverse_remaps, 1, 1, expected_params, 4, true, true, + AT_single_arg, return_flags, false, !reverse_all_nonconst); + out << " }\n"; + } + + out << " Py_INCREF(Py_NotImplemented);\n"; + out << " return Py_NotImplemented;\n"; out << "}\n\n"; } break; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h index 711e6e4f27..13aee0e5b0 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.h +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.h @@ -71,9 +71,6 @@ protected: CFMutableArrayRef find_display_modes(int width, int height); bool do_switch_fullscreen(CGDisplayModeRef mode); - virtual void mouse_mode_absolute(); - virtual void mouse_mode_relative(); - private: NSData *load_image_data(const Filename &filename); NSImage *load_image(const Filename &filename); diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index d0b4d4c9f1..0f4acf7a83 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -693,7 +693,7 @@ open_window() { // Enable relative mouse mode, if this was requested. if (_properties.has_mouse_mode() && _properties.get_mouse_mode() == WindowProperties::M_relative) { - mouse_mode_relative(); + CGAssociateMouseAndMouseCursorPosition(NO); } _vsync_enabled = sync_video && cocoagsg->setup_vsync(); @@ -747,22 +747,6 @@ close_window() { GraphicsWindow::close_window(); } -/** - * Overridden from GraphicsWindow. - */ -void CocoaGraphicsWindow:: -mouse_mode_absolute() { - CGAssociateMouseAndMouseCursorPosition(YES); -} - -/** - * Overridden from GraphicsWindow. - */ -void CocoaGraphicsWindow:: -mouse_mode_relative() { - CGAssociateMouseAndMouseCursorPosition(NO); -} - /** * Applies the requested set of properties to the window, if possible, for * instance to request a change in size or minimization status. diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 4ae956d50b..093069a2b5 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -383,6 +383,11 @@ ConfigVariableFilename subprocess_window "and is not used or needed in other environments. See " "WindowProperties::set_subprocess_window().")); +ConfigVariableBool ime_aware +("ime-aware", false, + PRC_DESC("Set this true to show candidate strings in Panda3D rather than via " + "an OS-provided external popup window.")); + ConfigVariableString framebuffer_mode ("framebuffer-mode", "", PRC_DESC("No longer has any effect. Do not use.")); diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index c06d284674..8560251f1b 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -84,6 +84,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableString window_title; extern EXPCL_PANDA_DISPLAY ConfigVariableInt parent_window_handle; extern EXPCL_PANDA_DISPLAY ConfigVariableBool win_unexposed_draw; extern EXPCL_PANDA_DISPLAY ConfigVariableFilename subprocess_window; +extern EXPCL_PANDA_DISPLAY ConfigVariableBool ime_aware; extern EXPCL_PANDA_DISPLAY ConfigVariableString framebuffer_mode; extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_hardware; diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index e7a7a09cbd..7abda2bac7 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -696,23 +696,6 @@ add_input_device(InputDevice *device) { return index; } -/** - * detaches mouse. Only mouse delta from now on. - * - */ -void GraphicsWindow:: -mouse_mode_relative() { -} - -/** - * reattaches mouse to location - * - */ -void GraphicsWindow:: -mouse_mode_absolute() { - -} - /** * Returns whether the specified event msg is a touch message. * diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index 074aa8a41f..3424ec710f 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -130,9 +130,6 @@ protected: virtual bool do_reshape_request(int x_origin, int y_origin, bool has_origin, int x_size, int y_size); - virtual void mouse_mode_absolute(); - virtual void mouse_mode_relative(); - // It is an error to call any of the following methods from any thread other // than the window thread. void system_changed_properties(const WindowProperties &properties); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 9afeab89e5..5f5f90abc1 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1084,10 +1084,12 @@ reset() { if (is_at_least_gl_version(4, 4) || has_extension("GL_ARB_clear_texture")) { _glClearTexImage = (PFNGLCLEARTEXIMAGEPROC) get_extension_func("glClearTexImage"); + _glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC) + get_extension_func("glClearTexSubImage"); - if (_glClearTexImage == nullptr) { + if (_glClearTexImage == nullptr || _glClearTexSubImage == nullptr) { GLCAT.warning() - << "GL_ARB_clear_texture advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n"; + << "GL_ARB_clear_texture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n"; } else { _supports_clear_texture = true; } @@ -13458,59 +13460,9 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { } } - bool success = true; - if (texture_type == Texture::TT_cube_map) { - // A cube map must load six different 2-d images (which are stored as the - // six pages of the system ram image). - if (!_supports_cube_map) { - report_my_gl_errors(); - return false; - } - nassertr(target == GL_TEXTURE_CUBE_MAP, false); - - success = success && upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, - GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_POSITIVE_X, - internal_format, external_format, component_type, - true, 0, image_compression); - - success = success && upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, - GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - internal_format, external_format, component_type, - true, 1, image_compression); - - success = success && upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, - GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, - internal_format, external_format, component_type, - true, 2, image_compression); - - success = success && upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, - GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - internal_format, external_format, component_type, - true, 3, image_compression); - - success = success && upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, - GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, - internal_format, external_format, component_type, - true, 4, image_compression); - - success = success && upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, - GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, - internal_format, external_format, component_type, - true, 5, image_compression); - - } else { - // Any other kind of texture can be loaded all at once. - success = upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, target, - target, internal_format, external_format, - component_type, false, 0, image_compression); - } + bool success = upload_texture_image + (gtc, needs_reload, uses_mipmaps, mipmap_bias, target, + internal_format, external_format, component_type, image_compression); if (gtc->_generate_mipmaps && _glGenerateMipmap != nullptr && !image.is_null()) { @@ -13571,21 +13523,13 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { /** * Loads a texture image, or one page of a cube map image, from system RAM to * texture memory. - * - * texture_target is normally the same thing as page_target; both represent - * the GL target onto which the texture image is loaded, e.g. GL_TEXTURE_1D, - * GL_TEXTURE_2D, etc. The only time they may differ is in the case of cube - * mapping, in which case texture_target will be target for the overall - * texture, e.g. GL_TEXTURE_CUBE_MAP, and page_target will be the target for - * this particular page, e.g. GL_TEXTURE_CUBE_MAP_POSITIVE_X. */ bool CLP(GraphicsStateGuardian):: upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, bool uses_mipmaps, int mipmap_bias, - GLenum texture_target, GLenum page_target, + GLenum texture_target, GLint internal_format, GLint external_format, GLenum component_type, - bool one_page_only, int z, Texture::CompressionMode image_compression) { // Make sure the error stack is cleared out before we begin. clear_my_gl_errors(); @@ -13635,29 +13579,32 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, // saving on texture memory fragmentation. if (GLCAT.is_debug()) { + SparseArray pages = gtc->get_image_modified_pages(0); if (num_ram_mipmap_levels == 0) { if (tex->has_clear_color()) { GLCAT.debug() << "clearing texture " << tex->get_name() << ", " - << width << " x " << height << " x " << depth << ", z = " << z + << width << " x " << height << " x " << depth << ", pages " << pages << ", uses_mipmaps = " << uses_mipmaps << ", clear_color = " << tex->get_clear_color() << "\n"; } else { GLCAT.debug() << "not loading NULL image for texture " << tex->get_name() << ", " << width << " x " << height << " x " << depth - << ", z = " << z << ", uses_mipmaps = " << uses_mipmaps << "\n"; + << ", pages " << pages << ", uses_mipmaps = " << uses_mipmaps << "\n"; } } else { GLCAT.debug() << "updating image data of texture " << tex->get_name() << ", " << width << " x " << height << " x " << depth - << ", z = " << z << ", mipmaps " << num_ram_mipmap_levels + << ", pages " << pages << ", mipmaps " << num_ram_mipmap_levels << ", uses_mipmaps = " << uses_mipmaps << "\n"; } } for (int n = mipmap_bias; n < num_levels; ++n) { + SparseArray pages = gtc->get_image_modified_pages(n); + // we grab the mipmap pointer first, if it is NULL we grab the normal // mipmap image pointer which is a PTA_uchar const unsigned char *image_ptr = (unsigned char*)tex->get_ram_mipmap_pointer(n); @@ -13684,8 +13631,17 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, // function. vector_uchar clear_data = tex->get_clear_data(); - _glClearTexImage(gtc->_index, n - mipmap_bias, external_format, - component_type, (void *)&clear_data[0]); + if (pages.has_all_of(0, depth)) { + _glClearTexImage(gtc->_index, n - mipmap_bias, external_format, + component_type, (void *)&clear_data[0]); + } + else for (size_t sri = 0; sri < pages.get_num_subranges(); ++sri) { + int begin = pages.get_subrange_begin(sri); + int num_pages = pages.get_subrange_end(sri) - begin; + _glClearTexSubImage(gtc->_index, n - mipmap_bias, 0, 0, begin, + width, height, num_pages, external_format, + component_type, (void *)&clear_data[0]); + } continue; } } else { @@ -13714,14 +13670,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, } PTA_uchar bgr_image; - size_t view_size = tex->get_ram_mipmap_view_size(n); + size_t page_size = tex->get_ram_mipmap_page_size(n); if (image_ptr != nullptr) { const unsigned char *orig_image_ptr = image_ptr; + size_t view_size = tex->get_ram_mipmap_view_size(n); image_ptr += view_size * gtc->get_view(); - if (one_page_only) { - view_size = tex->get_ram_mipmap_page_size(n); - image_ptr += view_size * z; - } nassertr(image_ptr >= orig_image_ptr && image_ptr + view_size <= orig_image_ptr + tex->get_ram_mipmap_image_size(n), false); if (image_compression == Texture::CM_off) { @@ -13734,23 +13687,29 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, int width = tex->get_expected_mipmap_x_size(n); int height = tex->get_expected_mipmap_y_size(n); -#ifndef OPENGLES_1 - int depth = tex->get_expected_mipmap_z_size(n); -#endif #ifdef DO_PSTATS - _data_transferred_pcollector.add_level(view_size); + _data_transferred_pcollector.add_level(page_size * pages.get_num_on_bits()); #endif switch (texture_target) { #ifndef OPENGLES_1 case GL_TEXTURE_3D: if (_supports_3d_texture) { - if (image_compression == Texture::CM_off) { - _glTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth, - external_format, component_type, image_ptr); - } else { - _glCompressedTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth, - external_format, view_size, image_ptr); + for (size_t sri = 0; sri < pages.get_num_subranges(); ++sri) { + int begin = pages.get_subrange_begin(sri); + int num_pages = pages.get_subrange_end(sri) - begin; + const unsigned char *page_ptr = image_ptr + page_size * begin; + + if (image_compression == Texture::CM_off) { + _glTexSubImage3D(texture_target, n - mipmap_bias, + 0, 0, begin, width, height, num_pages, + external_format, component_type, page_ptr); + } else { + _glCompressedTexSubImage3D(texture_target, n - mipmap_bias, + 0, 0, begin, width, height, num_pages, + external_format, + page_size * num_pages, page_ptr); + } } } else { report_my_gl_errors(); @@ -13762,11 +13721,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, #ifndef OPENGLES case GL_TEXTURE_1D: if (image_compression == Texture::CM_off) { - glTexSubImage1D(page_target, n - mipmap_bias, 0, width, + glTexSubImage1D(texture_target, n - mipmap_bias, 0, width, external_format, component_type, image_ptr); } else { - _glCompressedTexSubImage1D(page_target, n - mipmap_bias, 0, width, - external_format, view_size, image_ptr); + _glCompressedTexSubImage1D(texture_target, n - mipmap_bias, 0, width, + external_format, page_size, image_ptr); } break; #endif // OPENGLES @@ -13775,12 +13734,21 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_CUBE_MAP_ARRAY: if (_supports_2d_texture_array) { - if (image_compression == Texture::CM_off) { - _glTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth, - external_format, component_type, image_ptr); - } else { - _glCompressedTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth, - external_format, view_size, image_ptr); + for (size_t sri = 0; sri < pages.get_num_subranges(); ++sri) { + int begin = pages.get_subrange_begin(sri); + int num_pages = pages.get_subrange_end(sri) - begin; + const unsigned char *page_ptr = image_ptr + page_size * begin; + + if (image_compression == Texture::CM_off) { + _glTexSubImage3D(texture_target, n - mipmap_bias, + 0, 0, begin, width, height, num_pages, + external_format, component_type, page_ptr); + } else { + _glCompressedTexSubImage3D(texture_target, n - mipmap_bias, + 0, 0, begin, width, height, num_pages, + external_format, + page_size * num_pages, page_ptr); + } } } else { report_my_gl_errors(); @@ -13792,7 +13760,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, #ifndef OPENGLES case GL_TEXTURE_BUFFER: if (_supports_buffer_texture) { - _glBufferSubData(GL_TEXTURE_BUFFER, 0, view_size, image_ptr); + _glBufferSubData(GL_TEXTURE_BUFFER, 0, page_size, image_ptr); } else { report_my_gl_errors(); return false; @@ -13800,18 +13768,46 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, break; #endif // OPENGLES + case GL_TEXTURE_CUBE_MAP: + if (_supports_cube_map) { + // This is the only texture type that must be specified using separate + // per-page calls. + if (n == 0) { + height = tex->get_y_size() - tex->get_pad_y_size(); + } + for (int z = 0; z < 6; ++z) { + if (pages.get_bit(z)) { + GLenum page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z; + const unsigned char *page_ptr = image_ptr + page_size * z; + + if (image_compression == Texture::CM_off) { + glTexSubImage2D(page_target, n - mipmap_bias, 0, 0, width, height, + external_format, component_type, page_ptr); + } else { + _glCompressedTexSubImage2D(page_target, n - mipmap_bias, + 0, 0, width, height, + external_format, page_size, page_ptr); + } + } + } + } else { + report_my_gl_errors(); + return false; + } + break; + default: if (image_compression == Texture::CM_off) { - if (n==0) { + if (n == 0) { // It's unfortunate that we can't adjust the width, too, but // TexSubImage2D doesn't accept a row-stride parameter. height = tex->get_y_size() - tex->get_pad_y_size(); } - glTexSubImage2D(page_target, n - mipmap_bias, 0, 0, width, height, + glTexSubImage2D(texture_target, n - mipmap_bias, 0, 0, width, height, external_format, component_type, image_ptr); } else { - _glCompressedTexSubImage2D(page_target, n - mipmap_bias, 0, 0, width, height, - external_format, view_size, image_ptr); + _glCompressedTexSubImage2D(texture_target, n - mipmap_bias, 0, 0, width, height, + external_format, page_size, image_ptr); } break; } @@ -13835,7 +13831,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, if (GLCAT.is_debug()) { GLCAT.debug() << "loading new texture object for " << tex->get_name() << ", " << width - << " x " << height << " x " << depth << ", z = " << z << ", mipmaps " + << " x " << height << " x " << depth << ", mipmaps " << num_ram_mipmap_levels << ", uses_mipmaps = " << uses_mipmaps << "\n"; } @@ -13900,10 +13896,6 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, if (image_ptr != nullptr) { const unsigned char *orig_image_ptr = image_ptr; image_ptr += view_size * gtc->get_view(); - if (one_page_only) { - view_size = tex->get_ram_mipmap_page_size(n); - image_ptr += view_size * z; - } nassertr(image_ptr >= orig_image_ptr && image_ptr + view_size <= orig_image_ptr + tex->get_ram_mipmap_image_size(n), false); if (image_compression == Texture::CM_off) { @@ -13927,12 +13919,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, #ifndef OPENGLES // 1-d textures not supported by OpenGL ES. Fall through. case GL_TEXTURE_1D: if (image_compression == Texture::CM_off) { - glTexImage1D(page_target, n - mipmap_bias, internal_format, - width, 0, - external_format, component_type, image_ptr); + glTexImage1D(texture_target, n - mipmap_bias, internal_format, + width, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage1D(page_target, n - mipmap_bias, external_format, width, - 0, view_size, image_ptr); + _glCompressedTexImage1D(texture_target, n - mipmap_bias, external_format, + width, 0, view_size, image_ptr); } break; #endif // OPENGLES // OpenGL ES will fall through. @@ -13941,13 +13932,12 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, case GL_TEXTURE_3D: if (_supports_3d_texture) { if (image_compression == Texture::CM_off) { - _glTexImage3D(page_target, n - mipmap_bias, internal_format, + _glTexImage3D(texture_target, n - mipmap_bias, internal_format, width, height, depth, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage3D(page_target, n - mipmap_bias, external_format, width, - height, depth, - 0, view_size, image_ptr); + _glCompressedTexImage3D(texture_target, n - mipmap_bias, external_format, + width, height, depth, 0, view_size, image_ptr); } } else { report_my_gl_errors(); @@ -13961,13 +13951,12 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, case GL_TEXTURE_CUBE_MAP_ARRAY: if (_supports_2d_texture_array) { if (image_compression == Texture::CM_off) { - _glTexImage3D(page_target, n - mipmap_bias, internal_format, + _glTexImage3D(texture_target, n - mipmap_bias, internal_format, width, height, depth, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage3D(page_target, n - mipmap_bias, external_format, width, - height, depth, - 0, view_size, image_ptr); + _glCompressedTexImage3D(texture_target, n - mipmap_bias, external_format, + width, height, depth, 0, view_size, image_ptr); } } else { report_my_gl_errors(); @@ -13988,13 +13977,37 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, break; #endif // OPENGLES + case GL_TEXTURE_CUBE_MAP: + if (_supports_cube_map) { + // This is the only texture type that must be specified using separate + // per-page calls. + size_t page_size = tex->get_ram_mipmap_page_size(n); + for (int z = 0; z < 6; ++z) { + GLenum page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z; + const unsigned char *page_ptr = image_ptr + page_size * z; + + if (image_compression == Texture::CM_off) { + glTexImage2D(page_target, n - mipmap_bias, internal_format, + width, height, 0, + external_format, component_type, page_ptr); + } else { + _glCompressedTexImage2D(page_target, n - mipmap_bias, external_format, + width, height, 0, page_size, page_ptr); + } + } + } else { + report_my_gl_errors(); + return false; + } + break; + default: if (image_compression == Texture::CM_off) { - glTexImage2D(page_target, n - mipmap_bias, internal_format, + glTexImage2D(texture_target, n - mipmap_bias, internal_format, width, height, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage2D(page_target, n - mipmap_bias, external_format, + _glCompressedTexImage2D(texture_target, n - mipmap_bias, external_format, width, height, 0, view_size, image_ptr); } } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 7f0814041c..b7aa02c08e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -616,10 +616,9 @@ protected: bool upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps); bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, bool uses_mipmaps, int mipmap_bias, - GLenum texture_target, GLenum page_target, + GLenum texture_target, GLint internal_format, GLint external_format, GLenum component_type, - bool one_page_only, int z, Texture::CompressionMode image_compression); void generate_mipmaps(CLP(TextureContext) *gtc); bool upload_simple_texture(CLP(TextureContext) *gtc); @@ -808,6 +807,7 @@ public: bool _supports_clear_texture; #ifndef OPENGLES PFNGLCLEARTEXIMAGEPROC _glClearTexImage; + PFNGLCLEARTEXSUBIMAGEPROC _glClearTexSubImage; #endif bool _supports_clear_buffer; diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I index ce3aa6cffe..223a86aab5 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -357,8 +357,6 @@ INLINE bool Texture:: load(const PNMImage &pnmimage, const LoaderOptions &options) { CDWriter cdata(_cycler, true); do_clear(cdata); - cdata->inc_properties_modified(); - cdata->inc_image_modified(); if (do_load_one(cdata, pnmimage, get_name(), 0, 0, options)) { bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0); consider_auto_process_ram_image(generate_mipmaps || uses_mipmaps(), true); @@ -374,7 +372,7 @@ INLINE bool Texture:: load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options) { CDWriter cdata(_cycler, true); cdata->inc_properties_modified(); - cdata->inc_image_modified(); + cdata->inc_image_page_modified(z); if (do_load_one(cdata, pnmimage, get_name(), z, n, options)) { return true; } @@ -388,8 +386,6 @@ INLINE bool Texture:: load(const PfmFile &pfm, const LoaderOptions &options) { CDWriter cdata(_cycler, true); do_clear(cdata); - cdata->inc_properties_modified(); - cdata->inc_image_modified(); if (do_load_one(cdata, pfm, get_name(), 0, 0, options)) { bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0); consider_auto_process_ram_image(generate_mipmaps || uses_mipmaps(), true); @@ -405,7 +401,7 @@ INLINE bool Texture:: load(const PfmFile &pfm, int z, int n, const LoaderOptions &options) { CDWriter cdata(_cycler, true); cdata->inc_properties_modified(); - cdata->inc_image_modified(); + cdata->inc_image_page_modified(z); if (do_load_one(cdata, pfm, get_name(), z, n, options)) { return true; } @@ -2460,11 +2456,14 @@ inc_properties_modified() { } /** - * + * Marks the whole image as modified. */ INLINE void Texture::CData:: inc_image_modified() { ++_image_modified; + _modified_pages.resize(1); + _modified_pages[0]._z_end = (size_t)-1; + _modified_pages[0]._modified = _image_modified; } /** diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index d6c30296d9..c8c649bc36 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -551,8 +551,6 @@ bool Texture:: read(const Filename &fullpath, const LoaderOptions &options) { CDWriter cdata(_cycler, true); do_clear(cdata); - cdata->inc_properties_modified(); - cdata->inc_image_modified(); return do_read(cdata, fullpath, Filename(), 0, 0, 0, 0, false, false, options, nullptr); } @@ -570,8 +568,6 @@ read(const Filename &fullpath, const Filename &alpha_fullpath, const LoaderOptions &options) { CDWriter cdata(_cycler, true); do_clear(cdata); - cdata->inc_properties_modified(); - cdata->inc_image_modified(); return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels, alpha_file_channel, 0, 0, false, false, options, nullptr); @@ -585,12 +581,15 @@ read(const Filename &fullpath, const Filename &alpha_fullpath, * the various parameters. */ bool Texture:: -read(const Filename &fullpath, int z, int n, - bool read_pages, bool read_mipmaps, +read(const Filename &fullpath, int z, int n, bool read_pages, bool read_mipmaps, const LoaderOptions &options) { CDWriter cdata(_cycler, true); cdata->inc_properties_modified(); - cdata->inc_image_modified(); + if (read_pages) { + cdata->inc_image_modified(); + } else { + cdata->inc_image_page_modified(z); + } return do_read(cdata, fullpath, Filename(), 0, 0, z, n, read_pages, read_mipmaps, options, nullptr); } @@ -655,7 +654,11 @@ read(const Filename &fullpath, const Filename &alpha_fullpath, const LoaderOptions &options) { CDWriter cdata(_cycler, true); cdata->inc_properties_modified(); - cdata->inc_image_modified(); + if (read_pages) { + cdata->inc_image_modified(); + } else { + cdata->inc_image_page_modified(z); + } return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels, alpha_file_channel, z, n, read_pages, read_mipmaps, options, record); @@ -1422,6 +1425,39 @@ peek() { return nullptr; } +/** + * Returns a SparseArray containing all the image pages that have been modified + * since the given UpdateSeq value. + */ +SparseArray Texture:: +get_image_modified_pages(UpdateSeq since, int n) const { + CDReader cdata(_cycler); + + SparseArray result; + if (since == cdata->_image_modified) { + // Early-out since no range is more recent than _image_modified. + return result; + } + + if (n > 0 && cdata->_texture_type == Texture::TT_3d_texture) { + // Don't bother handling this special case, just consider all mipmap pages + // modified. + result.set_range(0, do_get_expected_mipmap_z_size(cdata, n)); + return result; + } + + for (const ModifiedPageRange &range : cdata->_modified_pages) { + if (range._z_begin >= cdata->_z_size) { + break; + } + if (since < range._modified) { + result.set_range(range._z_begin, std::min(range._z_end, (size_t)cdata->_z_size) - range._z_begin); + } + } + + return result; +} + /** * Indicates that the texture should be enqueued to be prepared in the * indicated prepared_objects at the beginning of the next frame. This will @@ -3541,7 +3577,7 @@ do_load_sub_image(CData *cdata, const PNMImage &image, int x, int y, int z, int // Flip y y = cdata->_y_size - (image.get_y_size() + y); - cdata->inc_image_modified(); + cdata->inc_image_page_modified(z); do_modify_ram_mipmap_image(cdata, n); convert_from_pnmimage(cdata->_ram_images[n]._image, do_get_expected_ram_mipmap_page_size(cdata, n), @@ -10619,6 +10655,10 @@ CData() { _simple_ram_image._page_size = 0; _has_clear_color = false; + + _modified_pages.resize(1); + _modified_pages[0]._z_end = (size_t)-1; + _modified_pages[0]._modified = _image_modified; } /** @@ -10633,6 +10673,7 @@ CData(const Texture::CData ©) { _properties_modified = copy._properties_modified; _image_modified = copy._image_modified; _simple_image_modified = copy._simple_image_modified; + _modified_pages = copy._modified_pages; } /** @@ -10690,6 +10731,46 @@ do_assign(const Texture::CData *copy) { _simple_ram_image = copy->_simple_ram_image; } +/** + * Marks a single page of the image as modified. + */ +void Texture::CData:: +inc_image_page_modified(int z) { + ++_image_modified; + + ModifiedPageRanges::iterator it = _modified_pages.begin(); + while (it != _modified_pages.end() && (*it)._z_end <= z) { + ++it; + continue; + } + nassertv(it != _modified_pages.end()); + + size_t orig_z_end = (*it)._z_end; + UpdateSeq orig_modified = (*it)._modified; + + if (z > (*it)._z_begin) { + // Split prefix. + ModifiedPageRange copy(*it); + copy._z_end = z; + it = _modified_pages.insert(it, copy); + ++it; + } + + (*it)._z_begin = z; + (*it)._z_end = z + 1; + (*it)._modified = _image_modified; + + if (z + 1 < orig_z_end) { + // Split suffix. + ModifiedPageRange copy(*it); + copy._z_begin = z + 1; + copy._z_end = orig_z_end; + copy._modified = orig_modified; + ++it; + _modified_pages.insert(it, copy); + } +} + /** * Writes the contents of this object to the datagram for shipping out to a * Bam file. diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 7abdb17216..0254db323a 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -525,6 +525,8 @@ PUBLISHED: MAKE_PROPERTY(image_modified, get_image_modified); MAKE_PROPERTY(simple_image_modified, get_simple_image_modified); + SparseArray get_image_modified_pages(UpdateSeq since, int n = 0) const; + INLINE bool has_auto_texture_scale() const; INLINE AutoTextureScale get_auto_texture_scale() const; INLINE void set_auto_texture_scale(AutoTextureScale scale); @@ -932,6 +934,13 @@ private: protected: typedef pvector RamImages; + struct ModifiedPageRange { + size_t _z_begin = 0; + size_t _z_end; + UpdateSeq _modified; + }; + typedef pvector ModifiedPageRanges; + // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_GOBJ CData : public CycleData { public: @@ -949,6 +958,7 @@ protected: void do_assign(const CData *copy); INLINE void inc_properties_modified(); INLINE void inc_image_modified(); + void inc_image_page_modified(int z); INLINE void inc_simple_image_modified(); Filename _filename; @@ -1020,6 +1030,8 @@ protected: UpdateSeq _image_modified; UpdateSeq _simple_image_modified; + ModifiedPageRanges _modified_pages; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/gobj/textureContext.I b/panda/src/gobj/textureContext.I index 2c58bc7b07..a4be4a4935 100644 --- a/panda/src/gobj/textureContext.I +++ b/panda/src/gobj/textureContext.I @@ -76,6 +76,15 @@ was_simple_image_modified() const { return _simple_image_modified != get_texture()->get_simple_image_modified(); } +/** + * Returns true if the given page of the texture image has been modified since + * the last time mark_loaded() was called. + */ +INLINE bool TextureContext:: +was_image_page_modified(int z, int n) const { + return get_texture()->get_image_modified_pages(_image_modified, n).get_bit(z); +} + /** * Returns a sequence number which is guaranteed to change at least every time * the texture properties (unrelated to the image) are modified. @@ -103,6 +112,15 @@ get_simple_image_modified() const { return _simple_image_modified; } +/** + * Returns a SparseArray indicating which pages of the texture have been + * modified since the last call to mark_loaded(). + */ +INLINE SparseArray TextureContext:: +get_image_modified_pages(int n) const { + return get_texture()->get_image_modified_pages(_image_modified, n); +} + /** * Should be called (usually by a derived class) when the on-card size of this * object has changed. diff --git a/panda/src/gobj/textureContext.h b/panda/src/gobj/textureContext.h index 2962fd38c0..b531c49504 100644 --- a/panda/src/gobj/textureContext.h +++ b/panda/src/gobj/textureContext.h @@ -44,11 +44,14 @@ PUBLISHED: INLINE bool was_properties_modified() const; INLINE bool was_image_modified() const; INLINE bool was_simple_image_modified() const; + INLINE bool was_image_page_modified(int z, int n) const; INLINE UpdateSeq get_properties_modified() const; INLINE UpdateSeq get_image_modified() const; INLINE UpdateSeq get_simple_image_modified() const; + INLINE SparseArray get_image_modified_pages(int n = 0) const; + public: INLINE void update_data_size_bytes(size_t new_data_size_bytes); INLINE void mark_loaded(); diff --git a/panda/src/linmath/lmatrix3_ext_src.I b/panda/src/linmath/lmatrix3_ext_src.I index 0d92b37aa4..38b47f4c04 100644 --- a/panda/src/linmath/lmatrix3_ext_src.I +++ b/panda/src/linmath/lmatrix3_ext_src.I @@ -34,6 +34,14 @@ __reduce__(PyObject *self) const { return result; } +/** + * + */ +INLINE_LINMATH FLOATNAME(LMatrix3) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} + /** * */ diff --git a/panda/src/linmath/lmatrix3_ext_src.h b/panda/src/linmath/lmatrix3_ext_src.h index 6c4894c69f..fa979338ff 100644 --- a/panda/src/linmath/lmatrix3_ext_src.h +++ b/panda/src/linmath/lmatrix3_ext_src.h @@ -19,6 +19,9 @@ template<> class Extension : public ExtensionBase { public: INLINE_LINMATH PyObject *__reduce__(PyObject *self) const; + + INLINE_LINMATH FLOATNAME(LMatrix3) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lmatrix3_src.h b/panda/src/linmath/lmatrix3_src.h index 2509471e9e..cf5f7c6776 100644 --- a/panda/src/linmath/lmatrix3_src.h +++ b/panda/src/linmath/lmatrix3_src.h @@ -183,6 +183,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LMatrix3) &operator *= (FLOATTYPE scalar); INLINE_LINMATH FLOATNAME(LMatrix3) &operator /= (FLOATTYPE scalar); + EXTENSION(INLINE_LINMATH FLOATNAME(LMatrix3) __rmul__(FLOATTYPE scalar) const); + INLINE_LINMATH void componentwise_mult(const FLOATNAME(LMatrix3) &other); INLINE_LINMATH FLOATTYPE determinant() const; diff --git a/panda/src/linmath/lmatrix4_ext_src.I b/panda/src/linmath/lmatrix4_ext_src.I index 68a2741bbb..2d4e6d07fb 100644 --- a/panda/src/linmath/lmatrix4_ext_src.I +++ b/panda/src/linmath/lmatrix4_ext_src.I @@ -35,6 +35,14 @@ __reduce__(PyObject *self) const { return result; } +/** + * + */ +INLINE_LINMATH FLOATNAME(LMatrix4) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} + /** * */ diff --git a/panda/src/linmath/lmatrix4_ext_src.h b/panda/src/linmath/lmatrix4_ext_src.h index 768be01526..da7a430175 100644 --- a/panda/src/linmath/lmatrix4_ext_src.h +++ b/panda/src/linmath/lmatrix4_ext_src.h @@ -19,6 +19,9 @@ template<> class Extension : public ExtensionBase { public: INLINE_LINMATH PyObject *__reduce__(PyObject *self) const; + + INLINE_LINMATH FLOATNAME(LMatrix4) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index 9fd695dbf6..e3cec2090f 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -189,6 +189,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LMatrix4) &operator *= (FLOATTYPE scalar); INLINE_LINMATH FLOATNAME(LMatrix4) &operator /= (FLOATTYPE scalar); + EXTENSION(INLINE_LINMATH FLOATNAME(LMatrix4) __rmul__(FLOATTYPE scalar) const); + INLINE_LINMATH void componentwise_mult(const FLOATNAME(LMatrix4) &other); INLINE_LINMATH void transpose_from(const FLOATNAME(LMatrix4) &other); diff --git a/panda/src/linmath/lpoint2_ext_src.I b/panda/src/linmath/lpoint2_ext_src.I index acfa098501..09f357686f 100644 --- a/panda/src/linmath/lpoint2_ext_src.I +++ b/panda/src/linmath/lpoint2_ext_src.I @@ -79,3 +79,11 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { // Upcall to LVecBase2. return invoke_extension(_this).__setattr__(self, attr_name, assign); } + +/** + * + */ +INLINE_LINMATH FLOATNAME(LPoint2) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} diff --git a/panda/src/linmath/lpoint2_ext_src.h b/panda/src/linmath/lpoint2_ext_src.h index 27eadd1bdf..496e7ec1a8 100644 --- a/panda/src/linmath/lpoint2_ext_src.h +++ b/panda/src/linmath/lpoint2_ext_src.h @@ -20,6 +20,9 @@ class Extension : public ExtensionBase { public: INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); + + INLINE_LINMATH FLOATNAME(LPoint2) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lpoint2_src.h b/panda/src/linmath/lpoint2_src.h index 070fc13086..8d8a97948c 100644 --- a/panda/src/linmath/lpoint2_src.h +++ b/panda/src/linmath/lpoint2_src.h @@ -46,6 +46,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LPoint2) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LPoint2) operator / (FLOATTYPE scalar) const; + EXTENSION(INLINE_LINMATH FLOATNAME(LPoint2) __rmul__(FLOATTYPE scalar) const); + #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LPoint2) normalized() const; INLINE_LINMATH FLOATNAME(LPoint2) project(const FLOATNAME(LVecBase2) &onto) const; diff --git a/panda/src/linmath/lpoint3_ext_src.I b/panda/src/linmath/lpoint3_ext_src.I index 95cb1dbdd3..b45b62e936 100644 --- a/panda/src/linmath/lpoint3_ext_src.I +++ b/panda/src/linmath/lpoint3_ext_src.I @@ -80,3 +80,11 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { // Upcall to LVecBase2. return invoke_extension(_this).__setattr__(self, attr_name, assign); } + +/** + * + */ +INLINE_LINMATH FLOATNAME(LPoint3) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} diff --git a/panda/src/linmath/lpoint3_ext_src.h b/panda/src/linmath/lpoint3_ext_src.h index 3c820d3a45..6789810ff5 100644 --- a/panda/src/linmath/lpoint3_ext_src.h +++ b/panda/src/linmath/lpoint3_ext_src.h @@ -20,6 +20,9 @@ class Extension : public ExtensionBase { public: INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); + + INLINE_LINMATH FLOATNAME(LPoint3) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lpoint3_src.h b/panda/src/linmath/lpoint3_src.h index 3510aa329e..512bc5ef88 100644 --- a/panda/src/linmath/lpoint3_src.h +++ b/panda/src/linmath/lpoint3_src.h @@ -66,6 +66,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LPoint3) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LPoint3) operator / (FLOATTYPE scalar) const; + EXTENSION(INLINE_LINMATH FLOATNAME(LPoint3) __rmul__(FLOATTYPE scalar) const); + // Some special named constructors for LPoint3. INLINE_LINMATH static const FLOATNAME(LPoint3) &origin(CoordinateSystem cs = CS_default); diff --git a/panda/src/linmath/lpoint4_ext_src.I b/panda/src/linmath/lpoint4_ext_src.I index e4101b5ed9..ee64b2c287 100644 --- a/panda/src/linmath/lpoint4_ext_src.I +++ b/panda/src/linmath/lpoint4_ext_src.I @@ -85,3 +85,11 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { // Upcall to LVecBase4. return invoke_extension(_this).__setattr__(self, attr_name, assign); } + +/** + * + */ +INLINE_LINMATH FLOATNAME(LPoint4) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} diff --git a/panda/src/linmath/lpoint4_ext_src.h b/panda/src/linmath/lpoint4_ext_src.h index 82c9ea62f1..2cb8eb6b9f 100644 --- a/panda/src/linmath/lpoint4_ext_src.h +++ b/panda/src/linmath/lpoint4_ext_src.h @@ -20,6 +20,9 @@ class Extension : public ExtensionBase { public: INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); + + INLINE_LINMATH FLOATNAME(LPoint4) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lpoint4_src.h b/panda/src/linmath/lpoint4_src.h index 7a9902f55d..b0d8eb8036 100644 --- a/panda/src/linmath/lpoint4_src.h +++ b/panda/src/linmath/lpoint4_src.h @@ -54,6 +54,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LPoint4) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LPoint4) operator / (FLOATTYPE scalar) const; + EXTENSION(INLINE_LINMATH FLOATNAME(LPoint4) __rmul__(FLOATTYPE scalar) const); + #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LPoint4) normalized() const; INLINE_LINMATH FLOATNAME(LPoint4) project(const FLOATNAME(LVecBase4) &onto) const; diff --git a/panda/src/linmath/lvecBase2_ext_src.I b/panda/src/linmath/lvecBase2_ext_src.I index 45d64e0de8..5d731fec7a 100644 --- a/panda/src/linmath/lvecBase2_ext_src.I +++ b/panda/src/linmath/lvecBase2_ext_src.I @@ -192,6 +192,25 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { return 0; } +/** + * + */ +INLINE_LINMATH PyObject *Extension:: +__rmul__(PyObject *self, FLOATTYPE scalar) const { +#ifndef CPPPARSER + extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase2); +#endif + PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self)); + if (py_vec != nullptr) { + FLOATNAME(LVecBase2) *vec = (FLOATNAME(LVecBase2) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase2)); + nassertr(vec != nullptr, nullptr); + + vec->_v(0) = _this->_v(0) * scalar; + vec->_v(1) = _this->_v(1) * scalar; + } + return py_vec; +} + /** * */ diff --git a/panda/src/linmath/lvecBase2_ext_src.h b/panda/src/linmath/lvecBase2_ext_src.h index c57dc15504..dad3807548 100644 --- a/panda/src/linmath/lvecBase2_ext_src.h +++ b/panda/src/linmath/lvecBase2_ext_src.h @@ -23,6 +23,8 @@ public: INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); INLINE_LINMATH std::string __repr__() const; + INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const; + INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const; INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar); diff --git a/panda/src/linmath/lvecBase2_src.h b/panda/src/linmath/lvecBase2_src.h index 6e41088706..9e6af9170f 100644 --- a/panda/src/linmath/lvecBase2_src.h +++ b/panda/src/linmath/lvecBase2_src.h @@ -133,6 +133,8 @@ PUBLISHED: INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase2) &other); + EXTENSION(INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const); + EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const); EXTENSION(INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar)); diff --git a/panda/src/linmath/lvecBase3_ext_src.I b/panda/src/linmath/lvecBase3_ext_src.I index 1197f203d6..85486df43d 100644 --- a/panda/src/linmath/lvecBase3_ext_src.I +++ b/panda/src/linmath/lvecBase3_ext_src.I @@ -193,6 +193,27 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { return 0; } + +/** + * + */ +INLINE_LINMATH PyObject *Extension:: +__rmul__(PyObject *self, FLOATTYPE scalar) const { +#ifndef CPPPARSER + extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase3); +#endif + PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self)); + if (py_vec != nullptr) { + FLOATNAME(LVecBase3) *vec = (FLOATNAME(LVecBase3) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase3)); + nassertr(vec != nullptr, nullptr); + + vec->_v(0) = _this->_v(0) * scalar; + vec->_v(1) = _this->_v(1) * scalar; + vec->_v(2) = _this->_v(2) * scalar; + } + return py_vec; +} + /** * */ diff --git a/panda/src/linmath/lvecBase3_ext_src.h b/panda/src/linmath/lvecBase3_ext_src.h index 09b6c30234..6d3d9c85a9 100644 --- a/panda/src/linmath/lvecBase3_ext_src.h +++ b/panda/src/linmath/lvecBase3_ext_src.h @@ -23,6 +23,8 @@ public: INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); INLINE_LINMATH std::string __repr__() const; + INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const; + INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const; INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar); diff --git a/panda/src/linmath/lvecBase3_src.h b/panda/src/linmath/lvecBase3_src.h index 72f5ffdf00..64785a4747 100644 --- a/panda/src/linmath/lvecBase3_src.h +++ b/panda/src/linmath/lvecBase3_src.h @@ -152,6 +152,8 @@ PUBLISHED: INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase3) &other); + EXTENSION(INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const); + EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const); EXTENSION(INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar)); diff --git a/panda/src/linmath/lvecBase4_ext_src.I b/panda/src/linmath/lvecBase4_ext_src.I index 858abf7207..b18012cbdc 100644 --- a/panda/src/linmath/lvecBase4_ext_src.I +++ b/panda/src/linmath/lvecBase4_ext_src.I @@ -199,6 +199,28 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { return 0; } + +/** + * + */ +INLINE_LINMATH PyObject *Extension:: +__rmul__(PyObject *self, FLOATTYPE scalar) const { +#ifndef CPPPARSER + extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase4); +#endif + PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self)); + if (py_vec != nullptr) { + FLOATNAME(LVecBase4) *vec = (FLOATNAME(LVecBase4) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase4)); + nassertr(vec != nullptr, nullptr); + + vec->_v(0) = _this->_v(0) * scalar; + vec->_v(1) = _this->_v(1) * scalar; + vec->_v(2) = _this->_v(2) * scalar; + vec->_v(3) = _this->_v(3) * scalar; + } + return py_vec; +} + /** * */ diff --git a/panda/src/linmath/lvecBase4_ext_src.h b/panda/src/linmath/lvecBase4_ext_src.h index 693c07e111..da9dcd3d4b 100644 --- a/panda/src/linmath/lvecBase4_ext_src.h +++ b/panda/src/linmath/lvecBase4_ext_src.h @@ -23,6 +23,8 @@ public: INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); INLINE_LINMATH std::string __repr__() const; + INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const; + INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const; INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar); diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index fd73d5dbed..e5a23374a4 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -160,6 +160,8 @@ PUBLISHED: INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase4) &other); + EXTENSION(INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const); + EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const); EXTENSION(INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar)); diff --git a/panda/src/linmath/lvector2_ext_src.I b/panda/src/linmath/lvector2_ext_src.I index 0d9aaa0258..dba65a1b61 100644 --- a/panda/src/linmath/lvector2_ext_src.I +++ b/panda/src/linmath/lvector2_ext_src.I @@ -79,3 +79,11 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { // Upcall to LVecBase2. return invoke_extension(_this).__setattr__(self, attr_name, assign); } + +/** + * + */ +INLINE_LINMATH FLOATNAME(LVector2) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} diff --git a/panda/src/linmath/lvector2_ext_src.h b/panda/src/linmath/lvector2_ext_src.h index 0028c73431..6518c6ebd4 100644 --- a/panda/src/linmath/lvector2_ext_src.h +++ b/panda/src/linmath/lvector2_ext_src.h @@ -20,6 +20,9 @@ class Extension : public ExtensionBase public: INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); + + INLINE_LINMATH FLOATNAME(LVector2) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lvector2_src.h b/panda/src/linmath/lvector2_src.h index 1454205985..2e0b68f0d3 100644 --- a/panda/src/linmath/lvector2_src.h +++ b/panda/src/linmath/lvector2_src.h @@ -40,6 +40,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector2) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LVector2) operator / (FLOATTYPE scalar) const; + EXTENSION(INLINE_LINMATH FLOATNAME(LVector2) __rmul__(FLOATTYPE scalar) const); + #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVector2) normalized() const; INLINE_LINMATH FLOATNAME(LVector2) project(const FLOATNAME(LVecBase2) &onto) const; diff --git a/panda/src/linmath/lvector3_ext_src.I b/panda/src/linmath/lvector3_ext_src.I index ee620ba30a..1c309ef87e 100644 --- a/panda/src/linmath/lvector3_ext_src.I +++ b/panda/src/linmath/lvector3_ext_src.I @@ -80,3 +80,11 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { // Upcall to LVecBase3. return invoke_extension(_this).__setattr__(self, attr_name, assign); } + +/** + * + */ +INLINE_LINMATH FLOATNAME(LVector3) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} diff --git a/panda/src/linmath/lvector3_ext_src.h b/panda/src/linmath/lvector3_ext_src.h index 577382dcd8..b6b94c9c74 100644 --- a/panda/src/linmath/lvector3_ext_src.h +++ b/panda/src/linmath/lvector3_ext_src.h @@ -20,6 +20,9 @@ class Extension : public ExtensionBase public: INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); + + INLINE_LINMATH FLOATNAME(LVector3) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lvector3_src.h b/panda/src/linmath/lvector3_src.h index 1052e5cee8..075dbe338f 100644 --- a/panda/src/linmath/lvector3_src.h +++ b/panda/src/linmath/lvector3_src.h @@ -71,6 +71,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector3) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LVector3) operator / (FLOATTYPE scalar) const; + EXTENSION(INLINE_LINMATH FLOATNAME(LVector3) __rmul__(FLOATTYPE scalar) const); + // Some special named constructors for LVector3. INLINE_LINMATH static FLOATNAME(LVector3) up(CoordinateSystem cs = CS_default); diff --git a/panda/src/linmath/lvector4_ext_src.I b/panda/src/linmath/lvector4_ext_src.I index 6d4f7743b6..dd8b3a6b48 100644 --- a/panda/src/linmath/lvector4_ext_src.I +++ b/panda/src/linmath/lvector4_ext_src.I @@ -85,3 +85,11 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) { // Upcall to LVecBase4. return invoke_extension(_this).__setattr__(self, attr_name, assign); } + +/** + * + */ +INLINE_LINMATH FLOATNAME(LVector4) Extension:: +__rmul__(FLOATTYPE scalar) const { + return *_this * scalar; +} diff --git a/panda/src/linmath/lvector4_ext_src.h b/panda/src/linmath/lvector4_ext_src.h index 4aebf7c1e3..e398875517 100644 --- a/panda/src/linmath/lvector4_ext_src.h +++ b/panda/src/linmath/lvector4_ext_src.h @@ -20,6 +20,9 @@ class Extension : public ExtensionBase public: INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign); + + INLINE_LINMATH FLOATNAME(LVector4) __rmul__(FLOATTYPE scalar) const; + INLINE_LINMATH std::string __repr__() const; }; diff --git a/panda/src/linmath/lvector4_src.h b/panda/src/linmath/lvector4_src.h index cc04c5bbdf..d8a00c7e9f 100644 --- a/panda/src/linmath/lvector4_src.h +++ b/panda/src/linmath/lvector4_src.h @@ -48,6 +48,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector4) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LVector4) operator / (FLOATTYPE scalar) const; + EXTENSION(INLINE_LINMATH FLOATNAME(LVector4) __rmul__(FLOATTYPE scalar) const); + #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVector4) normalized() const; INLINE_LINMATH FLOATNAME(LVector4) project(const FLOATNAME(LVecBase4) &onto) const; diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 220352a351..805892b947 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -746,6 +746,14 @@ get_num_unused_states() { for (size_t si = 0; si < size; ++si) { const RenderState *state = _states.get_key(si); + std::pair ir = + state_count.insert(StateCount::value_type(state, 1)); + if (!ir.second) { + // If the above insert operation fails, then it's already in the + // cache; increment its value. + (*(ir.first)).second++; + } + size_t i; size_t cache_size = state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { @@ -1843,6 +1851,7 @@ init_states() { // is declared globally, and lives forever. RenderState *state = new RenderState; state->local_object(); + state->cache_ref_only(); state->_saved_entry = _states.store(state, nullptr); _empty_state = state; } diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 027dae3c3f..f096dfee27 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -145,6 +145,7 @@ PUBLISHED: static void list_states(std::ostream &out); static bool validate_states(); EXTENSION(static PyObject *get_states()); + EXTENSION(static PyObject *get_unused_states()); PUBLISHED: // These methods are intended for use by low-level code, but they're also diff --git a/panda/src/pgraph/renderState_ext.cxx b/panda/src/pgraph/renderState_ext.cxx index d0afb0718a..f1aa14615b 100644 --- a/panda/src/pgraph/renderState_ext.cxx +++ b/panda/src/pgraph/renderState_ext.cxx @@ -139,4 +139,29 @@ get_states() { return list; } +/** + * Returns a list of all of the "unused" RenderState objects in the state + * cache. See get_num_unused_states(). + */ +PyObject *Extension:: +get_unused_states() { + extern struct Dtool_PyTypedObject Dtool_RenderState; + LightReMutexHolder holder(*RenderState::_states_lock); + + PyObject *list = PyList_New(0); + size_t size = RenderState::_states.get_num_entries(); + for (size_t si = 0; si < size; ++si) { + const RenderState *state = RenderState::_states.get_key(si); + if (state->get_cache_ref_count() == state->get_ref_count()) { + state->ref(); + PyObject *a = + DTool_CreatePyInstanceTyped((void *)state, Dtool_RenderState, + true, true, state->get_type_index()); + PyList_Append(list, a); + Py_DECREF(a); + } + } + return list; +} + #endif // HAVE_PYTHON diff --git a/panda/src/pgraph/renderState_ext.h b/panda/src/pgraph/renderState_ext.h index c452a025af..f7854d8646 100644 --- a/panda/src/pgraph/renderState_ext.h +++ b/panda/src/pgraph/renderState_ext.h @@ -32,6 +32,7 @@ public: PyObject *get_composition_cache() const; PyObject *get_invert_composition_cache() const; static PyObject *get_states(); + static PyObject *get_unused_states(); }; #endif // HAVE_PYTHON diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 916280ff1c..aac37c306b 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -1017,6 +1017,14 @@ get_num_unused_states() { for (size_t si = 0; si < size; ++si) { const TransformState *state = _states.get_key(si); + std::pair ir = + state_count.insert(StateCount::value_type(state, 1)); + if (!ir.second) { + // If the above insert operation fails, then it's already in the + // cache; increment its value. + (*(ir.first)).second++; + } + size_t i; size_t cache_size = state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { diff --git a/panda/src/pgui/pgEntry.cxx b/panda/src/pgui/pgEntry.cxx index 633f999769..2fc40d546a 100644 --- a/panda/src/pgui/pgEntry.cxx +++ b/panda/src/pgui/pgEntry.cxx @@ -41,7 +41,8 @@ PGEntry:: PGEntry(const string &name) : PGItem(name), _text(get_text_node()), - _obscure_text(get_text_node()) + _obscure_text(get_text_node()), + _candidate_text(get_text_node()) { set_cull_callback(); @@ -103,6 +104,7 @@ PGEntry(const PGEntry ©) : PGItem(copy), _text(copy._text), _obscure_text(copy._obscure_text), + _candidate_text(copy._candidate_text), _cursor_position(copy._cursor_position), _cursor_visible(copy._cursor_visible), _candidate_highlight_start(copy._candidate_highlight_start), @@ -216,11 +218,6 @@ press(const MouseWatcherParameter ¶m, bool background) { } else if ((!background && get_focus()) || (background && get_background_focus())) { // Keyboard button. - if (!_candidate_wtext.empty()) { - _candidate_wtext = wstring(); - _text_geom_stale = true; - } - _cursor_position = min(_cursor_position, _text.get_num_characters()); _blink_start = ClockObject::get_global_clock()->get_frame_time(); if (button == KeyboardButton::enter()) { @@ -797,9 +794,16 @@ update_text() { } else { TextPropertiesManager *tp_mgr = TextPropertiesManager::get_global_ptr(); + bool has_inactive = tp_mgr->has_properties(_candidate_inactive); TextProperties inactive = tp_mgr->get_properties(_candidate_inactive); TextProperties active = tp_mgr->get_properties(_candidate_active); + if (!has_inactive) { + // Just underscoring the candidate is a sensible default. + inactive.set_underscore(true); + tp_mgr->set_properties(_candidate_inactive, inactive); + } + // Insert the complex sequence of characters required to show the // candidate string in a different color. This gets inserted at the // current cursor position. @@ -818,9 +822,9 @@ update_text() { cseq += wstring(1, (wchar_t)text_pop_properties_key); // Create a special TextAssembler to insert the candidate string. - TextAssembler ctext(_text); - ctext.set_wsubstr(cseq, _cursor_position, 0); - assembled = ctext.assemble_text(); + _candidate_text = _text; + _candidate_text.set_wsubstr(cseq, _cursor_position, 0); + assembled = _candidate_text.assemble_text(); } if (!_current_text.is_empty()) { @@ -902,7 +906,8 @@ update_cursor() { _obscure_text.calc_r_c(row, column, _cursor_position); xpos = _obscure_text.get_xpos(row, column); ypos = _obscure_text.get_ypos(row, column); - } else { + } + else if (_candidate_wtext.empty()) { _text.calc_r_c(row, column, _cursor_position); if (_cursor_position > 0 && _text.get_character(_cursor_position - 1) == '\n') { row += 1; @@ -911,6 +916,11 @@ update_cursor() { xpos = _text.get_xpos(row, column); ypos = _text.get_ypos(row, column); } + else { + _candidate_text.calc_r_c(row, column, _cursor_position + (int)_candidate_cursor_pos); + xpos = _candidate_text.get_xpos(row, column); + ypos = _candidate_text.get_ypos(row, column); + } _cursor_def.set_pos(xpos - _current_padding, 0.0f, ypos); _cursor_stale = false; @@ -919,7 +929,7 @@ update_cursor() { } // Should the cursor be visible? - if (!get_focus() || !_candidate_wtext.empty()) { + if (!get_focus()) { show_hide_cursor(false); } else { double elapsed_time = diff --git a/panda/src/pgui/pgEntry.h b/panda/src/pgui/pgEntry.h index 45548c8b12..a12c4cc70b 100644 --- a/panda/src/pgui/pgEntry.h +++ b/panda/src/pgui/pgEntry.h @@ -149,6 +149,7 @@ private: TextAssembler _text; TextAssembler _obscure_text; + TextAssembler _candidate_text; int _cursor_position; bool _cursor_stale; bool _cursor_visible; diff --git a/panda/src/putil/sparseArray.cxx b/panda/src/putil/sparseArray.cxx index 5404728aa1..450cc5a6b3 100644 --- a/panda/src/putil/sparseArray.cxx +++ b/panda/src/putil/sparseArray.cxx @@ -215,21 +215,28 @@ has_bits_in_common(const SparseArray &other) const { */ void SparseArray:: output(std::ostream &out) const { + if (_subranges.empty()) { + out << (_inverse ? "[ all ]" : "[ ]"); + return; + } out << "[ "; if (_inverse) { out << "all except: "; } Subranges::const_iterator si; for (si = _subranges.begin(); si != _subranges.end(); ++si) { + if (si != _subranges.begin()) { + out << ", "; + } if ((*si)._end == (*si)._begin + 1) { // A single element. - out << (*si)._begin << ", "; + out << (*si)._begin; } else { // A range of elements. - out << (*si)._begin << "-" << ((*si)._end - 1) << ", "; + out << (*si)._begin << "-" << ((*si)._end - 1); } } - out << "]"; + out << " ]"; } /** diff --git a/panda/src/windisplay/config_windisplay.cxx b/panda/src/windisplay/config_windisplay.cxx index ca3dc64f72..6d655d905b 100644 --- a/panda/src/windisplay/config_windisplay.cxx +++ b/panda/src/windisplay/config_windisplay.cxx @@ -49,12 +49,6 @@ ConfigVariableBool auto_cpu_data "require an explicit call to pipe->lookup_cpu_data(). Setting this " "true may slow down startup time by 1-2 seconds.")); -ConfigVariableBool ime_aware -("ime-aware", false, - PRC_DESC("Set this true to show ime texts on the chat panel and hide the " - "IME default windows. This is a mechanism to work around DX8/9 " - "interface.")); - ConfigVariableBool ime_hide ("ime-hide", false, PRC_DESC("Set this true to hide ime windows.")); diff --git a/panda/src/windisplay/config_windisplay.h b/panda/src/windisplay/config_windisplay.h index ae6568ec11..377b272472 100644 --- a/panda/src/windisplay/config_windisplay.h +++ b/panda/src/windisplay/config_windisplay.h @@ -25,8 +25,6 @@ extern ConfigVariableBool responsive_minimized_fullscreen_window; extern ConfigVariableBool hold_keys_across_windows; extern ConfigVariableBool do_vidmemsize_check; extern ConfigVariableBool auto_cpu_data; -extern ConfigVariableBool ime_composition_w; -extern ConfigVariableBool ime_aware; extern ConfigVariableBool ime_hide; extern ConfigVariableBool request_dxdisplay_information; extern ConfigVariableBool dpi_aware; diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index c6739c3bf3..8d28847b39 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -493,7 +493,8 @@ process_events() { break; case ClientMessage: - if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) { + if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window && + event.xany.window == _xwindow) { // This is a message from the window manager indicating that the user // has requested to close the window. string close_request_event = get_close_request_event(); @@ -860,12 +861,15 @@ set_properties_now(WindowProperties &properties) { int value_mask = 0; if (_properties.get_fullscreen()) { - changes.x = 0; - changes.y = 0; - value_mask |= CWX | CWY; - properties.clear_origin(); - - } else if (properties.has_origin()) { + if (_properties.get_x_origin() != 0 || + _properties.get_y_origin() != 0) { + changes.x = 0; + changes.y = 0; + value_mask |= CWX | CWY; + properties.clear_origin(); + } + } + else if (properties.has_origin()) { changes.x = properties.get_x_origin(); changes.y = properties.get_y_origin(); if (changes.x != -1) value_mask |= CWX; @@ -1049,22 +1053,6 @@ set_properties_now(WindowProperties &properties) { } } -/** - * Overridden from GraphicsWindow. - */ -void x11GraphicsWindow:: -mouse_mode_absolute() { - // unused: remove in 1.10! -} - -/** - * Overridden from GraphicsWindow. - */ -void x11GraphicsWindow:: -mouse_mode_relative() { - // unused: remove in 1.10! -} - /** * Closes the window right now. Called from the window thread. */ @@ -1195,15 +1183,41 @@ open_window() { set_wm_properties(_properties, false); - // We don't specify any fancy properties of the XIC. It would be nicer if - // we could support fancy IM's that want preedit callbacks, etc., but that - // can wait until we have an X server that actually supports these to test - // it on. + // Initialize the input context, which (if enabled) will enable us to capture + // candidate strings and display them inside Panda3D rather than via an + // external popup window. XIM im = x11_pipe->get_im(); _ic = nullptr; if (im) { - _ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, _xwindow, nullptr); + if (ime_aware) { + XIMCallback start_callback; + start_callback.client_data = (XPointer)this; + start_callback.callback = (XIMProc)xim_preedit_start; + XIMCallback draw_callback; + draw_callback.client_data = (XPointer)this; + draw_callback.callback = (XIMProc)xim_preedit_draw; + XIMCallback caret_callback; + caret_callback.client_data = (XPointer)this; + caret_callback.callback = (XIMProc)xim_preedit_caret; + XIMCallback done_callback; + done_callback.client_data = (XPointer)this; + done_callback.callback = (XIMProc)xim_preedit_done; + XVaNestedList preedit_attributes = XVaCreateNestedList( + 0, + XNPreeditStartCallback, &start_callback, + XNPreeditDrawCallback, &draw_callback, + XNPreeditCaretCallback, &caret_callback, + XNPreeditDoneCallback, &done_callback, + nullptr); + _ic = XCreateIC(im, + XNInputStyle, XIMPreeditCallbacks | XIMStatusNothing, + XNClientWindow, _xwindow, + XNPreeditAttributes, preedit_attributes, + nullptr); + } else { + _ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, _xwindow, nullptr); + } if (_ic == (XIC)nullptr) { x11display_cat.warning() << "Couldn't create input context.\n"; @@ -1562,6 +1576,123 @@ open_raw_mice() { #endif } +/** + * + */ +int x11GraphicsWindow:: +handle_preedit_start() { + _preedit_state = new PreeditState; + + if (x11display_cat.is_spam()) { + x11display_cat.spam() + << "Preedit started\n"; + } + + return sizeof(_preedit_state->_buffer) / sizeof(wchar_t); +} + +/** + * + */ +void x11GraphicsWindow:: +handle_preedit_draw(XIMPreeditDrawCallbackStruct &data) { + nassertv_always(_preedit_state != nullptr); + PreeditState &state = *_preedit_state; + + if (data.text != nullptr) { + // Replace characters in the preedit buffer. + int added_chars = data.text->length - data.chg_length; + memmove(state._buffer + data.chg_first, + state._buffer + data.chg_first + data.chg_length, + state._length - (size_t)(data.chg_first + data.chg_length) + data.text->length); + state._length += added_chars; + + if (added_chars != 0) { + if (state._highlight_start > data.chg_first) { + state._highlight_start = std::max(data.chg_first, state._highlight_start + added_chars); + } + if (state._highlight_end > data.chg_first) { + state._highlight_end = std::max(data.chg_first, state._highlight_end + added_chars); + } + } + + if (data.text->encoding_is_wchar) { + memcpy(state._buffer + data.chg_first, data.text->string.wide_char, data.text->length * sizeof(wchar_t)); + } else { + mbstowcs(state._buffer + data.chg_first, data.text->string.multi_byte, data.text->length); + } + + if (data.text->feedback != nullptr) { + // Update the highlighted region. + for (int i = 0; i < data.text->length; ++i) { + if (data.text->feedback[i] & XIMReverse) { + if (state._highlight_end > state._highlight_start) { + state._highlight_start = std::min(state._highlight_start, data.chg_first + i); + state._highlight_end = std::max(state._highlight_end, data.chg_first + i + 1); + } else { + state._highlight_start = data.chg_first + i; + state._highlight_end = data.chg_first + i + 1; + } + } + else if (state._highlight_end > state._highlight_start) { + if (state._highlight_start == data.chg_first + i) { + ++state._highlight_start; + } + if (state._highlight_end == data.chg_first + i + 1) { + --state._highlight_end; + } + if (state._highlight_end <= state._highlight_start) { + state._highlight_start = 0; + state._highlight_end = 0; + } + } + } + } + } else { + // Delete characters from the preedit buffer. + memmove(state._buffer + data.chg_first, + state._buffer + data.chg_first + data.chg_length, + state._length - (size_t)(data.chg_first + data.chg_length)); + state._length -= data.chg_length; + + if (state._highlight_start > data.chg_first) { + state._highlight_start = std::max(data.chg_first, state._highlight_start - data.chg_length); + } + if (state._highlight_end > data.chg_first) { + state._highlight_end = std::max(data.chg_first, state._highlight_end - data.chg_length); + } + } + _input->candidate(std::wstring(state._buffer, state._length), + state._highlight_start, state._highlight_end, data.caret); +} + +/** + * + */ +void x11GraphicsWindow:: +handle_preedit_caret(XIMPreeditCaretCallbackStruct &data) { + nassertv_always(_preedit_state != nullptr); + PreeditState &state = *_preedit_state; + + if (data.direction == XIMAbsolutePosition) { + _input->candidate(std::wstring(state._buffer, state._length), + state._highlight_start, state._highlight_end, data.position); + } +} + +/** + * + */ +void x11GraphicsWindow:: +handle_preedit_done() { + if (x11display_cat.is_spam()) { + x11display_cat.spam() + << "Preedit done\n"; + } + delete _preedit_state; + _preedit_state = nullptr; +} + /** * Generates a keystroke corresponding to the indicated X KeyPress event. */ @@ -2207,7 +2338,8 @@ check_event(X11_Display *display, XEvent *event, char *arg) { // We accept any event that is sent to our window. However, we have to let // raw mouse events through, since they're not associated with any window. return (event->xany.window == self->_xwindow || - (event->type == GenericEvent && self->_raw_mouse_enabled)); + (event->type == GenericEvent && self->_raw_mouse_enabled)) || + (event->type == ClientMessage); } /** @@ -2523,3 +2655,43 @@ cleanup: return ret; } + +/** + * + */ +int x11GraphicsWindow:: +xim_preedit_start(XIC ic, XPointer client_data, XPointer call_data) { + x11GraphicsWindow *window = (x11GraphicsWindow *)client_data; + return window->handle_preedit_start(); +} + +/** + * + */ +void x11GraphicsWindow:: +xim_preedit_draw(XIC ic, XPointer client_data, + XIMPreeditDrawCallbackStruct *call_data) { + x11GraphicsWindow *window = (x11GraphicsWindow *)client_data; + nassertv_always(call_data != nullptr); + window->handle_preedit_draw(*call_data); +} + +/** + * + */ +void x11GraphicsWindow:: +xim_preedit_caret(XIC ic, XPointer client_data, + XIMPreeditCaretCallbackStruct *call_data) { + x11GraphicsWindow *window = (x11GraphicsWindow *)client_data; + nassertv_always(call_data != nullptr); + window->handle_preedit_caret(*call_data); +} + +/** + * + */ +void x11GraphicsWindow:: +xim_preedit_done(XIC ic, XPointer client_data, XPointer call_data) { + x11GraphicsWindow *window = (x11GraphicsWindow *)client_data; + window->handle_preedit_done(); +} diff --git a/panda/src/x11display/x11GraphicsWindow.h b/panda/src/x11display/x11GraphicsWindow.h index e3d4a86382..0744ebe143 100644 --- a/panda/src/x11display/x11GraphicsWindow.h +++ b/panda/src/x11display/x11GraphicsWindow.h @@ -50,13 +50,14 @@ protected: virtual void close_window(); virtual bool open_window(); - virtual void mouse_mode_absolute(); - virtual void mouse_mode_relative(); - void set_wm_properties(const WindowProperties &properties, bool already_mapped); virtual void setup_colormap(XVisualInfo *visual); + int handle_preedit_start(); + void handle_preedit_draw(XIMPreeditDrawCallbackStruct &data); + void handle_preedit_caret(XIMPreeditCaretCallbackStruct &data); + void handle_preedit_done(); void handle_keystroke(XKeyEvent &event); void handle_keypress(XKeyEvent &event); void handle_keyrelease(XKeyEvent &event); @@ -75,6 +76,11 @@ private: X11_Cursor get_cursor(const Filename &filename); X11_Cursor read_ico(std::istream &ico); + static int xim_preedit_start(XIC ic, XPointer client_data, XPointer call_data); + static void xim_preedit_draw(XIC ic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data); + static void xim_preedit_caret(XIC ic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data); + static void xim_preedit_done(XIC ic, XPointer client_data, XPointer call_data); + protected: X11_Display *_display; int _screen; @@ -88,6 +94,13 @@ protected: LVecBase2i _fixed_size; GraphicsWindowInputDevice *_input; + struct PreeditState { + wchar_t _buffer[1024]; + size_t _length = 0; + int _highlight_start = 0; + int _highlight_end = 0; + }; + PreeditState *_preedit_state = nullptr; long _event_mask; bool _awaiting_configure; diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 3e48b9d809..4a106d4497 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -619,7 +619,7 @@ load_mesh(size_t index) { PT(GeomVertexArrayFormat) aformat = new GeomVertexArrayFormat; aformat->add_column(InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point); if (mesh.HasNormals()) { - aformat->add_column(InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_vector); + aformat->add_column(InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_normal); } if (mesh.HasVertexColors(0)) { aformat->add_column(InternalName::get_color(), 4, Geom::NT_stdfloat, Geom::C_color); diff --git a/tests/linmath/test_lvector2.py b/tests/linmath/test_lvector2.py index 1f61887bc1..9b77076fa2 100644 --- a/tests/linmath/test_lvector2.py +++ b/tests/linmath/test_lvector2.py @@ -120,6 +120,10 @@ def test_vec2_ceil(): assert rounded_vector.y == -2 +def test_vec2_rmul(): + assert 2 * Vec2(3, -4) == Vec2(6, -8) + + @pytest.mark.parametrize("type", (core.LVecBase2f, core.LVecBase2d, core.LVecBase2i)) def test_vec2_floordiv(type): with pytest.raises(ZeroDivisionError): diff --git a/tests/linmath/test_lvector3.py b/tests/linmath/test_lvector3.py index 4ef45a246f..2e1ceae67d 100644 --- a/tests/linmath/test_lvector3.py +++ b/tests/linmath/test_lvector3.py @@ -105,6 +105,10 @@ def test_vec3_ceil(): assert rounded_vector.z == 4 +def test_vec3_rmul(): + assert 2 * Vec3(0, 3, -4) == Vec3(0, 6, -8) + + @pytest.mark.parametrize("type", (core.LVecBase3f, core.LVecBase3d, core.LVecBase3i)) def test_vec3_floordiv(type): with pytest.raises(ZeroDivisionError): diff --git a/tests/linmath/test_lvector4.py b/tests/linmath/test_lvector4.py index f51bd3f718..0127359c50 100644 --- a/tests/linmath/test_lvector4.py +++ b/tests/linmath/test_lvector4.py @@ -121,6 +121,10 @@ def test_vec4_ceil(): assert rounded_vector.w == 1 +def test_vec4_rmul(): + assert 2 * Vec4(0, 3, -4, 0.5) == Vec4(0, 6, -8, 1) + + @pytest.mark.parametrize("type", (core.LVecBase4f, core.LVecBase4d, core.LVecBase4i)) def test_vec4_floordiv(type): with pytest.raises(ZeroDivisionError):